mirror of
https://github.com/containers/podman.git
synced 2025-09-17 15:07:40 +08:00

Add the `podman generate kube` and `podman play kube` command. The code has largely been copied from Podman v1 but restructured to not leak the K8s core API into the (remote) client. Both commands are added in the same commit to allow for enabling the tests at the same time. Move some exports from `cmd/podman/common` to the appropriate places in the backend to avoid circular dependencies. Move definitions of label annotations to `libpod/define` and set the security-opt labels in the frontend to make kube tests pass. Implement rest endpoints, bindings and the tunnel interface. Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
480 lines
15 KiB
Go
480 lines
15 KiB
Go
package generate
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/containers/common/pkg/config"
|
|
"github.com/containers/libpod/libpod"
|
|
"github.com/containers/libpod/libpod/define"
|
|
"github.com/containers/libpod/libpod/image"
|
|
"github.com/containers/libpod/pkg/rootless"
|
|
"github.com/containers/libpod/pkg/specgen"
|
|
"github.com/containers/libpod/pkg/util"
|
|
spec "github.com/opencontainers/runtime-spec/specs-go"
|
|
"github.com/opencontainers/runtime-tools/generate"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// Get the default namespace mode for any given namespace type.
|
|
func GetDefaultNamespaceMode(nsType string, cfg *config.Config, pod *libpod.Pod) (specgen.Namespace, error) {
|
|
// The default for most is private
|
|
toReturn := specgen.Namespace{}
|
|
toReturn.NSMode = specgen.Private
|
|
|
|
// Ensure case insensitivity
|
|
nsType = strings.ToLower(nsType)
|
|
|
|
// If the pod is not nil - check shared namespaces
|
|
if pod != nil && pod.HasInfraContainer() {
|
|
podMode := false
|
|
switch {
|
|
case nsType == "pid" && pod.SharesPID():
|
|
podMode = true
|
|
case nsType == "ipc" && pod.SharesIPC():
|
|
podMode = true
|
|
case nsType == "uts" && pod.SharesUTS():
|
|
podMode = true
|
|
case nsType == "user" && pod.SharesUser():
|
|
podMode = true
|
|
case nsType == "net" && pod.SharesNet():
|
|
podMode = true
|
|
case nsType == "cgroup" && pod.SharesCgroup():
|
|
podMode = true
|
|
}
|
|
if podMode {
|
|
toReturn.NSMode = specgen.FromPod
|
|
return toReturn, nil
|
|
}
|
|
}
|
|
|
|
if cfg == nil {
|
|
cfg = &config.Config{}
|
|
}
|
|
switch nsType {
|
|
case "pid":
|
|
return specgen.ParseNamespace(cfg.Containers.PidNS)
|
|
case "ipc":
|
|
return specgen.ParseNamespace(cfg.Containers.IPCNS)
|
|
case "uts":
|
|
return specgen.ParseNamespace(cfg.Containers.UTSNS)
|
|
case "user":
|
|
return specgen.ParseUserNamespace(cfg.Containers.UserNS)
|
|
case "cgroup":
|
|
return specgen.ParseCgroupNamespace(cfg.Containers.CgroupNS)
|
|
case "net":
|
|
ns, _, err := specgen.ParseNetworkNamespace(cfg.Containers.NetNS)
|
|
return ns, err
|
|
}
|
|
|
|
return toReturn, errors.Wrapf(define.ErrInvalidArg, "invalid namespace type %q passed", nsType)
|
|
}
|
|
|
|
// GenerateNamespaceOptions generates container creation options for all
|
|
// namespaces in a SpecGenerator.
|
|
// Pod is the pod the container will join. May be nil is the container is not
|
|
// joining a pod.
|
|
// TODO: Consider grouping options that are not directly attached to a namespace
|
|
// elsewhere.
|
|
func GenerateNamespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod.Pod, img *image.Image) ([]libpod.CtrCreateOption, error) {
|
|
toReturn := []libpod.CtrCreateOption{}
|
|
|
|
// If pod is not nil, get infra container.
|
|
var infraCtr *libpod.Container
|
|
if pod != nil {
|
|
infraID, err := pod.InfraContainerID()
|
|
if err != nil {
|
|
// This is likely to be of the fatal kind (pod was
|
|
// removed) so hard fail
|
|
return nil, errors.Wrapf(err, "error looking up pod %s infra container", pod.ID())
|
|
}
|
|
if infraID != "" {
|
|
ctr, err := rt.GetContainer(infraID)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "error retrieving pod %s infra container %s", pod.ID(), infraID)
|
|
}
|
|
infraCtr = ctr
|
|
}
|
|
}
|
|
|
|
errNoInfra := errors.Wrapf(define.ErrInvalidArg, "cannot use pod namespace as container is not joining a pod or pod has no infra container")
|
|
|
|
// PID
|
|
switch s.PidNS.NSMode {
|
|
case specgen.FromPod:
|
|
if pod == nil || infraCtr == nil {
|
|
return nil, errNoInfra
|
|
}
|
|
toReturn = append(toReturn, libpod.WithPIDNSFrom(infraCtr))
|
|
case specgen.FromContainer:
|
|
pidCtr, err := rt.LookupContainer(s.PidNS.Value)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "error looking up container to share pid namespace with")
|
|
}
|
|
toReturn = append(toReturn, libpod.WithPIDNSFrom(pidCtr))
|
|
}
|
|
|
|
// IPC
|
|
switch s.IpcNS.NSMode {
|
|
case specgen.Host:
|
|
// Force use of host /dev/shm for host namespace
|
|
toReturn = append(toReturn, libpod.WithShmDir("/dev/shm"))
|
|
case specgen.FromPod:
|
|
if pod == nil || infraCtr == nil {
|
|
return nil, errNoInfra
|
|
}
|
|
toReturn = append(toReturn, libpod.WithIPCNSFrom(infraCtr))
|
|
case specgen.FromContainer:
|
|
ipcCtr, err := rt.LookupContainer(s.IpcNS.Value)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "error looking up container to share ipc namespace with")
|
|
}
|
|
toReturn = append(toReturn, libpod.WithIPCNSFrom(ipcCtr))
|
|
toReturn = append(toReturn, libpod.WithShmDir(ipcCtr.ShmDir()))
|
|
}
|
|
|
|
// UTS
|
|
switch s.UtsNS.NSMode {
|
|
case specgen.FromPod:
|
|
if pod == nil || infraCtr == nil {
|
|
return nil, errNoInfra
|
|
}
|
|
toReturn = append(toReturn, libpod.WithUTSNSFrom(infraCtr))
|
|
case specgen.FromContainer:
|
|
utsCtr, err := rt.LookupContainer(s.UtsNS.Value)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "error looking up container to share uts namespace with")
|
|
}
|
|
toReturn = append(toReturn, libpod.WithUTSNSFrom(utsCtr))
|
|
}
|
|
|
|
// User
|
|
switch s.UserNS.NSMode {
|
|
case specgen.KeepID:
|
|
if rootless.IsRootless() {
|
|
s.User = ""
|
|
} else {
|
|
// keep-id as root doesn't need a user namespace
|
|
s.UserNS.NSMode = specgen.Host
|
|
}
|
|
case specgen.FromPod:
|
|
if pod == nil || infraCtr == nil {
|
|
return nil, errNoInfra
|
|
}
|
|
toReturn = append(toReturn, libpod.WithUserNSFrom(infraCtr))
|
|
case specgen.FromContainer:
|
|
userCtr, err := rt.LookupContainer(s.UserNS.Value)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "error looking up container to share user namespace with")
|
|
}
|
|
toReturn = append(toReturn, libpod.WithUserNSFrom(userCtr))
|
|
}
|
|
|
|
if s.IDMappings != nil {
|
|
toReturn = append(toReturn, libpod.WithIDMappings(*s.IDMappings))
|
|
}
|
|
if s.User != "" {
|
|
toReturn = append(toReturn, libpod.WithUser(s.User))
|
|
}
|
|
if len(s.Groups) > 0 {
|
|
toReturn = append(toReturn, libpod.WithGroups(s.Groups))
|
|
}
|
|
|
|
// Cgroup
|
|
switch s.CgroupNS.NSMode {
|
|
case specgen.FromPod:
|
|
if pod == nil || infraCtr == nil {
|
|
return nil, errNoInfra
|
|
}
|
|
toReturn = append(toReturn, libpod.WithCgroupNSFrom(infraCtr))
|
|
case specgen.FromContainer:
|
|
cgroupCtr, err := rt.LookupContainer(s.CgroupNS.Value)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "error looking up container to share cgroup namespace with")
|
|
}
|
|
toReturn = append(toReturn, libpod.WithCgroupNSFrom(cgroupCtr))
|
|
}
|
|
|
|
if s.CgroupParent != "" {
|
|
toReturn = append(toReturn, libpod.WithCgroupParent(s.CgroupParent))
|
|
}
|
|
|
|
if s.CgroupsMode != "" {
|
|
toReturn = append(toReturn, libpod.WithCgroupsMode(s.CgroupsMode))
|
|
}
|
|
|
|
// Net
|
|
// TODO validate CNINetworks, StaticIP, StaticIPv6 are only set if we
|
|
// are in bridge mode.
|
|
postConfigureNetNS := !s.UserNS.IsHost()
|
|
switch s.NetNS.NSMode {
|
|
case specgen.FromPod:
|
|
if pod == nil || infraCtr == nil {
|
|
return nil, errNoInfra
|
|
}
|
|
toReturn = append(toReturn, libpod.WithNetNSFrom(infraCtr))
|
|
case specgen.FromContainer:
|
|
netCtr, err := rt.LookupContainer(s.NetNS.Value)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "error looking up container to share net namespace with")
|
|
}
|
|
toReturn = append(toReturn, libpod.WithNetNSFrom(netCtr))
|
|
case specgen.Slirp:
|
|
portMappings, err := createPortMappings(ctx, s, img)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
toReturn = append(toReturn, libpod.WithNetNS(portMappings, postConfigureNetNS, "slirp4netns", nil))
|
|
case specgen.Bridge:
|
|
portMappings, err := createPortMappings(ctx, s, img)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
toReturn = append(toReturn, libpod.WithNetNS(portMappings, postConfigureNetNS, "bridge", s.CNINetworks))
|
|
}
|
|
|
|
if s.UseImageHosts {
|
|
toReturn = append(toReturn, libpod.WithUseImageHosts())
|
|
} else if len(s.HostAdd) > 0 {
|
|
toReturn = append(toReturn, libpod.WithHosts(s.HostAdd))
|
|
}
|
|
if len(s.DNSSearch) > 0 {
|
|
toReturn = append(toReturn, libpod.WithDNSSearch(s.DNSSearch))
|
|
}
|
|
if s.UseImageResolvConf {
|
|
toReturn = append(toReturn, libpod.WithUseImageResolvConf())
|
|
} else if len(s.DNSServers) > 0 {
|
|
var dnsServers []string
|
|
for _, d := range s.DNSServers {
|
|
dnsServers = append(dnsServers, d.String())
|
|
}
|
|
toReturn = append(toReturn, libpod.WithDNS(dnsServers))
|
|
}
|
|
if len(s.DNSOptions) > 0 {
|
|
toReturn = append(toReturn, libpod.WithDNSOption(s.DNSOptions))
|
|
}
|
|
if s.StaticIP != nil {
|
|
toReturn = append(toReturn, libpod.WithStaticIP(*s.StaticIP))
|
|
}
|
|
if s.StaticMAC != nil {
|
|
toReturn = append(toReturn, libpod.WithStaticMAC(*s.StaticMAC))
|
|
}
|
|
|
|
return toReturn, nil
|
|
}
|
|
|
|
func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt *libpod.Runtime) error {
|
|
// PID
|
|
switch s.PidNS.NSMode {
|
|
case specgen.Path:
|
|
if _, err := os.Stat(s.PidNS.Value); err != nil {
|
|
return errors.Wrapf(err, "cannot find specified PID namespace path %q", s.PidNS.Value)
|
|
}
|
|
if err := g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), s.PidNS.Value); err != nil {
|
|
return err
|
|
}
|
|
case specgen.Host:
|
|
if err := g.RemoveLinuxNamespace(string(spec.PIDNamespace)); err != nil {
|
|
return err
|
|
}
|
|
case specgen.Private:
|
|
if err := g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), ""); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// IPC
|
|
switch s.IpcNS.NSMode {
|
|
case specgen.Path:
|
|
if _, err := os.Stat(s.IpcNS.Value); err != nil {
|
|
return errors.Wrapf(err, "cannot find specified IPC namespace path %q", s.IpcNS.Value)
|
|
}
|
|
if err := g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), s.IpcNS.Value); err != nil {
|
|
return err
|
|
}
|
|
case specgen.Host:
|
|
if err := g.RemoveLinuxNamespace(string(spec.IPCNamespace)); err != nil {
|
|
return err
|
|
}
|
|
case specgen.Private:
|
|
if err := g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), ""); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// UTS
|
|
switch s.UtsNS.NSMode {
|
|
case specgen.Path:
|
|
if _, err := os.Stat(s.UtsNS.Value); err != nil {
|
|
return errors.Wrapf(err, "cannot find specified UTS namespace path %q", s.UtsNS.Value)
|
|
}
|
|
if err := g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), s.UtsNS.Value); err != nil {
|
|
return err
|
|
}
|
|
case specgen.Host:
|
|
if err := g.RemoveLinuxNamespace(string(spec.UTSNamespace)); err != nil {
|
|
return err
|
|
}
|
|
case specgen.Private:
|
|
if err := g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), ""); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
hostname := s.Hostname
|
|
if hostname == "" {
|
|
switch {
|
|
case s.UtsNS.NSMode == specgen.FromContainer:
|
|
utsCtr, err := rt.LookupContainer(s.UtsNS.Value)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error looking up container to share uts namespace with")
|
|
}
|
|
hostname = utsCtr.Hostname()
|
|
case s.NetNS.NSMode == specgen.Host || s.UtsNS.NSMode == specgen.Host:
|
|
tmpHostname, err := os.Hostname()
|
|
if err != nil {
|
|
return errors.Wrap(err, "unable to retrieve hostname of the host")
|
|
}
|
|
hostname = tmpHostname
|
|
default:
|
|
logrus.Debug("No hostname set; container's hostname will default to runtime default")
|
|
}
|
|
}
|
|
|
|
g.RemoveHostname()
|
|
if s.Hostname != "" || s.UtsNS.NSMode != specgen.Host {
|
|
// Set the hostname in the OCI configuration only if specified by
|
|
// the user or if we are creating a new UTS namespace.
|
|
// TODO: Should we be doing this for pod or container shared
|
|
// namespaces?
|
|
g.SetHostname(hostname)
|
|
}
|
|
g.AddProcessEnv("HOSTNAME", hostname)
|
|
|
|
// User
|
|
switch s.UserNS.NSMode {
|
|
case specgen.Path:
|
|
if _, err := os.Stat(s.UserNS.Value); err != nil {
|
|
return errors.Wrapf(err, "cannot find specified user namespace path %s", s.UserNS.Value)
|
|
}
|
|
if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), s.UserNS.Value); err != nil {
|
|
return err
|
|
}
|
|
// runc complains if no mapping is specified, even if we join another ns. So provide a dummy mapping
|
|
g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1))
|
|
g.AddLinuxGIDMapping(uint32(0), uint32(0), uint32(1))
|
|
case specgen.Host:
|
|
if err := g.RemoveLinuxNamespace(string(spec.UserNamespace)); err != nil {
|
|
return err
|
|
}
|
|
case specgen.KeepID:
|
|
var (
|
|
err error
|
|
uid, gid int
|
|
)
|
|
s.IDMappings, uid, gid, err = util.GetKeepIDMapping()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
g.SetProcessUID(uint32(uid))
|
|
g.SetProcessGID(uint32(gid))
|
|
fallthrough
|
|
case specgen.Private:
|
|
if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil {
|
|
return err
|
|
}
|
|
if s.IDMappings == nil || (len(s.IDMappings.UIDMap) == 0 && len(s.IDMappings.GIDMap) == 0) {
|
|
return errors.Errorf("must provide at least one UID or GID mapping to configure a user namespace")
|
|
}
|
|
for _, uidmap := range s.IDMappings.UIDMap {
|
|
g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size))
|
|
}
|
|
for _, gidmap := range s.IDMappings.GIDMap {
|
|
g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
|
|
}
|
|
}
|
|
|
|
// Cgroup
|
|
switch s.CgroupNS.NSMode {
|
|
case specgen.Path:
|
|
if _, err := os.Stat(s.CgroupNS.Value); err != nil {
|
|
return errors.Wrapf(err, "cannot find specified cgroup namespace path %s", s.CgroupNS.Value)
|
|
}
|
|
if err := g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), s.CgroupNS.Value); err != nil {
|
|
return err
|
|
}
|
|
case specgen.Host:
|
|
if err := g.RemoveLinuxNamespace(string(spec.CgroupNamespace)); err != nil {
|
|
return err
|
|
}
|
|
case specgen.Private:
|
|
if err := g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), ""); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Net
|
|
switch s.NetNS.NSMode {
|
|
case specgen.Path:
|
|
if _, err := os.Stat(s.NetNS.Value); err != nil {
|
|
return errors.Wrapf(err, "cannot find specified network namespace path %s", s.NetNS.Value)
|
|
}
|
|
if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), s.NetNS.Value); err != nil {
|
|
return err
|
|
}
|
|
case specgen.Host:
|
|
if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil {
|
|
return err
|
|
}
|
|
case specgen.Private, specgen.NoNetwork:
|
|
if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), ""); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if g.Config.Annotations == nil {
|
|
g.Config.Annotations = make(map[string]string)
|
|
}
|
|
if s.PublishExposedPorts {
|
|
g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseTrue
|
|
} else {
|
|
g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseFalse
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetNamespaceOptions transforms a slice of kernel namespaces
|
|
// into a slice of pod create options. Currently, not all
|
|
// kernel namespaces are supported, and they will be returned in an error
|
|
func GetNamespaceOptions(ns []string) ([]libpod.PodCreateOption, error) {
|
|
var options []libpod.PodCreateOption
|
|
var erroredOptions []libpod.PodCreateOption
|
|
for _, toShare := range ns {
|
|
switch toShare {
|
|
case "cgroup":
|
|
options = append(options, libpod.WithPodCgroups())
|
|
case "net":
|
|
options = append(options, libpod.WithPodNet())
|
|
case "mnt":
|
|
return erroredOptions, errors.Errorf("Mount sharing functionality not supported on pod level")
|
|
case "pid":
|
|
options = append(options, libpod.WithPodPID())
|
|
case "user":
|
|
return erroredOptions, errors.Errorf("User sharing functionality not supported on pod level")
|
|
case "ipc":
|
|
options = append(options, libpod.WithPodIPC())
|
|
case "uts":
|
|
options = append(options, libpod.WithPodUTS())
|
|
case "":
|
|
case "none":
|
|
return erroredOptions, nil
|
|
default:
|
|
return erroredOptions, errors.Errorf("Invalid kernel namespace to share: %s. Options are: net, pid, ipc, uts or none", toShare)
|
|
}
|
|
}
|
|
return options, nil
|
|
}
|