diff options
author | Daniel J Walsh <dwalsh@redhat.com> | 2018-09-01 12:51:00 -0400 |
---|---|---|
committer | Atomic Bot <atomic-devel@projectatomic.io> | 2018-09-07 18:18:54 +0000 |
commit | 87f90ce14abf36fbf85f6128b3024ea89a44d670 (patch) | |
tree | 7090c0a491c2f4199f8172e1d30b525fa938ed86 | |
parent | ccc4a339cd124abc668b7542a9eb838cd7d1b214 (diff) | |
download | podman-87f90ce14abf36fbf85f6128b3024ea89a44d670.tar.gz podman-87f90ce14abf36fbf85f6128b3024ea89a44d670.tar.bz2 podman-87f90ce14abf36fbf85f6128b3024ea89a44d670.zip |
Fix pod sharing for utsmode
We should be sharing cgroups namespace by default in pods
uts namespace sharing was broken in pods.
Create a new libpod/pkg/namespaces for handling of namespace fields
in containers
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
Closes: #1418
Approved by: mheon
-rw-r--r-- | cmd/podman/create.go | 12 | ||||
-rw-r--r-- | cmd/podman/parse.go | 138 | ||||
-rw-r--r-- | cmd/podman/pod_create.go | 2 | ||||
-rw-r--r-- | cmd/podman/shared/pod.go | 2 | ||||
-rw-r--r-- | pkg/namespaces/namespaces.go | 219 | ||||
-rw-r--r-- | pkg/spec/createconfig.go | 56 | ||||
-rw-r--r-- | pkg/varlinkapi/containers_create.go | 12 | ||||
-rw-r--r-- | test/e2e/rootless_test.go | 3 |
8 files changed, 269 insertions, 175 deletions
diff --git a/cmd/podman/create.go b/cmd/podman/create.go index 20d976c0b..7a3b26c85 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -16,11 +16,11 @@ import ( ann "github.com/containers/libpod/pkg/annotations" "github.com/containers/libpod/pkg/apparmor" "github.com/containers/libpod/pkg/inspect" + "github.com/containers/libpod/pkg/namespaces" "github.com/containers/libpod/pkg/rootless" cc "github.com/containers/libpod/pkg/spec" "github.com/containers/libpod/pkg/util" libpodVersion "github.com/containers/libpod/version" - "github.com/docker/docker/api/types/container" "github.com/docker/docker/pkg/signal" "github.com/docker/go-connections/nat" "github.com/docker/go-units" @@ -456,7 +456,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim if !c.IsSet("pid") && pod != nil && pod.SharesPID() { pidModeStr = cc.POD } - pidMode := container.PidMode(pidModeStr) + pidMode := namespaces.PidMode(pidModeStr) if !cc.Valid(string(pidMode), pidMode) { return nil, errors.Errorf("--pid %q is not valid", c.String("pid")) } @@ -465,7 +465,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim if !c.IsSet("userns") && pod != nil && pod.SharesUser() { usernsModeStr = cc.POD } - usernsMode := container.UsernsMode(usernsModeStr) + usernsMode := namespaces.UsernsMode(usernsModeStr) if !cc.Valid(string(usernsMode), usernsMode) { return nil, errors.Errorf("--userns %q is not valid", c.String("userns")) } @@ -474,7 +474,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim if !c.IsSet("uts") && pod != nil && pod.SharesUTS() { utsModeStr = cc.POD } - utsMode := container.UTSMode(utsModeStr) + utsMode := namespaces.UTSMode(utsModeStr) if !cc.Valid(string(utsMode), utsMode) { return nil, errors.Errorf("--uts %q is not valid", c.String("uts")) } @@ -483,7 +483,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim if !c.IsSet("ipc") && pod != nil && pod.SharesIPC() { ipcModeStr = cc.POD } - ipcMode := container.IpcMode(ipcModeStr) + ipcMode := namespaces.IpcMode(ipcModeStr) if !cc.Valid(string(ipcMode), ipcMode) { return nil, errors.Errorf("--ipc %q is not valid", ipcMode) } @@ -492,7 +492,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim netModeStr = cc.POD } // Make sure if network is set to container namespace, port binding is not also being asked for - netMode := container.NetworkMode(netModeStr) + netMode := namespaces.NetworkMode(netModeStr) if netMode.IsContainer() || cc.IsPod(netModeStr) { if len(c.StringSlice("publish")) > 0 || c.Bool("publish-all") { return nil, errors.Errorf("cannot set port bindings on an existing container network namespace") diff --git a/cmd/podman/parse.go b/cmd/podman/parse.go index 158a006fb..ade592ddf 100644 --- a/cmd/podman/parse.go +++ b/cmd/podman/parse.go @@ -239,50 +239,6 @@ func parseEnvFile(env map[string]string, filename string) error { return scanner.Err() } -// NsIpc represents the container ipc stack. -// for ipc flag -type NsIpc string - -// IsPrivate indicates whether the container uses its private ipc stack. -func (n NsIpc) IsPrivate() bool { - return !(n.IsHost() || n.IsContainer()) -} - -// IsHost indicates whether the container uses the host's ipc stack. -func (n NsIpc) IsHost() bool { - return n == "host" -} - -// IsContainer indicates whether the container uses a container's ipc stack. -func (n NsIpc) IsContainer() bool { - parts := strings.SplitN(string(n), ":", 2) - return len(parts) > 1 && parts[0] == "container" -} - -// Valid indicates whether the ipc stack is valid. -func (n NsIpc) Valid() bool { - parts := strings.Split(string(n), ":") - switch mode := parts[0]; mode { - case "", "host": - case "container": - if len(parts) != 2 || parts[1] == "" { - return false - } - default: - return false - } - return true -} - -// Container returns the name of the container ipc stack is going to be used. -func (n NsIpc) Container() string { - parts := strings.SplitN(string(n), ":", 2) - if len(parts) > 1 { - return parts[1] - } - return "" -} - // validateLabel validates that the specified string is a valid label, and returns it. // Labels are in the form on key=value. // for label flag @@ -313,50 +269,6 @@ func parseLoggingOpts(logDriver string, logDriverOpt []string) (map[string]strin return logOptsMap, nil } -// NsPid represents the pid namespace of the container. -//for pid flag -type NsPid string - -// IsPrivate indicates whether the container uses its own new pid namespace. -func (n NsPid) IsPrivate() bool { - return !(n.IsHost() || n.IsContainer()) -} - -// IsHost indicates whether the container uses the host's pid namespace. -func (n NsPid) IsHost() bool { - return n == "host" -} - -// IsContainer indicates whether the container uses a container's pid namespace. -func (n NsPid) IsContainer() bool { - parts := strings.SplitN(string(n), ":", 2) - return len(parts) > 1 && parts[0] == "container" -} - -// Valid indicates whether the pid namespace is valid. -func (n NsPid) Valid() bool { - parts := strings.Split(string(n), ":") - switch mode := parts[0]; mode { - case "", "host": - case "container": - if len(parts) != 2 || parts[1] == "" { - return false - } - default: - return false - } - return true -} - -// Container returns the name of the container whose pid namespace is going to be used. -func (n NsPid) Container() string { - parts := strings.SplitN(string(n), ":", 2) - if len(parts) > 1 { - return parts[1] - } - return "" -} - // parsePortSpecs receives port specs in the format of ip:public:private/proto and parses // these in to the internal types // for publish, publish-all, and expose flags @@ -567,56 +479,6 @@ func convertKVStringsToMap(values []string) map[string]string { return result } -// NsUser represents userns mode in the container. -// for userns flag -type NsUser string - -// IsHost indicates whether the container uses the host's userns. -func (n NsUser) IsHost() bool { - return n == "host" -} - -// IsPrivate indicates whether the container uses the a private userns. -func (n NsUser) IsPrivate() bool { - return !(n.IsHost()) -} - -// Valid indicates whether the userns is valid. -func (n NsUser) Valid() bool { - parts := strings.Split(string(n), ":") - switch mode := parts[0]; mode { - case "", "host": - default: - return false - } - return true -} - -// NsUts represents the UTS namespace of the container. -// for uts flag -type NsUts string - -// IsPrivate indicates whether the container uses its private UTS namespace. -func (n NsUts) IsPrivate() bool { - return !(n.IsHost()) -} - -// IsHost indicates whether the container uses the host's UTS namespace. -func (n NsUts) IsHost() bool { - return n == "host" -} - -// Valid indicates whether the UTS namespace is valid. -func (n NsUts) Valid() bool { - parts := strings.Split(string(n), ":") - switch mode := parts[0]; mode { - case "", "host": - default: - return false - } - return true -} - // Takes a stringslice and converts to a uint32slice func stringSlicetoUint32Slice(inputSlice []string) ([]uint32, error) { var outputSlice []uint32 diff --git a/cmd/podman/pod_create.go b/cmd/podman/pod_create.go index 488fef869..61086f890 100644 --- a/cmd/podman/pod_create.go +++ b/cmd/podman/pod_create.go @@ -15,7 +15,7 @@ import ( var ( // Kernel namespaces shared by default within a pod - DefaultKernelNamespaces = "ipc,net,uts" + DefaultKernelNamespaces = "cgroup,ipc,net,uts" ) var podCreateDescription = "Creates a new empty pod. The pod ID is then" + diff --git a/cmd/podman/shared/pod.go b/cmd/podman/shared/pod.go index 1a14b3777..badc7a837 100644 --- a/cmd/podman/shared/pod.go +++ b/cmd/podman/shared/pod.go @@ -70,6 +70,8 @@ func GetNamespaceOptions(ns []string) ([]libpod.PodCreateOption, error) { 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": diff --git a/pkg/namespaces/namespaces.go b/pkg/namespaces/namespaces.go new file mode 100644 index 000000000..1bdb2b00d --- /dev/null +++ b/pkg/namespaces/namespaces.go @@ -0,0 +1,219 @@ +package namespaces + +import ( + "strings" +) + +// UsernsMode represents userns mode in the container. +type UsernsMode string + +// IsHost indicates whether the container uses the host's userns. +func (n UsernsMode) IsHost() bool { + return n == "host" +} + +// IsPrivate indicates whether the container uses the a private userns. +func (n UsernsMode) IsPrivate() bool { + return !(n.IsHost()) +} + +// Valid indicates whether the userns is valid. +func (n UsernsMode) Valid() bool { + parts := strings.Split(string(n), ":") + switch mode := parts[0]; mode { + case "", "host": + default: + return false + } + return true +} + +// UTSMode represents the UTS namespace of the container. +type UTSMode string + +// IsPrivate indicates whether the container uses its private UTS namespace. +func (n UTSMode) IsPrivate() bool { + return !(n.IsHost()) +} + +// IsHost indicates whether the container uses the host's UTS namespace. +func (n UTSMode) IsHost() bool { + return n == "host" +} + +// IsContainer indicates whether the container uses a container's UTS namespace. +func (n UTSMode) IsContainer() bool { + parts := strings.SplitN(string(n), ":", 2) + return len(parts) > 1 && parts[0] == "container" +} + +// Container returns the name of the container whose uts namespace is going to be used. +func (n UTSMode) Container() string { + parts := strings.SplitN(string(n), ":", 2) + if len(parts) > 1 { + return parts[1] + } + return "" +} + +// Valid indicates whether the UTS namespace is valid. +func (n UTSMode) Valid() bool { + parts := strings.Split(string(n), ":") + switch mode := parts[0]; mode { + case "", "host": + case "container": + if len(parts) != 2 || parts[1] == "" { + return false + } + default: + return false + } + return true +} + +// IpcMode represents the container ipc stack. +type IpcMode string + +// IsPrivate indicates whether the container uses its own private ipc namespace which can not be shared. +func (n IpcMode) IsPrivate() bool { + return n == "private" +} + +// IsHost indicates whether the container shares the host's ipc namespace. +func (n IpcMode) IsHost() bool { + return n == "host" +} + +// IsShareable indicates whether the container's ipc namespace can be shared with another container. +func (n IpcMode) IsShareable() bool { + return n == "shareable" +} + +// IsContainer indicates whether the container uses another container's ipc namespace. +func (n IpcMode) IsContainer() bool { + parts := strings.SplitN(string(n), ":", 2) + return len(parts) > 1 && parts[0] == "container" +} + +// IsNone indicates whether container IpcMode is set to "none". +func (n IpcMode) IsNone() bool { + return n == "none" +} + +// IsEmpty indicates whether container IpcMode is empty +func (n IpcMode) IsEmpty() bool { + return n == "" +} + +// Valid indicates whether the ipc mode is valid. +func (n IpcMode) Valid() bool { + return n.IsEmpty() || n.IsNone() || n.IsPrivate() || n.IsHost() || n.IsShareable() || n.IsContainer() +} + +// Container returns the name of the container ipc stack is going to be used. +func (n IpcMode) Container() string { + parts := strings.SplitN(string(n), ":", 2) + if len(parts) > 1 && parts[0] == "container" { + return parts[1] + } + return "" +} + +// PidMode represents the pid namespace of the container. +type PidMode string + +// IsPrivate indicates whether the container uses its own new pid namespace. +func (n PidMode) IsPrivate() bool { + return !(n.IsHost() || n.IsContainer()) +} + +// IsHost indicates whether the container uses the host's pid namespace. +func (n PidMode) IsHost() bool { + return n == "host" +} + +// IsContainer indicates whether the container uses a container's pid namespace. +func (n PidMode) IsContainer() bool { + parts := strings.SplitN(string(n), ":", 2) + return len(parts) > 1 && parts[0] == "container" +} + +// Valid indicates whether the pid namespace is valid. +func (n PidMode) Valid() bool { + parts := strings.Split(string(n), ":") + switch mode := parts[0]; mode { + case "", "host": + case "container": + if len(parts) != 2 || parts[1] == "" { + return false + } + default: + return false + } + return true +} + +// Container returns the name of the container whose pid namespace is going to be used. +func (n PidMode) Container() string { + parts := strings.SplitN(string(n), ":", 2) + if len(parts) > 1 { + return parts[1] + } + return "" +} + +// NetworkMode represents the container network stack. +type NetworkMode string + +// IsNone indicates whether container isn't using a network stack. +func (n NetworkMode) IsNone() bool { + return n == "none" +} + +// IsHost indicates whether the container uses the host's network stack. +func (n NetworkMode) IsHost() bool { + return n == "host" +} + +// IsDefault indicates whether container uses the default network stack. +func (n NetworkMode) IsDefault() bool { + return n == "default" +} + +// IsPrivate indicates whether container uses its private network stack. +func (n NetworkMode) IsPrivate() bool { + return !(n.IsHost() || n.IsContainer()) +} + +// IsContainer indicates whether container uses a container network stack. +func (n NetworkMode) IsContainer() bool { + parts := strings.SplitN(string(n), ":", 2) + return len(parts) > 1 && parts[0] == "container" +} + +// ConnectedContainer is the id of the container which network this container is connected to. +func (n NetworkMode) ConnectedContainer() string { + parts := strings.SplitN(string(n), ":", 2) + if len(parts) > 1 { + return parts[1] + } + return "" +} + +//UserDefined indicates user-created network +func (n NetworkMode) UserDefined() string { + if n.IsUserDefined() { + return string(n) + } + return "" +} + +// IsBridge indicates whether container uses the bridge network stack +func (n NetworkMode) IsBridge() bool { + return n == "bridge" +} + +// IsUserDefined indicates user-created network +func (n NetworkMode) IsUserDefined() bool { + return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer() +} diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go index 0ab0bb9ff..0b7ee993d 100644 --- a/pkg/spec/createconfig.go +++ b/pkg/spec/createconfig.go @@ -8,10 +8,10 @@ import ( "syscall" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/namespaces" "github.com/containers/libpod/pkg/rootless" "github.com/containers/storage" "github.com/cri-o/ocicni/pkg/ocicni" - "github.com/docker/docker/api/types/container" "github.com/docker/go-connections/nat" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" @@ -90,22 +90,22 @@ type CreateConfig struct { ImageID string BuiltinImgVolumes map[string]struct{} // volumes defined in the image config IDMappings *storage.IDMappingOptions - ImageVolumeType string // how to handle the image volume, either bind, tmpfs, or ignore - Interactive bool //interactive - IpcMode container.IpcMode //ipc - IP6Address string //ipv6 - IPAddress string //ip - Labels map[string]string //label - LinkLocalIP []string // link-local-ip - LogDriver string // log-driver - LogDriverOpt []string // log-opt - MacAddress string //mac-address - Name string //name - NetMode container.NetworkMode //net - Network string //network - NetworkAlias []string //network-alias - PidMode container.PidMode //pid - Pod string //pod + ImageVolumeType string // how to handle the image volume, either bind, tmpfs, or ignore + Interactive bool //interactive + IpcMode namespaces.IpcMode //ipc + IP6Address string //ipv6 + IPAddress string //ip + Labels map[string]string //label + LinkLocalIP []string // link-local-ip + LogDriver string // log-driver + LogDriverOpt []string // log-opt + MacAddress string //mac-address + Name string //name + NetMode namespaces.NetworkMode //net + Network string //network + NetworkAlias []string //network-alias + PidMode namespaces.PidMode //pid + Pod string //pod PortBindings nat.PortMap Privileged bool //privileged Publish []string //publish @@ -119,12 +119,12 @@ type CreateConfig struct { StopTimeout uint // stop-timeout Sysctl map[string]string //sysctl Systemd bool - Tmpfs []string // tmpfs - Tty bool //tty - UsernsMode container.UsernsMode //userns - User string //user - UtsMode container.UTSMode //uts - Volumes []string //volume + Tmpfs []string // tmpfs + Tty bool //tty + UsernsMode namespaces.UsernsMode //userns + User string //user + UtsMode namespaces.UTSMode //uts + Volumes []string //volume VolumesFrom []string WorkDir string //workdir MountLabel string //SecurityOpts @@ -222,7 +222,7 @@ func (c *CreateConfig) GetVolumeMounts(specMounts []spec.Mount) ([]spec.Mount, e } // GetVolumesFrom reads the create-config artifact of the container to get volumes from -// and adds it to c.Volumes of the curent container. +// and adds it to c.Volumes of the current container. func (c *CreateConfig) GetVolumesFrom() error { var options string for _, vol := range c.VolumesFrom { @@ -423,6 +423,14 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]lib if IsPod(string(c.UtsMode)) { options = append(options, libpod.WithUTSNSFromPod(pod)) } + if c.UtsMode.IsContainer() { + connectedCtr, err := c.Runtime.LookupContainer(c.UtsMode.Container()) + if err != nil { + return nil, errors.Wrapf(err, "container %q not found", c.UtsMode.Container()) + } + + options = append(options, libpod.WithUTSNSFrom(connectedCtr)) + } // TODO: MNT, USER, CGROUP options = append(options, libpod.WithStopSignal(c.StopSignal)) diff --git a/pkg/varlinkapi/containers_create.go b/pkg/varlinkapi/containers_create.go index e57f51cc1..843d7a5ba 100644 --- a/pkg/varlinkapi/containers_create.go +++ b/pkg/varlinkapi/containers_create.go @@ -12,9 +12,9 @@ import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/inspect" + "github.com/containers/libpod/pkg/namespaces" cc "github.com/containers/libpod/pkg/spec" "github.com/containers/libpod/pkg/util" - "github.com/docker/docker/api/types/container" "github.com/docker/docker/pkg/signal" "github.com/sirupsen/logrus" ) @@ -164,10 +164,10 @@ func varlinkCreateToCreateConfig(ctx context.Context, create iopodman.Create, ru LogDriverOpt: create.Log_driver_opt, Name: create.Name, Network: networkMode, - IpcMode: container.IpcMode(create.Ipc_mode), - NetMode: container.NetworkMode(networkMode), - UtsMode: container.UTSMode(create.Uts_mode), - PidMode: container.PidMode(create.Pid_mode), + IpcMode: namespaces.IpcMode(create.Ipc_mode), + NetMode: namespaces.NetworkMode(networkMode), + UtsMode: namespaces.UTSMode(create.Uts_mode), + PidMode: namespaces.PidMode(create.Pid_mode), Pod: create.Pod, Privileged: create.Privileged, Publish: create.Publish, @@ -209,7 +209,7 @@ func varlinkCreateToCreateConfig(ctx context.Context, create iopodman.Create, ru Tmpfs: create.Tmpfs, Tty: create.Tty, User: user, - UsernsMode: container.UsernsMode(create.Userns_mode), + UsernsMode: namespaces.UsernsMode(create.Userns_mode), Volumes: create.Volumes, WorkDir: workDir, } diff --git a/test/e2e/rootless_test.go b/test/e2e/rootless_test.go index 84d5cd6f1..72ca37d6e 100644 --- a/test/e2e/rootless_test.go +++ b/test/e2e/rootless_test.go @@ -6,6 +6,7 @@ import ( "os" "os/exec" "path/filepath" + "runtime" "syscall" . "github.com/onsi/ginkgo" @@ -153,6 +154,8 @@ var _ = Describe("Podman rootless", func() { runRootlessHelper := func(args []string) { f := func(rootlessTest PodmanTest, xdgRuntimeDir string, home string, mountPath string) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() env := os.Environ() env = append(env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", xdgRuntimeDir)) env = append(env, fmt.Sprintf("HOME=%s", home)) |