diff options
author | haircommander <pehunt@redhat.com> | 2018-07-27 13:58:50 -0400 |
---|---|---|
committer | Atomic Bot <atomic-devel@projectatomic.io> | 2018-08-23 18:16:28 +0000 |
commit | d5e690914dc78eca8664442e7677eb5004522bfd (patch) | |
tree | 3f7ed30e4302c871c16126a0032b8a3d51c46f98 | |
parent | 63dd200e7e47261454c7e55fed2ad972144e147f (diff) | |
download | podman-d5e690914dc78eca8664442e7677eb5004522bfd.tar.gz podman-d5e690914dc78eca8664442e7677eb5004522bfd.tar.bz2 podman-d5e690914dc78eca8664442e7677eb5004522bfd.zip |
Added option to share kernel namespaces in libpod and podman
A pause container is added to the pod if the user opts in. The default pause image and command can be overridden. Pause containers are ignored in ps unless the -a option is present. Pod inspect and pod ps show shared namespaces and pause container. A pause container can't be removed with podman rm, and a pod can be removed if it only has a pause container.
Signed-off-by: haircommander <pehunt@redhat.com>
Closes: #1187
Approved by: mheon
41 files changed, 2569 insertions, 385 deletions
diff --git a/cmd/podman/create.go b/cmd/podman/create.go index 96934560f..d6bcea7bd 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -368,16 +368,6 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim tty := c.Bool("tty") - pidMode := container.PidMode(c.String("pid")) - if !cc.IsNS(string(pidMode)) && !pidMode.Valid() { - return nil, errors.Errorf("--pid %q is not valid", c.String("pid")) - } - - usernsMode := container.UsernsMode(c.String("userns")) - if !cc.IsNS(string(usernsMode)) && !usernsMode.Valid() { - return nil, errors.Errorf("--userns %q is not valid", c.String("userns")) - } - if c.Bool("detach") && c.Bool("rm") { return nil, errors.Errorf("--rm and --detach can not be specified together") } @@ -388,14 +378,62 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim return nil, errors.Errorf("--cpu-quota and --cpus cannot be set together") } - utsMode := container.UTSMode(c.String("uts")) - if !cc.IsNS(string(utsMode)) && !utsMode.Valid() { + // Kernel Namespaces + var pod *libpod.Pod + if c.IsSet("pod") { + pod, err = runtime.LookupPod(c.String("pod")) + if err != nil { + return nil, err + } + } + + pidModeStr := c.String("pid") + if !c.IsSet("pid") && pod != nil && pod.SharesPID() { + pidModeStr = "pod" + } + pidMode := container.PidMode(pidModeStr) + if !cc.Valid(string(pidMode), pidMode) { + return nil, errors.Errorf("--pid %q is not valid", c.String("pid")) + } + + usernsModeStr := c.String("userns") + if !c.IsSet("userns") && pod != nil && pod.SharesUser() { + usernsModeStr = "pod" + } + usernsMode := container.UsernsMode(usernsModeStr) + if !cc.Valid(string(usernsMode), usernsMode) { + return nil, errors.Errorf("--userns %q is not valid", c.String("userns")) + } + + utsModeStr := c.String("uts") + if !c.IsSet("uts") && pod != nil && pod.SharesUTS() { + utsModeStr = "pod" + } + utsMode := container.UTSMode(utsModeStr) + if !cc.Valid(string(utsMode), utsMode) { return nil, errors.Errorf("--uts %q is not valid", c.String("uts")) } - ipcMode := container.IpcMode(c.String("ipc")) - if !cc.IsNS(string(ipcMode)) && !ipcMode.Valid() { + + ipcModeStr := c.String("ipc") + if !c.IsSet("ipc") && pod != nil && pod.SharesIPC() { + ipcModeStr = "pod" + } + ipcMode := container.IpcMode(ipcModeStr) + if !cc.Valid(string(ipcMode), ipcMode) { return nil, errors.Errorf("--ipc %q is not valid", ipcMode) } + netModeStr := c.String("net") + if !c.IsSet("net") && pod != nil && pod.SharesNet() { + netModeStr = "pod" + } + // Make sure if network is set to container namespace, port binding is not also being asked for + netMode := container.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") + } + } + shmDir := "" if ipcMode.IsHost() { shmDir = "/dev/shm" @@ -534,14 +572,6 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim if err != nil { return nil, errors.Wrapf(err, "unable to translate --shm-size") } - // Network - netMode := container.NetworkMode(c.String("network")) - // Make sure if network is set to container namespace, port binding is not also being asked for - if netMode.IsContainer() { - if len(c.StringSlice("publish")) > 0 || c.Bool("publish-all") { - return nil, errors.Errorf("cannot set port bindings on an existing container network namespace") - } - } // Verify the additional hosts are in correct format for _, host := range c.StringSlice("add-host") { diff --git a/cmd/podman/libpodruntime/runtime.go b/cmd/podman/libpodruntime/runtime.go index e33b70e9a..af6be0602 100644 --- a/cmd/podman/libpodruntime/runtime.go +++ b/cmd/podman/libpodruntime/runtime.go @@ -124,5 +124,14 @@ func GetRuntimeWithStorageOpts(c *cli.Context, storageOpts *storage.StoreOptions // TODO flag to set CNI plugins dir? + // Pod create options + if c.IsSet("pause-image") { + options = append(options, libpod.WithDefaultPauseImage(c.String("pause-image"))) + } + + if c.IsSet("pause-command") { + options = append(options, libpod.WithDefaultPauseCommand(c.String("pause-command"))) + } + return libpod.NewRuntime(options...) } diff --git a/cmd/podman/pod_create.go b/cmd/podman/pod_create.go index 568ace6e7..6975c9386 100644 --- a/cmd/podman/pod_create.go +++ b/cmd/podman/pod_create.go @@ -3,6 +3,7 @@ package main import ( "fmt" "os" + "strings" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" @@ -11,6 +12,11 @@ import ( "github.com/urfave/cli" ) +var ( + // CRI-O default kernel namespaces + DefaultKernelNamespaces = "ipc,net,uts" +) + var podCreateDescription = "Creates a new empty pod. The pod ID is then" + " printed to stdout. You can then start it at any time with the" + " podman pod start <pod_id> command. The pod will be created with the" + @@ -33,10 +39,27 @@ var podCreateFlags = []cli.Flag{ Name: "name, n", Usage: "Assign a name to the pod", }, + cli.BoolTFlag{ + Name: "pause", + Usage: "Create a pause container associated with the pod to share namespaces with", + }, + cli.StringFlag{ + Name: "pause-image", + Usage: "The image of the pause container to associate with the pod", + }, + cli.StringFlag{ + Name: "pause-command", + Usage: "The command to run on the pause container when the pod is started", + }, cli.StringFlag{ Name: "pod-id-file", Usage: "Write the pod ID to the file", }, + cli.StringFlag{ + Name: "share", + Usage: "A comma deliminated list of kernel namespaces the pod will share", + Value: DefaultKernelNamespaces, + }, } var podCreateCommand = cli.Command{ @@ -71,6 +94,9 @@ func podCreateCmd(c *cli.Context) error { return errors.Wrapf(err, "unable to write pod id file %s", c.String("pod-id-file")) } } + if !c.BoolT("pause") && c.IsSet("share") && c.String("share") != "none" && c.String("share") != "" { + return errors.Errorf("You cannot share kernel namespaces on the pod level without a pause container") + } if c.IsSet("cgroup-parent") { options = append(options, libpod.WithPodCgroupParent(c.String("cgroup-parent"))) @@ -88,10 +114,39 @@ func podCreateCmd(c *cli.Context) error { options = append(options, libpod.WithPodName(c.String("name"))) } + if c.BoolT("pause") { + options = append(options, libpod.WithPauseContainer()) + for _, toShare := range strings.Split(c.String("share"), ",") { + switch toShare { + case "net": + options = append(options, libpod.WithPodNet()) + case "mnt": + //options = append(options, libpod.WithPodMNT()) + logrus.Debug("Mount Namespace sharing functionality not supported") + case "pid": + options = append(options, libpod.WithPodPID()) + case "user": + // Note: more set up needs to be done before this doesn't error out a create. + logrus.Debug("User Namespace sharing functionality not supported") + case "ipc": + options = append(options, libpod.WithPodIPC()) + case "uts": + options = append(options, libpod.WithPodUTS()) + case "": + case "none": + continue + default: + return errors.Errorf("Invalid kernel namespace to share: %s. Options are: %s, or none", toShare, strings.Join(libpod.KernelNamespaces, ",")) + } + } + } + // always have containers use pod cgroups + // User Opt out is not yet supported options = append(options, libpod.WithPodCgroups()) - pod, err := runtime.NewPod(options...) + ctx := getContext() + pod, err := runtime.NewPod(ctx, options...) if err != nil { return err } diff --git a/cmd/podman/pod_ps.go b/cmd/podman/pod_ps.go index 20beae53a..20cda1276 100644 --- a/cmd/podman/pod_ps.go +++ b/cmd/podman/pod_ps.go @@ -56,8 +56,9 @@ type podPsTemplateParams struct { NumberOfContainers int Status string Cgroup string - UsePodCgroup bool ContainerInfo string + PauseContainerID string + SharedNamespaces string } // podPsJSONParams is used as a base structure for the psParams @@ -73,7 +74,8 @@ type podPsJSONParams struct { Status string `json:"status"` CtrsInfo []podPsCtrInfo `json:"containerinfo,omitempty"` Cgroup string `json:"cgroup,omitempty"` - UsePodCgroup bool `json:"podcgroup,omitempty"` + PauseContainerID string `json:"pausecontainerid,omitempty"` + SharedNamespaces []string `json:"sharednamespaces,omitempty"` } // Type declaration and functions for sorting the pod PS output @@ -111,10 +113,6 @@ func (a podPsSortedStatus) Less(i, j int) bool { var ( podPsFlags = []cli.Flag{ cli.BoolFlag{ - Name: "cgroup", - Usage: "Print the Cgroup information of the pod", - }, - cli.BoolFlag{ Name: "ctr-names", Usage: "Display the container names", }, @@ -139,6 +137,10 @@ var ( Usage: "Show the latest pod created", }, cli.BoolFlag{ + Name: "namespace, ns", + Usage: "Display namespace information of the pod", + }, + cli.BoolFlag{ Name: "no-trunc", Usage: "Do not truncate pod and container IDs", }, @@ -348,14 +350,15 @@ func genPodPsFormat(c *cli.Context) string { format = formats.IDString } else { format = "table {{.ID}}\t{{.Name}}\t{{.Status}}\t{{.Created}}" - if c.Bool("cgroup") { - format += "\t{{.Cgroup}}\t{{.UsePodCgroup}}" + if c.Bool("namespace") { + format += "\t{{.Cgroup}}\t{{.SharedNamespaces}}" } if c.Bool("ctr-names") || c.Bool("ctr-ids") || c.Bool("ctr-status") { format += "\t{{.ContainerInfo}}" } else { format += "\t{{.NumberOfContainers}}" } + format += "\t{{.PauseContainerID}}" } return format } @@ -415,6 +418,7 @@ func getPodTemplateOutput(psParams []podPsJSONParams, opts podPsOptions) ([]podP for _, psParam := range psParams { podID := psParam.ID + pauseID := psParam.PauseContainerID var ctrStr string truncated := "" @@ -424,6 +428,7 @@ func getPodTemplateOutput(psParams []podPsJSONParams, opts podPsOptions) ([]podP psParam.CtrsInfo = psParam.CtrsInfo[:NUM_CTR_INFO] truncated = "..." } + pauseID = shortID(pauseID) } for _, ctrInfo := range psParam.CtrsInfo { ctrStr += "[ " @@ -449,9 +454,10 @@ func getPodTemplateOutput(psParams []podPsJSONParams, opts podPsOptions) ([]podP Name: psParam.Name, Status: psParam.Status, NumberOfContainers: psParam.NumberOfContainers, - UsePodCgroup: psParam.UsePodCgroup, Cgroup: psParam.Cgroup, ContainerInfo: ctrStr, + PauseContainerID: pauseID, + SharedNamespaces: strings.Join(psParam.SharedNamespaces, ","), } psOutput = append(psOutput, params) @@ -460,6 +466,32 @@ func getPodTemplateOutput(psParams []podPsJSONParams, opts podPsOptions) ([]podP return psOutput, nil } +func getSharedNamespaces(pod *libpod.Pod) []string { + var shared []string + if pod.SharesPID() { + shared = append(shared, "pid") + } + if pod.SharesNet() { + shared = append(shared, "net") + } + if pod.SharesMNT() { + shared = append(shared, "mnt") + } + if pod.SharesIPC() { + shared = append(shared, "ipc") + } + if pod.SharesUser() { + shared = append(shared, "user") + } + if pod.SharesCgroup() { + shared = append(shared, "cgroup") + } + if pod.SharesUTS() { + shared = append(shared, "uts") + } + return shared +} + // getAndSortPodJSONOutput returns the container info in its raw, sorted form func getAndSortPodJSONParams(pods []*libpod.Pod, opts podPsOptions, runtime *libpod.Runtime) ([]podPsJSONParams, error) { var ( @@ -478,6 +510,10 @@ func getAndSortPodJSONParams(pods []*libpod.Pod, opts podPsOptions, runtime *lib return nil, err } + pauseContainerID, err := pod.PauseContainerID() + if err != nil { + return nil, err + } for _, ctr := range ctrs { batchInfo, err := shared.BatchContainerOp(ctr, bc_opts) if err != nil { @@ -508,9 +544,10 @@ func getAndSortPodJSONParams(pods []*libpod.Pod, opts podPsOptions, runtime *lib Name: pod.Name(), Status: status, Cgroup: pod.CgroupParent(), - UsePodCgroup: pod.UsePodCgroup(), NumberOfContainers: ctrNum, CtrsInfo: ctrsInfo, + SharedNamespaces: getSharedNamespaces(pod), + PauseContainerID: pauseContainerID, } psOutput = append(psOutput, params) diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go index 3b53fe6f1..332416d14 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -44,6 +44,7 @@ type psTemplateParams struct { User string UTS string Pod string + IsPause bool } // psJSONParams is used as a base structure for the psParams @@ -71,6 +72,7 @@ type psJSONParams struct { ContainerRunning bool `json:"ctrRunning"` Namespaces *shared.Namespace `json:"namespace,omitempty"` Pod string `json:"pod,omitempty"` + IsPause bool `json:"pause"` } // Type declaration and functions for sorting the PS output @@ -216,7 +218,7 @@ func psCmd(c *cli.Context) error { return errors.Errorf("too many arguments, ps takes no arguments") } - format := genPsFormat(c.String("format"), c.Bool("quiet"), c.Bool("size"), c.Bool("namespace"), c.Bool("pod")) + format := genPsFormat(c.String("format"), c.Bool("quiet"), c.Bool("size"), c.Bool("namespace"), c.Bool("pod"), c.Bool("all")) opts := shared.PsOptions{ All: c.Bool("all"), @@ -239,7 +241,8 @@ func psCmd(c *cli.Context) error { // only get running containers filterFuncs = append(filterFuncs, func(c *libpod.Container) bool { state, _ := c.State() - return state == libpod.ContainerStateRunning + // Don't return pause containers + return state == libpod.ContainerStateRunning && !c.IsPause() }) } @@ -417,7 +420,7 @@ func generateContainerFilterFuncs(filter, filterValue string, runtime *libpod.Ru } // generate the template based on conditions given -func genPsFormat(format string, quiet, size, namespace, pod bool) string { +func genPsFormat(format string, quiet, size, namespace, pod, pause bool) string { if format != "" { // "\t" from the command line is not being recognized as a tab // replacing the string "\t" to a tab character if the user passes in "\t" @@ -431,13 +434,16 @@ func genPsFormat(format string, quiet, size, namespace, pod bool) string { podappend = "{{.Pod}}\t" } if namespace { - return fmt.Sprintf("table {{.ID}}\t{{.Names}}\t%s{{.PID}}\t{{.Cgroup}}\t{{.IPC}}\t{{.MNT}}\t{{.NET}}\t{{.PIDNS}}\t{{.User}}\t{{.UTS}}\t", podappend) + return fmt.Sprintf("table {{.ID}}\t{{.Names}}\t%s{{.PID}}\t{{.Cgroup}}\t{{.IPC}}\t{{.MNT}}\t{{.NET}}\t{{.PIDNS}}\t{{.User}}\t{{.UTS}}", podappend) } format = "table {{.ID}}\t{{.Image}}\t{{.Command}}\t{{.Created}}\t{{.Status}}\t{{.Ports}}\t{{.Names}}\t" format += podappend if size { format += "{{.Size}}\t" } + if pause { + format += "{{.IsPause}}\t" + } return format } @@ -572,6 +578,7 @@ func getTemplateOutput(psParams []psJSONParams, opts shared.PsOptions) ([]psTemp Mounts: getMounts(psParam.Mounts, opts.NoTrunc), PID: psParam.PID, Pod: pod, + IsPause: psParam.IsPause, } if opts.Namespace { @@ -628,6 +635,7 @@ func getAndSortJSONParams(containers []*libpod.Container, opts shared.PsOptions) ContainerRunning: batchInfo.ConState == libpod.ContainerStateRunning, Namespaces: ns, Pod: ctr.PodID(), + IsPause: ctr.IsPause(), } psOutput = append(psOutput, params) diff --git a/completions/bash/podman b/completions/bash/podman index 2bf175982..7aa85053c 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -2077,6 +2077,9 @@ _podman_logout() { _podman_pod_create() { local options_with_args=" --cgroup-parent + --share + --pause-command + --pause-image --podidfile --label-file --label @@ -2085,6 +2088,7 @@ _podman_pod_create() { " local boolean_options=" + --pause " _complete_ "$options_with_args" "$boolean_options" } diff --git a/docs/podman-pod-create.1.md b/docs/podman-pod-create.1.md index 495c6934a..063dbc918 100644 --- a/docs/podman-pod-create.1.md +++ b/docs/podman-pod-create.1.md @@ -39,6 +39,22 @@ Read in a line delimited file of labels Assign a name to the pod +**--pause** + +Create a pause container and associate it with the pod. A pause container is a lightweight container used to coordinate the shared kernel namespace of a pod. Default: true + +**--pause-command**="" + +The command that will be run to start the pause container. Default: "/pause" + +**--pause-image**="" + +The image that will be created for the pause container. Default: "k8s.gcr.io/pause:3.1" + +**--share**="" + +A comma deliminated list of kernel namespaces to share. If none or "" is specified, no namespaces will be shared. The namespaces to choose from are ipc, net, pid, user, uts. + The operator can identify a pod in three ways: UUID long identifier (“f78375b1c487e03c9438c729345e54db9d20cfa2ac1fc3494b6eb60872e74778”) UUID short identifier (“f78375b1c487”) @@ -53,6 +69,10 @@ for it. The name is useful any place you need to identify a pod. # podman pod create --name test +# podman pod create --pause=false + +# podman pod create --pause-command /top + ## SEE ALSO podman-pod(1) diff --git a/libpod.conf b/libpod.conf index cef5a12d8..9de42caf2 100644 --- a/libpod.conf +++ b/libpod.conf @@ -65,3 +65,9 @@ cni_plugin_dir = [ # The default namespace is "", which corresponds to no namespace. When no # namespace is set, all containers and pods are visible. #namespace = "" + +# Default pause image name for pod pause containers +pause_image = "k8s.gcr.io/pause:3.1" + +# Default command to run the pause container +pause_command = "/pause" diff --git a/libpod/container.go b/libpod/container.go index b79258c43..2e2d29899 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -252,6 +252,19 @@ type ContainerConfig struct { UTSNsCtr string `json:"utsNsCtr,omitempty"` CgroupNsCtr string `json:"cgroupNsCtr,omitempty"` + // Whether container shares an NS with the pod + // NetNsPod conflicts with the CreateNetNS bool + // {namespace}NsPod conflicts with {namespace}NsCtr + // The pause container will be considered dependencies of the given container + // It must be started before the given container is started + IPCNsPod bool `json:"ipcNsPod,omitempty"` + MountNsPod bool `json:"mountNsPod,omitempty"` + NetNsPod bool `json:"netNsPod,omitempty"` + PIDNsPod bool `json:"pidNsPod,omitempty"` + UserNsPod bool `json:"userNsPod,omitempty"` + UTSNsPod bool `json:"utsNsPod,omitempty"` + CgroupNsPod bool `json:"cgroupNsPod,omitempty"` + // IDs of dependency containers. // These containers must be started before this container is started. Dependencies []string @@ -328,6 +341,10 @@ type ContainerConfig struct { // LocalVolumes are the built-in volumes we get from the --volumes-from flag // It picks up the built-in volumes of the container used by --volumes-from LocalVolumes []string + + // IsPause is a bool indicating whether this container is a pause container used for + // sharing kernel namespaces in a pod + IsPause bool `json:"pause"` } // ContainerStatus returns a string representation for users @@ -956,3 +973,8 @@ func (c *Container) RootGID() int { } return 0 } + +// IsPause returns whether the container is a pause container +func (c *Container) IsPause() bool { + return c.config.IsPause +} diff --git a/libpod/container_ffjson.go b/libpod/container_ffjson.go index d843beb48..02dc10e68 100644 --- a/libpod/container_ffjson.go +++ b/libpod/container_ffjson.go @@ -194,6 +194,62 @@ func (j *ContainerConfig) MarshalJSONBuf(buf fflib.EncodingBuffer) error { fflib.WriteJsonString(buf, string(j.CgroupNsCtr)) buf.WriteByte(',') } + if j.IPCNsPod != false { + if j.IPCNsPod { + buf.WriteString(`"ipcNsPod":true`) + } else { + buf.WriteString(`"ipcNsPod":false`) + } + buf.WriteByte(',') + } + if j.MountNsPod != false { + if j.MountNsPod { + buf.WriteString(`"mountNsPod":true`) + } else { + buf.WriteString(`"mountNsPod":false`) + } + buf.WriteByte(',') + } + if j.NetNsPod != false { + if j.NetNsPod { + buf.WriteString(`"netNsPod":true`) + } else { + buf.WriteString(`"netNsPod":false`) + } + buf.WriteByte(',') + } + if j.PIDNsPod != false { + if j.PIDNsPod { + buf.WriteString(`"pidNsPod":true`) + } else { + buf.WriteString(`"pidNsPod":false`) + } + buf.WriteByte(',') + } + if j.UserNsPod != false { + if j.UserNsPod { + buf.WriteString(`"userNsPod":true`) + } else { + buf.WriteString(`"userNsPod":false`) + } + buf.WriteByte(',') + } + if j.UTSNsPod != false { + if j.UTSNsPod { + buf.WriteString(`"utsNsPod":true`) + } else { + buf.WriteString(`"utsNsPod":false`) + } + buf.WriteByte(',') + } + if j.CgroupNsPod != false { + if j.CgroupNsPod { + buf.WriteString(`"cgroupNsPod":true`) + } else { + buf.WriteString(`"cgroupNsPod":false`) + } + buf.WriteByte(',') + } buf.WriteString(`"Dependencies":`) if j.Dependencies != nil { buf.WriteString(`[`) @@ -461,6 +517,11 @@ func (j *ContainerConfig) MarshalJSONBuf(buf fflib.EncodingBuffer) error { } else { buf.WriteString(`null`) } + if j.IsPause { + buf.WriteString(`,"pause":true`) + } else { + buf.WriteString(`,"pause":false`) + } buf.WriteByte('}') return nil } @@ -521,6 +582,20 @@ const ( ffjtContainerConfigCgroupNsCtr + ffjtContainerConfigIPCNsPod + + ffjtContainerConfigMountNsPod + + ffjtContainerConfigNetNsPod + + ffjtContainerConfigPIDNsPod + + ffjtContainerConfigUserNsPod + + ffjtContainerConfigUTSNsPod + + ffjtContainerConfigCgroupNsPod + ffjtContainerConfigDependencies ffjtContainerConfigCreateNetNS @@ -564,6 +639,8 @@ const ( ffjtContainerConfigExitCommand ffjtContainerConfigLocalVolumes + + ffjtContainerConfigIsPause ) var ffjKeyContainerConfigSpec = []byte("spec") @@ -618,6 +695,20 @@ var ffjKeyContainerConfigUTSNsCtr = []byte("utsNsCtr") var ffjKeyContainerConfigCgroupNsCtr = []byte("cgroupNsCtr") +var ffjKeyContainerConfigIPCNsPod = []byte("ipcNsPod") + +var ffjKeyContainerConfigMountNsPod = []byte("mountNsPod") + +var ffjKeyContainerConfigNetNsPod = []byte("netNsPod") + +var ffjKeyContainerConfigPIDNsPod = []byte("pidNsPod") + +var ffjKeyContainerConfigUserNsPod = []byte("userNsPod") + +var ffjKeyContainerConfigUTSNsPod = []byte("utsNsPod") + +var ffjKeyContainerConfigCgroupNsPod = []byte("cgroupNsPod") + var ffjKeyContainerConfigDependencies = []byte("Dependencies") var ffjKeyContainerConfigCreateNetNS = []byte("createNetNS") @@ -662,6 +753,8 @@ var ffjKeyContainerConfigExitCommand = []byte("exitCommand") var ffjKeyContainerConfigLocalVolumes = []byte("LocalVolumes") +var ffjKeyContainerConfigIsPause = []byte("pause") + // UnmarshalJSON umarshall json - template of ffjson func (j *ContainerConfig) UnmarshalJSON(input []byte) error { fs := fflib.NewFFLexer(input) @@ -770,6 +863,11 @@ mainparse: state = fflib.FFParse_want_colon goto mainparse + } else if bytes.Equal(ffjKeyContainerConfigCgroupNsPod, kn) { + currentKey = ffjtContainerConfigCgroupNsPod + state = fflib.FFParse_want_colon + goto mainparse + } else if bytes.Equal(ffjKeyContainerConfigCreateNetNS, kn) { currentKey = ffjtContainerConfigCreateNetNS state = fflib.FFParse_want_colon @@ -864,6 +962,11 @@ mainparse: currentKey = ffjtContainerConfigIPCNsCtr state = fflib.FFParse_want_colon goto mainparse + + } else if bytes.Equal(ffjKeyContainerConfigIPCNsPod, kn) { + currentKey = ffjtContainerConfigIPCNsPod + state = fflib.FFParse_want_colon + goto mainparse } case 'l': @@ -890,6 +993,11 @@ mainparse: currentKey = ffjtContainerConfigMountNsCtr state = fflib.FFParse_want_colon goto mainparse + + } else if bytes.Equal(ffjKeyContainerConfigMountNsPod, kn) { + currentKey = ffjtContainerConfigMountNsPod + state = fflib.FFParse_want_colon + goto mainparse } case 'n': @@ -909,6 +1017,11 @@ mainparse: state = fflib.FFParse_want_colon goto mainparse + } else if bytes.Equal(ffjKeyContainerConfigNetNsPod, kn) { + currentKey = ffjtContainerConfigNetNsPod + state = fflib.FFParse_want_colon + goto mainparse + } else if bytes.Equal(ffjKeyContainerConfigNetworks, kn) { currentKey = ffjtContainerConfigNetworks state = fflib.FFParse_want_colon @@ -932,6 +1045,11 @@ mainparse: state = fflib.FFParse_want_colon goto mainparse + } else if bytes.Equal(ffjKeyContainerConfigPIDNsPod, kn) { + currentKey = ffjtContainerConfigPIDNsPod + state = fflib.FFParse_want_colon + goto mainparse + } else if bytes.Equal(ffjKeyContainerConfigPortMappings, kn) { currentKey = ffjtContainerConfigPortMappings state = fflib.FFParse_want_colon @@ -941,6 +1059,11 @@ mainparse: currentKey = ffjtContainerConfigPostConfigureNetNS state = fflib.FFParse_want_colon goto mainparse + + } else if bytes.Equal(ffjKeyContainerConfigIsPause, kn) { + currentKey = ffjtContainerConfigIsPause + state = fflib.FFParse_want_colon + goto mainparse } case 'r': @@ -1011,6 +1134,16 @@ mainparse: state = fflib.FFParse_want_colon goto mainparse + } else if bytes.Equal(ffjKeyContainerConfigUserNsPod, kn) { + currentKey = ffjtContainerConfigUserNsPod + state = fflib.FFParse_want_colon + goto mainparse + + } else if bytes.Equal(ffjKeyContainerConfigUTSNsPod, kn) { + currentKey = ffjtContainerConfigUTSNsPod + state = fflib.FFParse_want_colon + goto mainparse + } else if bytes.Equal(ffjKeyContainerConfigUserVolumes, kn) { currentKey = ffjtContainerConfigUserVolumes state = fflib.FFParse_want_colon @@ -1019,6 +1152,12 @@ mainparse: } + if fflib.EqualFoldRight(ffjKeyContainerConfigIsPause, kn) { + currentKey = ffjtContainerConfigIsPause + state = fflib.FFParse_want_colon + goto mainparse + } + if fflib.EqualFoldRight(ffjKeyContainerConfigLocalVolumes, kn) { currentKey = ffjtContainerConfigLocalVolumes state = fflib.FFParse_want_colon @@ -1151,6 +1290,48 @@ mainparse: goto mainparse } + if fflib.EqualFoldRight(ffjKeyContainerConfigCgroupNsPod, kn) { + currentKey = ffjtContainerConfigCgroupNsPod + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyContainerConfigUTSNsPod, kn) { + currentKey = ffjtContainerConfigUTSNsPod + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyContainerConfigUserNsPod, kn) { + currentKey = ffjtContainerConfigUserNsPod + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyContainerConfigPIDNsPod, kn) { + currentKey = ffjtContainerConfigPIDNsPod + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyContainerConfigNetNsPod, kn) { + currentKey = ffjtContainerConfigNetNsPod + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyContainerConfigMountNsPod, kn) { + currentKey = ffjtContainerConfigMountNsPod + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyContainerConfigIPCNsPod, kn) { + currentKey = ffjtContainerConfigIPCNsPod + state = fflib.FFParse_want_colon + goto mainparse + } + if fflib.EqualFoldRight(ffjKeyContainerConfigCgroupNsCtr, kn) { currentKey = ffjtContainerConfigCgroupNsCtr state = fflib.FFParse_want_colon @@ -1402,6 +1583,27 @@ mainparse: case ffjtContainerConfigCgroupNsCtr: goto handle_CgroupNsCtr + case ffjtContainerConfigIPCNsPod: + goto handle_IPCNsPod + + case ffjtContainerConfigMountNsPod: + goto handle_MountNsPod + + case ffjtContainerConfigNetNsPod: + goto handle_NetNsPod + + case ffjtContainerConfigPIDNsPod: + goto handle_PIDNsPod + + case ffjtContainerConfigUserNsPod: + goto handle_UserNsPod + + case ffjtContainerConfigUTSNsPod: + goto handle_UTSNsPod + + case ffjtContainerConfigCgroupNsPod: + goto handle_CgroupNsPod + case ffjtContainerConfigDependencies: goto handle_Dependencies @@ -1468,6 +1670,9 @@ mainparse: case ffjtContainerConfigLocalVolumes: goto handle_LocalVolumes + case ffjtContainerConfigIsPause: + goto handle_IsPause + case ffjtContainerConfignosuchkey: err = fs.SkipField(tok) if err != nil { @@ -2264,6 +2469,251 @@ handle_CgroupNsCtr: state = fflib.FFParse_after_value goto mainparse +handle_IPCNsPod: + + /* handler: j.IPCNsPod type=bool kind=bool quoted=false*/ + + { + if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) + } + } + + { + if tok == fflib.FFTok_null { + + } else { + tmpb := fs.Output.Bytes() + + if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { + + j.IPCNsPod = true + + } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { + + j.IPCNsPod = false + + } else { + err = errors.New("unexpected bytes for true/false value") + return fs.WrapErr(err) + } + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_MountNsPod: + + /* handler: j.MountNsPod type=bool kind=bool quoted=false*/ + + { + if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) + } + } + + { + if tok == fflib.FFTok_null { + + } else { + tmpb := fs.Output.Bytes() + + if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { + + j.MountNsPod = true + + } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { + + j.MountNsPod = false + + } else { + err = errors.New("unexpected bytes for true/false value") + return fs.WrapErr(err) + } + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_NetNsPod: + + /* handler: j.NetNsPod type=bool kind=bool quoted=false*/ + + { + if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) + } + } + + { + if tok == fflib.FFTok_null { + + } else { + tmpb := fs.Output.Bytes() + + if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { + + j.NetNsPod = true + + } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { + + j.NetNsPod = false + + } else { + err = errors.New("unexpected bytes for true/false value") + return fs.WrapErr(err) + } + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_PIDNsPod: + + /* handler: j.PIDNsPod type=bool kind=bool quoted=false*/ + + { + if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) + } + } + + { + if tok == fflib.FFTok_null { + + } else { + tmpb := fs.Output.Bytes() + + if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { + + j.PIDNsPod = true + + } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { + + j.PIDNsPod = false + + } else { + err = errors.New("unexpected bytes for true/false value") + return fs.WrapErr(err) + } + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_UserNsPod: + + /* handler: j.UserNsPod type=bool kind=bool quoted=false*/ + + { + if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) + } + } + + { + if tok == fflib.FFTok_null { + + } else { + tmpb := fs.Output.Bytes() + + if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { + + j.UserNsPod = true + + } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { + + j.UserNsPod = false + + } else { + err = errors.New("unexpected bytes for true/false value") + return fs.WrapErr(err) + } + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_UTSNsPod: + + /* handler: j.UTSNsPod type=bool kind=bool quoted=false*/ + + { + if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) + } + } + + { + if tok == fflib.FFTok_null { + + } else { + tmpb := fs.Output.Bytes() + + if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { + + j.UTSNsPod = true + + } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { + + j.UTSNsPod = false + + } else { + err = errors.New("unexpected bytes for true/false value") + return fs.WrapErr(err) + } + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_CgroupNsPod: + + /* handler: j.CgroupNsPod type=bool kind=bool quoted=false*/ + + { + if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) + } + } + + { + if tok == fflib.FFTok_null { + + } else { + tmpb := fs.Output.Bytes() + + if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { + + j.CgroupNsPod = true + + } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { + + j.CgroupNsPod = false + + } else { + err = errors.New("unexpected bytes for true/false value") + return fs.WrapErr(err) + } + + } + } + + state = fflib.FFParse_after_value + goto mainparse + handle_Dependencies: /* handler: j.Dependencies type=[]string kind=slice quoted=false*/ @@ -3523,6 +3973,41 @@ handle_LocalVolumes: state = fflib.FFParse_after_value goto mainparse +handle_IsPause: + + /* handler: j.IsPause type=bool kind=bool quoted=false*/ + + { + if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) + } + } + + { + if tok == fflib.FFTok_null { + + } else { + tmpb := fs.Output.Bytes() + + if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { + + j.IsPause = true + + } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { + + j.IsPause = false + + } else { + err = errors.New("unexpected bytes for true/false value") + return fs.WrapErr(err) + } + + } + } + + state = fflib.FFParse_after_value + goto mainparse + wantedvalue: return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok)) wrongtokenerror: diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go index a7ee911a6..18a8b9b83 100644 --- a/libpod/container_inspect.go +++ b/libpod/container_inspect.go @@ -104,6 +104,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *inspect.Data) IPv6Gateway: "", MacAddress: "", // TODO }, + IsPause: c.IsPause(), } // Copy port mappings into network settings diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 32036ca7a..e276e0194 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -11,7 +11,6 @@ import ( "path/filepath" "strings" "syscall" - "time" "github.com/containers/libpod/pkg/chrootuser" "github.com/containers/libpod/pkg/hooks" @@ -23,13 +22,11 @@ import ( "github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/chrootarchive" "github.com/containers/storage/pkg/mount" - "github.com/containers/storage/pkg/stringid" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/ulule/deepcopier" "golang.org/x/text/language" ) @@ -174,38 +171,6 @@ func (c *Container) syncContainer() error { return nil } -// Make a new container -func newContainer(rspec *spec.Spec, lockDir string) (*Container, error) { - if rspec == nil { - return nil, errors.Wrapf(ErrInvalidArg, "must provide a valid runtime spec to create container") - } - - ctr := new(Container) - ctr.config = new(ContainerConfig) - ctr.state = new(containerState) - - ctr.config.ID = stringid.GenerateNonCryptoID() - - ctr.config.Spec = new(spec.Spec) - deepcopier.Copy(rspec).To(ctr.config.Spec) - ctr.config.CreatedTime = time.Now() - - ctr.config.ShmSize = DefaultShmSize - - ctr.state.BindMounts = make(map[string]string) - - // Path our lock file will reside at - lockPath := filepath.Join(lockDir, ctr.config.ID) - // Grab a lockfile at the given path - lock, err := storage.GetLockfile(lockPath) - if err != nil { - return nil, errors.Wrapf(err, "error creating lockfile for new container") - } - ctr.lock = lock - - return ctr, nil -} - // Create container root filesystem for use func (c *Container) setupStorage(ctx context.Context) error { if !c.valid { diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index ba02c9f5a..efd808b57 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -168,42 +168,91 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { } } + var podInfraContainer string + if c.config.Pod != "" { + pod, err := c.runtime.state.LookupPod(c.config.Pod) + if err != nil { + return nil, err + } + if pod.SharesNamespaces() { + if err := pod.updatePod(); err != nil { + return nil, err + } + podInfraContainer = pod.state.PauseContainerID + } + } + // Add shared namespaces from other containers if c.config.IPCNsCtr != "" { if err := c.addNamespaceContainer(&g, IPCNS, c.config.IPCNsCtr, spec.IPCNamespace); err != nil { return nil, err } } + if c.config.IPCNsPod && podInfraContainer != "" { + if err := c.addNamespaceContainer(&g, IPCNS, podInfraContainer, spec.IPCNamespace); err != nil { + return nil, err + } + } if c.config.MountNsCtr != "" { if err := c.addNamespaceContainer(&g, MountNS, c.config.MountNsCtr, spec.MountNamespace); err != nil { return nil, err } } + if c.config.MountNsPod && podInfraContainer != "" { + if err := c.addNamespaceContainer(&g, MountNS, podInfraContainer, spec.MountNamespace); err != nil { + return nil, err + } + } if c.config.NetNsCtr != "" { if err := c.addNamespaceContainer(&g, NetNS, c.config.NetNsCtr, spec.NetworkNamespace); err != nil { return nil, err } } + if c.config.NetNsPod && podInfraContainer != "" { + if err := c.addNamespaceContainer(&g, NetNS, podInfraContainer, spec.NetworkNamespace); err != nil { + return nil, err + } + } if c.config.PIDNsCtr != "" { if err := c.addNamespaceContainer(&g, PIDNS, c.config.PIDNsCtr, string(spec.PIDNamespace)); err != nil { return nil, err } } + if c.config.PIDNsPod && podInfraContainer != "" { + if err := c.addNamespaceContainer(&g, PIDNS, podInfraContainer, string(spec.PIDNamespace)); err != nil { + return nil, err + } + } if c.config.UserNsCtr != "" { if err := c.addNamespaceContainer(&g, UserNS, c.config.UserNsCtr, spec.UserNamespace); err != nil { return nil, err } } + if c.config.UserNsPod && podInfraContainer != "" { + if err := c.addNamespaceContainer(&g, UserNS, podInfraContainer, spec.UserNamespace); err != nil { + return nil, err + } + } if c.config.UTSNsCtr != "" { if err := c.addNamespaceContainer(&g, UTSNS, c.config.UTSNsCtr, spec.UTSNamespace); err != nil { return nil, err } } + if c.config.UTSNsPod && podInfraContainer != "" { + if err := c.addNamespaceContainer(&g, UTSNS, podInfraContainer, spec.UTSNamespace); err != nil { + return nil, err + } + } if c.config.CgroupNsCtr != "" { if err := c.addNamespaceContainer(&g, CgroupNS, c.config.CgroupNsCtr, spec.CgroupNamespace); err != nil { return nil, err } } + if c.config.CgroupNsPod && podInfraContainer != "" { + if err := c.addNamespaceContainer(&g, CgroupNS, podInfraContainer, spec.CgroupNamespace); err != nil { + return nil, err + } + } if c.config.Rootfs == "" { if err := idtools.MkdirAllAs(c.state.RealMountpoint, 0700, c.RootUID(), c.RootGID()); err != nil { diff --git a/libpod/options.go b/libpod/options.go index 7bb4a3632..c5e32d20e 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -304,6 +304,37 @@ func WithNamespace(ns string) RuntimeOption { } } +// WithDefaultPauseImage sets the pause image for libpod. +// A pause image is used for inter-container kernel +// namespace sharing within a pod. Typically, a pause +// container is lightweight and is there to reap +// zombie processes within its pid namespace. +func WithDefaultPauseImage(img string) RuntimeOption { + return func(rt *Runtime) error { + if rt.valid { + return ErrRuntimeFinalized + } + + rt.config.PauseImage = img + + return nil + } +} + +// WithDefaultPauseCommand sets the command to +// run on pause container start up. +func WithDefaultPauseCommand(cmd string) RuntimeOption { + return func(rt *Runtime) error { + if rt.valid { + return ErrRuntimeFinalized + } + + rt.config.PauseCommand = cmd + + return nil + } +} + // Container Creation Options // WithShmDir sets the directory that should be mounted on /dev/shm. @@ -518,6 +549,132 @@ func WithExitCommand(exitCommand []string) CtrCreateOption { } } +// WithIPCNSFromPod indicates the the container should join the IPC namespace of +// its pod +func WithIPCNSFromPod() CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return ErrCtrFinalized + } + + if ctr.config.Pod == "" { + return errors.Wrapf(ErrInvalidArg, "container is not a member of any pod") + } + + ctr.config.IPCNsPod = true + + return nil + } +} + +// WithMountNSFromPod indicates the the container should join the Mount namespace of +// its pod +func WithMountNSFromPod() CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return ErrCtrFinalized + } + + if ctr.config.Pod == "" { + return errors.Wrapf(ErrInvalidArg, "container is not a member of any pod") + } + + ctr.config.MountNsPod = true + + return nil + } +} + +// WithNetNSFromPod indicates the the container should join the network namespace of +// its pod +func WithNetNSFromPod() CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return ErrCtrFinalized + } + + if ctr.config.Pod == "" { + return errors.Wrapf(ErrInvalidArg, "container is not a member of any pod") + } + + ctr.config.NetNsPod = true + + return nil + } +} + +// WithPIDNSFromPod indicates the the container should join the PID namespace of +// its pod +func WithPIDNSFromPod() CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return ErrCtrFinalized + } + + if ctr.config.Pod == "" { + return errors.Wrapf(ErrInvalidArg, "container is not a member of any pod") + } + + ctr.config.PIDNsPod = true + + return nil + } +} + +// WithUTSNSFromPod indicates the the container should join the UTS namespace of +// its pod +func WithUTSNSFromPod() CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return ErrCtrFinalized + } + + if ctr.config.Pod == "" { + return errors.Wrapf(ErrInvalidArg, "container is not a member of any pod") + } + + ctr.config.UTSNsPod = true + + return nil + } +} + +// WithUserNSFromPod indicates the the container should join the User namespace of +// its pod +func WithUserNSFromPod() CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return ErrCtrFinalized + } + + if ctr.config.Pod == "" { + return errors.Wrapf(ErrInvalidArg, "container is not a member of any pod") + } + + ctr.config.UserNsPod = true + + return nil + } +} + +// WithCgroupNSFromPod indicates the the container should join the Cgroup namespace of +// its pod +func WithCgroupNSFromPod() CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return ErrCtrFinalized + } + + if ctr.config.Pod == "" { + return errors.Wrapf(ErrInvalidArg, "container is not a member of any pod") + } + + ctr.config.CgroupNsPod = true + + return nil + } +} + // WithIPCNSFrom indicates the the container should join the IPC namespace of // the given container. // If the container has joined a pod, it can only join the namespaces of @@ -999,6 +1156,20 @@ func WithCtrNamespace(ns string) CtrCreateOption { } } +// withIsPause sets the container to be a pause container. This means the container will be sometimes hidden +// and expected to be the first container in the pod. +func withIsPause() CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return ErrCtrFinalized + } + + ctr.config.IsPause = true + + return nil + } +} + // Pod Creation Options // WithPodName sets the name of the pod. @@ -1080,3 +1251,112 @@ func WithPodNamespace(ns string) PodCreateOption { return nil } } + +// WithPodIPC tells containers in this pod to use the ipc namespace +// created for this pod. +// Containers in a pod will inherit the kernel namespaces from the +// first container added. +func WithPodIPC() PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return ErrPodFinalized + } + + pod.config.UsePodIPC = true + + return nil + } +} + +// WithPodNet tells containers in this pod to use the network namespace +// created for this pod. +// Containers in a pod will inherit the kernel namespaces from the +// first container added. +func WithPodNet() PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return ErrPodFinalized + } + + pod.config.UsePodNet = true + + return nil + } +} + +// WithPodMNT tells containers in this pod to use the mount namespace +// created for this pod. +// Containers in a pod will inherit the kernel namespaces from the +// first container added. +func WithPodMNT() PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return ErrPodFinalized + } + + pod.config.UsePodMNT = true + + return nil + } +} + +// WithPodUser tells containers in this pod to use the user namespace +// created for this pod. +// Containers in a pod will inherit the kernel namespaces from the +// first container added. +func WithPodUser() PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return ErrPodFinalized + } + + pod.config.UsePodUser = true + + return nil + } +} + +// WithPodPID tells containers in this pod to use the pid namespace +// created for this pod. +// Containers in a pod will inherit the kernel namespaces from the +// first container added. +func WithPodPID() PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return ErrPodFinalized + } + + pod.config.UsePodPID = true + + return nil + } +} + +// WithPodUTS tells containers in this pod to use the uts namespace +// created for this pod. +// Containers in a pod will inherit the kernel namespaces from the +// first container added. +func WithPodUTS() PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return ErrPodFinalized + } + + pod.config.UsePodUTS = true + + return nil + } +} + +// WithPauseContainer tells the pod to create a pause container +func WithPauseContainer() PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return ErrPodFinalized + } + + pod.config.PauseContainer.HasPauseContainer = true + + return nil + } +} diff --git a/libpod/pod.go b/libpod/pod.go index 666480aa8..e70cd9138 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -7,6 +7,11 @@ import ( "github.com/pkg/errors" ) +var ( + // KernelNamespaces is a list of the kernel namespaces a pod can share + KernelNamespaces = []string{"ipc", "net", "pid", "user", "mnt", "uts", "cgroup"} +) + // Pod represents a group of containers that are managed together. // Any operations on a Pod that access state must begin with a call to // updatePod(). @@ -18,6 +23,7 @@ import ( // function takes the pod lock and accesses any part of state, it should // updatePod() immediately after locking. // ffjson: skip +// Pod represents a group of containers that may share namespaces type Pod struct { config *PodConfig state *podState @@ -38,11 +44,23 @@ type PodConfig struct { Labels map[string]string `json:"labels"` // CgroupParent contains the pod's CGroup parent CgroupParent string `json:"cgroupParent"` + // UsePodCgroup indicates whether the pod will create its own CGroup and // join containers to it. // If true, all containers joined to the pod will use the pod cgroup as // their cgroup parent, and cannot set a different cgroup parent - UsePodCgroup bool `json:"usePodCgroup"` + UsePodCgroup bool `json:"sharesCgroup,omitempty"` + + // The following UsePod{kernelNamespace} indicate whether the containers + // in the pod will inherit the namespace from the first container in the pod. + UsePodPID bool `json:"sharesPid,omitempty"` + UsePodIPC bool `json:"sharesIpc,omitempty"` + UsePodNet bool `json:"sharesNet,omitempty"` + UsePodMNT bool `json:"sharesMnt,omitempty"` + UsePodUser bool `json:"sharesUser,omitempty"` + UsePodUTS bool `json:"sharesUts,omitempty"` + + PauseContainer *PauseContainerConfig `json:"pauseConfig"` // Time pod was created CreatedTime time.Time `json:"created"` @@ -52,6 +70,9 @@ type PodConfig struct { type podState struct { // CgroupPath is the path to the pod's CGroup CgroupPath string `json:"cgroupPath"` + // PauseContainerID is the container that holds pod namespace information + // Most often a pause container + PauseContainerID string } // PodInspect represents the data we want to display for @@ -64,7 +85,8 @@ type PodInspect struct { // PodInspectState contains inspect data on the pod's state type PodInspectState struct { - CgroupPath string `json:"cgroupPath"` + CgroupPath string `json:"cgroupPath"` + PauseContainerID string `json:"pauseContainerID"` } // PodContainerInfo keeps information on a container in a pod @@ -73,6 +95,11 @@ type PodContainerInfo struct { State string `json:"state"` } +// PauseContainerConfig is the configuration for the pod's pause container +type PauseContainerConfig struct { + HasPauseContainer bool `json:"makePauseContainer"` +} + // ID retrieves the pod's ID func (p *Pod) ID() string { return p.config.ID @@ -109,9 +136,45 @@ func (p *Pod) CgroupParent() string { return p.config.CgroupParent } -// UsePodCgroup returns whether containers in the pod will default to this pod's +// SharesPID returns whether containers in pod +// default to use PID namespace of first container in pod +func (p *Pod) SharesPID() bool { + return p.config.UsePodPID +} + +// SharesIPC returns whether containers in pod +// default to use IPC namespace of first container in pod +func (p *Pod) SharesIPC() bool { + return p.config.UsePodIPC +} + +// SharesNet returns whether containers in pod +// default to use network namespace of first container in pod +func (p *Pod) SharesNet() bool { + return p.config.UsePodNet +} + +// SharesMNT returns whether containers in pod +// default to use PID namespace of first container in pod +func (p *Pod) SharesMNT() bool { + return p.config.UsePodMNT +} + +// SharesUser returns whether containers in pod +// default to use user namespace of first container in pod +func (p *Pod) SharesUser() bool { + return p.config.UsePodUser +} + +// SharesUTS returns whether containers in pod +// default to use UTS namespace of first container in pod +func (p *Pod) SharesUTS() bool { + return p.config.UsePodUTS +} + +// SharesCgroup returns whether containers in the pod will default to this pod's // cgroup instead of the default libpod parent -func (p *Pod) UsePodCgroup() bool { +func (p *Pod) SharesCgroup() bool { return p.config.UsePodCgroup } @@ -161,6 +224,30 @@ func (p *Pod) allContainers() ([]*Container, error) { return p.runtime.state.PodContainers(p) } +// HasPauseContainer returns whether the pod will create a pause container +func (p *Pod) HasPauseContainer() bool { + return p.config.PauseContainer.HasPauseContainer +} + +// SharesNamespaces checks if the pod has any kernel namespaces set as shared. A pause container will not be +// created if no kernel namespaces are shared. +func (p *Pod) SharesNamespaces() bool { + return p.SharesPID() || p.SharesIPC() || p.SharesNet() || p.SharesMNT() || p.SharesUser() || p.SharesUTS() +} + +// PauseContainerID returns a the pause container ID for a pod. +// If the container returned is "", the pod has no pause container. +func (p *Pod) PauseContainerID() (string, error) { + p.lock.Lock() + defer p.lock.Unlock() + + if err := p.updatePod(); err != nil { + return "", err + } + + return p.state.PauseContainerID, nil +} + // TODO add pod batching // Lock pod to avoid lock contention // Store and lock all containers (no RemoveContainer in batch guarantees cache will not become stale) diff --git a/libpod/pod_api.go b/libpod/pod_api.go index d1e19063c..096c9b513 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -426,13 +426,18 @@ func (p *Pod) Inspect() (*PodInspect, error) { } podContainers = append(podContainers, pc) } + pauseContainerID := p.state.PauseContainerID + if err != nil { + return &PodInspect{}, err + } config := new(PodConfig) deepcopier.Copy(p.config).To(config) inspectData := PodInspect{ Config: config, State: &PodInspectState{ - CgroupPath: p.state.CgroupPath, + CgroupPath: p.state.CgroupPath, + PauseContainerID: pauseContainerID, }, Containers: podContainers, } diff --git a/libpod/pod_ffjson.go b/libpod/pod_ffjson.go index 36b1cf08f..a2030bb4c 100644 --- a/libpod/pod_ffjson.go +++ b/libpod/pod_ffjson.go @@ -12,6 +12,212 @@ import ( ) // MarshalJSON marshal bytes to json - template +func (j *PauseContainerConfig) MarshalJSON() ([]byte, error) { + var buf fflib.Buffer + if j == nil { + buf.WriteString("null") + return buf.Bytes(), nil + } + err := j.MarshalJSONBuf(&buf) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// MarshalJSONBuf marshal buff to json - template +func (j *PauseContainerConfig) MarshalJSONBuf(buf fflib.EncodingBuffer) error { + if j == nil { + buf.WriteString("null") + return nil + } + var err error + var obj []byte + _ = obj + _ = err + if j.HasPauseContainer { + buf.WriteString(`{"makePauseContainer":true`) + } else { + buf.WriteString(`{"makePauseContainer":false`) + } + buf.WriteByte('}') + return nil +} + +const ( + ffjtPauseContainerConfigbase = iota + ffjtPauseContainerConfignosuchkey + + ffjtPauseContainerConfigHasPauseContainer +) + +var ffjKeyPauseContainerConfigHasPauseContainer = []byte("makePauseContainer") + +// UnmarshalJSON umarshall json - template of ffjson +func (j *PauseContainerConfig) UnmarshalJSON(input []byte) error { + fs := fflib.NewFFLexer(input) + return j.UnmarshalJSONFFLexer(fs, fflib.FFParse_map_start) +} + +// UnmarshalJSONFFLexer fast json unmarshall - template ffjson +func (j *PauseContainerConfig) UnmarshalJSONFFLexer(fs *fflib.FFLexer, state fflib.FFParseState) error { + var err error + currentKey := ffjtPauseContainerConfigbase + _ = currentKey + tok := fflib.FFTok_init + wantedTok := fflib.FFTok_init + +mainparse: + for { + tok = fs.Scan() + // println(fmt.Sprintf("debug: tok: %v state: %v", tok, state)) + if tok == fflib.FFTok_error { + goto tokerror + } + + switch state { + + case fflib.FFParse_map_start: + if tok != fflib.FFTok_left_bracket { + wantedTok = fflib.FFTok_left_bracket + goto wrongtokenerror + } + state = fflib.FFParse_want_key + continue + + case fflib.FFParse_after_value: + if tok == fflib.FFTok_comma { + state = fflib.FFParse_want_key + } else if tok == fflib.FFTok_right_bracket { + goto done + } else { + wantedTok = fflib.FFTok_comma + goto wrongtokenerror + } + + case fflib.FFParse_want_key: + // json {} ended. goto exit. woo. + if tok == fflib.FFTok_right_bracket { + goto done + } + if tok != fflib.FFTok_string { + wantedTok = fflib.FFTok_string + goto wrongtokenerror + } + + kn := fs.Output.Bytes() + if len(kn) <= 0 { + // "" case. hrm. + currentKey = ffjtPauseContainerConfignosuchkey + state = fflib.FFParse_want_colon + goto mainparse + } else { + switch kn[0] { + + case 'm': + + if bytes.Equal(ffjKeyPauseContainerConfigHasPauseContainer, kn) { + currentKey = ffjtPauseContainerConfigHasPauseContainer + state = fflib.FFParse_want_colon + goto mainparse + } + + } + + if fflib.EqualFoldRight(ffjKeyPauseContainerConfigHasPauseContainer, kn) { + currentKey = ffjtPauseContainerConfigHasPauseContainer + state = fflib.FFParse_want_colon + goto mainparse + } + + currentKey = ffjtPauseContainerConfignosuchkey + state = fflib.FFParse_want_colon + goto mainparse + } + + case fflib.FFParse_want_colon: + if tok != fflib.FFTok_colon { + wantedTok = fflib.FFTok_colon + goto wrongtokenerror + } + state = fflib.FFParse_want_value + continue + case fflib.FFParse_want_value: + + if tok == fflib.FFTok_left_brace || tok == fflib.FFTok_left_bracket || tok == fflib.FFTok_integer || tok == fflib.FFTok_double || tok == fflib.FFTok_string || tok == fflib.FFTok_bool || tok == fflib.FFTok_null { + switch currentKey { + + case ffjtPauseContainerConfigHasPauseContainer: + goto handle_HasPauseContainer + + case ffjtPauseContainerConfignosuchkey: + err = fs.SkipField(tok) + if err != nil { + return fs.WrapErr(err) + } + state = fflib.FFParse_after_value + goto mainparse + } + } else { + goto wantedvalue + } + } + } + +handle_HasPauseContainer: + + /* handler: j.HasPauseContainer type=bool kind=bool quoted=false*/ + + { + if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) + } + } + + { + if tok == fflib.FFTok_null { + + } else { + tmpb := fs.Output.Bytes() + + if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { + + j.HasPauseContainer = true + + } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { + + j.HasPauseContainer = false + + } else { + err = errors.New("unexpected bytes for true/false value") + return fs.WrapErr(err) + } + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +wantedvalue: + return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok)) +wrongtokenerror: + return fs.WrapErr(fmt.Errorf("ffjson: wanted token: %v, but got token: %v output=%s", wantedTok, tok, fs.Output.String())) +tokerror: + if fs.BigError != nil { + return fs.WrapErr(fs.BigError) + } + err = fs.Error.ToError() + if err != nil { + return fs.WrapErr(err) + } + panic("ffjson-generated: unreachable, please report bug.") +done: + + return nil +} + +// MarshalJSON marshal bytes to json - template func (j *PodConfig) MarshalJSON() ([]byte, error) { var buf fflib.Buffer if j == nil { @@ -60,10 +266,76 @@ func (j *PodConfig) MarshalJSONBuf(buf fflib.EncodingBuffer) error { } buf.WriteString(`,"cgroupParent":`) fflib.WriteJsonString(buf, string(j.CgroupParent)) - if j.UsePodCgroup { - buf.WriteString(`,"usePodCgroup":true`) + buf.WriteByte(',') + if j.UsePodCgroup != false { + if j.UsePodCgroup { + buf.WriteString(`"sharesCgroup":true`) + } else { + buf.WriteString(`"sharesCgroup":false`) + } + buf.WriteByte(',') + } + if j.UsePodPID != false { + if j.UsePodPID { + buf.WriteString(`"sharesPid":true`) + } else { + buf.WriteString(`"sharesPid":false`) + } + buf.WriteByte(',') + } + if j.UsePodIPC != false { + if j.UsePodIPC { + buf.WriteString(`"sharesIpc":true`) + } else { + buf.WriteString(`"sharesIpc":false`) + } + buf.WriteByte(',') + } + if j.UsePodNet != false { + if j.UsePodNet { + buf.WriteString(`"sharesNet":true`) + } else { + buf.WriteString(`"sharesNet":false`) + } + buf.WriteByte(',') + } + if j.UsePodMNT != false { + if j.UsePodMNT { + buf.WriteString(`"sharesMnt":true`) + } else { + buf.WriteString(`"sharesMnt":false`) + } + buf.WriteByte(',') + } + if j.UsePodUser != false { + if j.UsePodUser { + buf.WriteString(`"sharesUser":true`) + } else { + buf.WriteString(`"sharesUser":false`) + } + buf.WriteByte(',') + } + if j.UsePodUTS != false { + if j.UsePodUTS { + buf.WriteString(`"sharesUts":true`) + } else { + buf.WriteString(`"sharesUts":false`) + } + buf.WriteByte(',') + } + if j.PauseContainer != nil { + buf.WriteString(`"pauseConfig":`) + + { + + err = j.PauseContainer.MarshalJSONBuf(buf) + if err != nil { + return err + } + + } } else { - buf.WriteString(`,"usePodCgroup":false`) + buf.WriteString(`"pauseConfig":null`) } buf.WriteString(`,"created":`) @@ -96,6 +368,20 @@ const ( ffjtPodConfigUsePodCgroup + ffjtPodConfigUsePodPID + + ffjtPodConfigUsePodIPC + + ffjtPodConfigUsePodNet + + ffjtPodConfigUsePodMNT + + ffjtPodConfigUsePodUser + + ffjtPodConfigUsePodUTS + + ffjtPodConfigPauseContainer + ffjtPodConfigCreatedTime ) @@ -109,7 +395,21 @@ var ffjKeyPodConfigLabels = []byte("labels") var ffjKeyPodConfigCgroupParent = []byte("cgroupParent") -var ffjKeyPodConfigUsePodCgroup = []byte("usePodCgroup") +var ffjKeyPodConfigUsePodCgroup = []byte("sharesCgroup") + +var ffjKeyPodConfigUsePodPID = []byte("sharesPid") + +var ffjKeyPodConfigUsePodIPC = []byte("sharesIpc") + +var ffjKeyPodConfigUsePodNet = []byte("sharesNet") + +var ffjKeyPodConfigUsePodMNT = []byte("sharesMnt") + +var ffjKeyPodConfigUsePodUser = []byte("sharesUser") + +var ffjKeyPodConfigUsePodUTS = []byte("sharesUts") + +var ffjKeyPodConfigPauseContainer = []byte("pauseConfig") var ffjKeyPodConfigCreatedTime = []byte("created") @@ -216,12 +516,50 @@ mainparse: goto mainparse } - case 'u': + case 'p': + + if bytes.Equal(ffjKeyPodConfigPauseContainer, kn) { + currentKey = ffjtPodConfigPauseContainer + state = fflib.FFParse_want_colon + goto mainparse + } + + case 's': if bytes.Equal(ffjKeyPodConfigUsePodCgroup, kn) { currentKey = ffjtPodConfigUsePodCgroup state = fflib.FFParse_want_colon goto mainparse + + } else if bytes.Equal(ffjKeyPodConfigUsePodPID, kn) { + currentKey = ffjtPodConfigUsePodPID + state = fflib.FFParse_want_colon + goto mainparse + + } else if bytes.Equal(ffjKeyPodConfigUsePodIPC, kn) { + currentKey = ffjtPodConfigUsePodIPC + state = fflib.FFParse_want_colon + goto mainparse + + } else if bytes.Equal(ffjKeyPodConfigUsePodNet, kn) { + currentKey = ffjtPodConfigUsePodNet + state = fflib.FFParse_want_colon + goto mainparse + + } else if bytes.Equal(ffjKeyPodConfigUsePodMNT, kn) { + currentKey = ffjtPodConfigUsePodMNT + state = fflib.FFParse_want_colon + goto mainparse + + } else if bytes.Equal(ffjKeyPodConfigUsePodUser, kn) { + currentKey = ffjtPodConfigUsePodUser + state = fflib.FFParse_want_colon + goto mainparse + + } else if bytes.Equal(ffjKeyPodConfigUsePodUTS, kn) { + currentKey = ffjtPodConfigUsePodUTS + state = fflib.FFParse_want_colon + goto mainparse } } @@ -232,6 +570,48 @@ mainparse: goto mainparse } + if fflib.EqualFoldRight(ffjKeyPodConfigPauseContainer, kn) { + currentKey = ffjtPodConfigPauseContainer + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyPodConfigUsePodUTS, kn) { + currentKey = ffjtPodConfigUsePodUTS + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyPodConfigUsePodUser, kn) { + currentKey = ffjtPodConfigUsePodUser + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyPodConfigUsePodMNT, kn) { + currentKey = ffjtPodConfigUsePodMNT + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyPodConfigUsePodNet, kn) { + currentKey = ffjtPodConfigUsePodNet + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyPodConfigUsePodIPC, kn) { + currentKey = ffjtPodConfigUsePodIPC + state = fflib.FFParse_want_colon + goto mainparse + } + + if fflib.EqualFoldRight(ffjKeyPodConfigUsePodPID, kn) { + currentKey = ffjtPodConfigUsePodPID + state = fflib.FFParse_want_colon + goto mainparse + } + if fflib.EqualFoldRight(ffjKeyPodConfigUsePodCgroup, kn) { currentKey = ffjtPodConfigUsePodCgroup state = fflib.FFParse_want_colon @@ -303,6 +683,27 @@ mainparse: case ffjtPodConfigUsePodCgroup: goto handle_UsePodCgroup + case ffjtPodConfigUsePodPID: + goto handle_UsePodPID + + case ffjtPodConfigUsePodIPC: + goto handle_UsePodIPC + + case ffjtPodConfigUsePodNet: + goto handle_UsePodNet + + case ffjtPodConfigUsePodMNT: + goto handle_UsePodMNT + + case ffjtPodConfigUsePodUser: + goto handle_UsePodUser + + case ffjtPodConfigUsePodUTS: + goto handle_UsePodUTS + + case ffjtPodConfigPauseContainer: + goto handle_PauseContainer + case ffjtPodConfigCreatedTime: goto handle_CreatedTime @@ -564,6 +965,242 @@ handle_UsePodCgroup: state = fflib.FFParse_after_value goto mainparse +handle_UsePodPID: + + /* handler: j.UsePodPID type=bool kind=bool quoted=false*/ + + { + if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) + } + } + + { + if tok == fflib.FFTok_null { + + } else { + tmpb := fs.Output.Bytes() + + if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { + + j.UsePodPID = true + + } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { + + j.UsePodPID = false + + } else { + err = errors.New("unexpected bytes for true/false value") + return fs.WrapErr(err) + } + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_UsePodIPC: + + /* handler: j.UsePodIPC type=bool kind=bool quoted=false*/ + + { + if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) + } + } + + { + if tok == fflib.FFTok_null { + + } else { + tmpb := fs.Output.Bytes() + + if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { + + j.UsePodIPC = true + + } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { + + j.UsePodIPC = false + + } else { + err = errors.New("unexpected bytes for true/false value") + return fs.WrapErr(err) + } + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_UsePodNet: + + /* handler: j.UsePodNet type=bool kind=bool quoted=false*/ + + { + if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) + } + } + + { + if tok == fflib.FFTok_null { + + } else { + tmpb := fs.Output.Bytes() + + if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { + + j.UsePodNet = true + + } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { + + j.UsePodNet = false + + } else { + err = errors.New("unexpected bytes for true/false value") + return fs.WrapErr(err) + } + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_UsePodMNT: + + /* handler: j.UsePodMNT type=bool kind=bool quoted=false*/ + + { + if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) + } + } + + { + if tok == fflib.FFTok_null { + + } else { + tmpb := fs.Output.Bytes() + + if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { + + j.UsePodMNT = true + + } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { + + j.UsePodMNT = false + + } else { + err = errors.New("unexpected bytes for true/false value") + return fs.WrapErr(err) + } + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_UsePodUser: + + /* handler: j.UsePodUser type=bool kind=bool quoted=false*/ + + { + if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) + } + } + + { + if tok == fflib.FFTok_null { + + } else { + tmpb := fs.Output.Bytes() + + if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { + + j.UsePodUser = true + + } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { + + j.UsePodUser = false + + } else { + err = errors.New("unexpected bytes for true/false value") + return fs.WrapErr(err) + } + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_UsePodUTS: + + /* handler: j.UsePodUTS type=bool kind=bool quoted=false*/ + + { + if tok != fflib.FFTok_bool && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for bool", tok)) + } + } + + { + if tok == fflib.FFTok_null { + + } else { + tmpb := fs.Output.Bytes() + + if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 { + + j.UsePodUTS = true + + } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 { + + j.UsePodUTS = false + + } else { + err = errors.New("unexpected bytes for true/false value") + return fs.WrapErr(err) + } + + } + } + + state = fflib.FFParse_after_value + goto mainparse + +handle_PauseContainer: + + /* handler: j.PauseContainer type=libpod.PauseContainerConfig kind=struct quoted=false*/ + + { + if tok == fflib.FFTok_null { + + j.PauseContainer = nil + + } else { + + if j.PauseContainer == nil { + j.PauseContainer = new(PauseContainerConfig) + } + + err = j.PauseContainer.UnmarshalJSONFFLexer(fs, fflib.FFParse_want_key) + if err != nil { + return err + } + } + state = fflib.FFParse_after_value + } + + state = fflib.FFParse_after_value + goto mainparse + handle_CreatedTime: /* handler: j.CreatedTime type=time.Time kind=struct quoted=false*/ @@ -1586,6 +2223,8 @@ func (j *PodInspectState) MarshalJSONBuf(buf fflib.EncodingBuffer) error { _ = err buf.WriteString(`{"cgroupPath":`) fflib.WriteJsonString(buf, string(j.CgroupPath)) + buf.WriteString(`,"pauseContainerID":`) + fflib.WriteJsonString(buf, string(j.PauseContainerID)) buf.WriteByte('}') return nil } @@ -1595,10 +2234,14 @@ const ( ffjtPodInspectStatenosuchkey ffjtPodInspectStateCgroupPath + + ffjtPodInspectStatePauseContainerID ) var ffjKeyPodInspectStateCgroupPath = []byte("cgroupPath") +var ffjKeyPodInspectStatePauseContainerID = []byte("pauseContainerID") + // UnmarshalJSON umarshall json - template of ffjson func (j *PodInspectState) UnmarshalJSON(input []byte) error { fs := fflib.NewFFLexer(input) @@ -1668,6 +2311,20 @@ mainparse: goto mainparse } + case 'p': + + if bytes.Equal(ffjKeyPodInspectStatePauseContainerID, kn) { + currentKey = ffjtPodInspectStatePauseContainerID + state = fflib.FFParse_want_colon + goto mainparse + } + + } + + if fflib.EqualFoldRight(ffjKeyPodInspectStatePauseContainerID, kn) { + currentKey = ffjtPodInspectStatePauseContainerID + state = fflib.FFParse_want_colon + goto mainparse } if fflib.SimpleLetterEqualFold(ffjKeyPodInspectStateCgroupPath, kn) { @@ -1696,6 +2353,9 @@ mainparse: case ffjtPodInspectStateCgroupPath: goto handle_CgroupPath + case ffjtPodInspectStatePauseContainerID: + goto handle_PauseContainerID + case ffjtPodInspectStatenosuchkey: err = fs.SkipField(tok) if err != nil { @@ -1736,6 +2396,32 @@ handle_CgroupPath: state = fflib.FFParse_after_value goto mainparse +handle_PauseContainerID: + + /* handler: j.PauseContainerID type=string kind=string quoted=false*/ + + { + + { + if tok != fflib.FFTok_string && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for string", tok)) + } + } + + if tok == fflib.FFTok_null { + + } else { + + outBuf := fs.Output.Bytes() + + j.PauseContainerID = string(string(outBuf)) + + } + } + + state = fflib.FFParse_after_value + goto mainparse + wantedvalue: return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok)) wrongtokenerror: @@ -1780,6 +2466,8 @@ func (j *podState) MarshalJSONBuf(buf fflib.EncodingBuffer) error { _ = err buf.WriteString(`{"cgroupPath":`) fflib.WriteJsonString(buf, string(j.CgroupPath)) + buf.WriteString(`,"PauseContainerID":`) + fflib.WriteJsonString(buf, string(j.PauseContainerID)) buf.WriteByte('}') return nil } @@ -1789,10 +2477,14 @@ const ( ffjtpodStatenosuchkey ffjtpodStateCgroupPath + + ffjtpodStatePauseContainerID ) var ffjKeypodStateCgroupPath = []byte("cgroupPath") +var ffjKeypodStatePauseContainerID = []byte("PauseContainerID") + // UnmarshalJSON umarshall json - template of ffjson func (j *podState) UnmarshalJSON(input []byte) error { fs := fflib.NewFFLexer(input) @@ -1854,6 +2546,14 @@ mainparse: } else { switch kn[0] { + case 'P': + + if bytes.Equal(ffjKeypodStatePauseContainerID, kn) { + currentKey = ffjtpodStatePauseContainerID + state = fflib.FFParse_want_colon + goto mainparse + } + case 'c': if bytes.Equal(ffjKeypodStateCgroupPath, kn) { @@ -1864,6 +2564,12 @@ mainparse: } + if fflib.EqualFoldRight(ffjKeypodStatePauseContainerID, kn) { + currentKey = ffjtpodStatePauseContainerID + state = fflib.FFParse_want_colon + goto mainparse + } + if fflib.SimpleLetterEqualFold(ffjKeypodStateCgroupPath, kn) { currentKey = ffjtpodStateCgroupPath state = fflib.FFParse_want_colon @@ -1890,6 +2596,9 @@ mainparse: case ffjtpodStateCgroupPath: goto handle_CgroupPath + case ffjtpodStatePauseContainerID: + goto handle_PauseContainerID + case ffjtpodStatenosuchkey: err = fs.SkipField(tok) if err != nil { @@ -1930,6 +2639,32 @@ handle_CgroupPath: state = fflib.FFParse_after_value goto mainparse +handle_PauseContainerID: + + /* handler: j.PauseContainerID type=string kind=string quoted=false*/ + + { + + { + if tok != fflib.FFTok_string && tok != fflib.FFTok_null { + return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for string", tok)) + } + } + + if tok == fflib.FFTok_null { + + } else { + + outBuf := fs.Output.Bytes() + + j.PauseContainerID = string(string(outBuf)) + + } + } + + state = fflib.FFParse_after_value + goto mainparse + wantedvalue: return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok)) wrongtokenerror: diff --git a/libpod/pod_internal.go b/libpod/pod_internal.go index 1ba4487ab..fb0b68906 100644 --- a/libpod/pod_internal.go +++ b/libpod/pod_internal.go @@ -20,6 +20,7 @@ func newPod(lockDir string, runtime *Runtime) (*Pod, error) { pod.config.ID = stringid.GenerateNonCryptoID() pod.config.Labels = make(map[string]string) pod.config.CreatedTime = time.Now() + pod.config.PauseContainer = new(PauseContainerConfig) pod.state = new(podState) pod.runtime = runtime diff --git a/libpod/runtime.go b/libpod/runtime.go index 73f516cd5..7e006b1fc 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -56,6 +56,11 @@ const ( // configuration file. If OverrideConfigPath exists, it will be used in // place of the configuration file pointed to by ConfigPath. OverrideConfigPath = "/etc/containers/libpod.conf" + + // DefaultPauseImage to use for pause container + DefaultPauseImage = "k8s.gcr.io/pause:3.1" + // DefaultPauseCommand to be run in a pause container + DefaultPauseCommand = "/pause" ) // A RuntimeOption is a functional option which alters the Runtime created by @@ -152,6 +157,10 @@ type RuntimeConfig struct { // and all containers and pods will be visible. // The default namespace is "". Namespace string `toml:"namespace,omitempty"` + // PauseImage is the image a pod pause container will use to manage namespaces + PauseImage string `toml:"pause_image"` + // PauseCommand is the command run to start up a pod pause container + PauseCommand string `toml:"pause_command"` } var ( @@ -186,6 +195,8 @@ var ( NoPivotRoot: false, CNIConfigDir: "/etc/cni/net.d/", CNIPluginDir: []string{"/usr/libexec/cni", "/usr/lib/cni", "/opt/cni/bin"}, + PauseCommand: DefaultPauseCommand, + PauseImage: DefaultPauseImage, } ) diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 051b3e85e..1aca559de 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -8,9 +8,12 @@ import ( "strings" "time" + "github.com/containers/storage" + "github.com/containers/storage/pkg/stringid" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "github.com/ulule/deepcopier" ) // CtrRemoveTimeout is the default number of seconds to wait after stopping a container @@ -35,11 +38,37 @@ func (r *Runtime) NewContainer(ctx context.Context, rSpec *spec.Spec, options .. if !r.valid { return nil, ErrRuntimeStopped } + return r.newContainer(ctx, rSpec, options...) +} + +func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ...CtrCreateOption) (c *Container, err error) { + if rSpec == nil { + return nil, errors.Wrapf(ErrInvalidArg, "must provide a valid runtime spec to create container") + } + + ctr := new(Container) + ctr.config = new(ContainerConfig) + ctr.state = new(containerState) + + ctr.config.ID = stringid.GenerateNonCryptoID() + + ctr.config.Spec = new(spec.Spec) + deepcopier.Copy(rSpec).To(ctr.config.Spec) + ctr.config.CreatedTime = time.Now() + + ctr.config.ShmSize = DefaultShmSize + + ctr.state.BindMounts = make(map[string]string) - ctr, err := newContainer(rSpec, r.lockDir) + // Path our lock file will reside at + lockPath := filepath.Join(r.lockDir, ctr.config.ID) + // Grab a lockfile at the given path + lock, err := storage.GetLockfile(lockPath) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "error creating lockfile for new container") } + ctr.lock = lock + ctr.config.StopTimeout = CtrRemoveTimeout // Set namespace based on current runtime namespace @@ -59,6 +88,7 @@ func (r *Runtime) NewContainer(ctx context.Context, rSpec *spec.Spec, options .. ctr.runtime = r var pod *Pod + if ctr.config.Pod != "" { // Get the pod from state pod, err = r.state.Pod(ctr.config.Pod) @@ -194,6 +224,14 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool) // Lock the pod while we're removing container pod.lock.Lock() defer pod.lock.Unlock() + if err := pod.updatePod(); err != nil { + return err + } + + pauseID := pod.state.PauseContainerID + if c.ID() == pauseID { + return errors.Errorf("a pause container cannot be removed without removing pod %s", pod.ID()) + } } c.lock.Lock() diff --git a/libpod/runtime_pod.go b/libpod/runtime_pod.go index 280699b79..19e32d1b0 100644 --- a/libpod/runtime_pod.go +++ b/libpod/runtime_pod.go @@ -26,6 +26,16 @@ type PodFilter func(*Pod) bool // being removed // Otherwise, the pod will not be removed if any containers are running func (r *Runtime) RemovePod(ctx context.Context, p *Pod, removeCtrs, force bool) error { + r.lock.Lock() + defer r.lock.Unlock() + + if !r.valid { + return ErrRuntimeStopped + } + + p.lock.Lock() + defer p.lock.Unlock() + return r.removePod(ctx, p, removeCtrs, force) } diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go index 3592c2fee..eff15be76 100644 --- a/libpod/runtime_pod_linux.go +++ b/libpod/runtime_pod_linux.go @@ -15,7 +15,7 @@ import ( ) // NewPod makes a new, empty pod -func (r *Runtime) NewPod(options ...PodCreateOption) (*Pod, error) { +func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (*Pod, error) { r.lock.Lock() defer r.lock.Unlock() @@ -87,38 +87,42 @@ func (r *Runtime) NewPod(options ...PodCreateOption) (*Pod, error) { if pod.config.UsePodCgroup { logrus.Debugf("Got pod cgroup as %s", pod.state.CgroupPath) } + if pod.HasPauseContainer() != pod.SharesNamespaces() { + return nil, errors.Errorf("Pods must have a pause container to share namespaces") + } if err := r.state.AddPod(pod); err != nil { return nil, errors.Wrapf(err, "error adding pod to state") } + if pod.HasPauseContainer() { + ctr, err := r.createPauseContainer(ctx, pod) + if err != nil { + // Tear down pod, as it is assumed a the pod will contain + // a pause container, and it does not. + if err2 := r.removePod(ctx, pod, true, true); err2 != nil { + logrus.Errorf("Error removing pod after pause container creation failure: %v", err2) + } + return nil, errors.Wrapf(err, "error adding Pause Container") + } + pod.state.PauseContainerID = ctr.ID() + if err := pod.save(); err != nil { + return nil, err + } + } + return pod, nil } func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) error { - r.lock.Lock() - defer r.lock.Unlock() - - if !r.valid { - return ErrRuntimeStopped - } - if !p.valid { if ok, _ := r.state.HasPod(p.ID()); !ok { - // Pod was either already removed, or never existed to - // begin with + // Pod probably already removed + // Or was never in the runtime to begin with return nil } } - p.lock.Lock() - defer p.lock.Unlock() - - // Force a pod update - if err := p.updatePod(); err != nil { - return err - } - ctrs, err := r.state.PodContainers(p) if err != nil { return err @@ -126,6 +130,15 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) numCtrs := len(ctrs) + // If the only container in the pod is the pause container, remove the pod and container unconditionally. + if err := p.updatePod(); err != nil { + return err + } + pauseCtrID := p.state.PauseContainerID + if numCtrs == 1 && ctrs[0].ID() == pauseCtrID { + removeCtrs = true + force = true + } if !removeCtrs && numCtrs > 0 { return errors.Wrapf(ErrCtrExists, "pod %s contains containers and cannot be removed", p.ID()) } diff --git a/libpod/runtime_pod_pause_linux.go b/libpod/runtime_pod_pause_linux.go new file mode 100644 index 000000000..41bf8b041 --- /dev/null +++ b/libpod/runtime_pod_pause_linux.go @@ -0,0 +1,60 @@ +// +build linux + +package libpod + +import ( + "context" + + "github.com/containers/libpod/libpod/image" + "github.com/opencontainers/runtime-tools/generate" +) + +const ( + // IDTruncLength is the length of the pod's id that will be used to make the + // pause container name + IDTruncLength = 12 +) + +func (r *Runtime) makePauseContainer(ctx context.Context, p *Pod, imgName, imgID string) (*Container, error) { + + // Set up generator for pause container defaults + g, err := generate.New("linux") + if err != nil { + return nil, err + } + + g.SetRootReadonly(true) + g.SetProcessArgs([]string{r.config.PauseCommand}) + + containerName := p.ID()[:IDTruncLength] + "-infra" + var options []CtrCreateOption + options = append(options, r.WithPod(p)) + options = append(options, WithRootFSFromImage(imgID, imgName, false)) + options = append(options, WithName(containerName)) + options = append(options, withIsPause()) + + return r.newContainer(ctx, g.Config, options...) +} + +// createPauseContainer wrap creates a pause container for a pod. +// A pause container becomes the basis for kernel namespace sharing between +// containers in the pod. +func (r *Runtime) createPauseContainer(ctx context.Context, p *Pod) (*Container, error) { + if !r.valid { + return nil, ErrRuntimeStopped + } + + newImage, err := r.ImageRuntime().New(ctx, r.config.PauseImage, "", "", nil, nil, image.SigningOptions{}, false, false) + if err != nil { + return nil, err + } + + data, err := newImage.Inspect(ctx) + if err != nil { + return nil, err + } + imageName := newImage.Names()[0] + imageID := data.ID + + return r.makePauseContainer(ctx, p, imageName, imageID) +} diff --git a/libpod/runtime_pod_unsupported.go b/libpod/runtime_pod_unsupported.go index 7cecb7c56..d2629d5ab 100644 --- a/libpod/runtime_pod_unsupported.go +++ b/libpod/runtime_pod_unsupported.go @@ -7,7 +7,7 @@ import ( ) // NewPod makes a new, empty pod -func (r *Runtime) NewPod(options ...PodCreateOption) (*Pod, error) { +func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (*Pod, error) { return nil, ErrOSNotSupported } diff --git a/pkg/inspect/inspect.go b/pkg/inspect/inspect.go index d2c9e79a5..be3818db8 100644 --- a/pkg/inspect/inspect.go +++ b/pkg/inspect/inspect.go @@ -170,6 +170,7 @@ type ContainerInspectData struct { NetworkSettings *NetworkSettings `json:"NetworkSettings"` //TODO ExitCommand []string `json:"ExitCommand"` Namespace string `json:"Namespace"` + IsPause bool `json:"IsPause"` } // ContainerInspectState represents the state of a container. diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go index 6df6fb480..dd1cd5833 100644 --- a/pkg/spec/createconfig.go +++ b/pkg/spec/createconfig.go @@ -364,6 +364,9 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]lib networks := make([]string, 0) userNetworks := c.NetMode.UserDefined() + if IsPod(userNetworks) { + userNetworks = "" + } if userNetworks != "" { for _, netName := range strings.Split(userNetworks, ",") { if netName == "" { @@ -381,6 +384,8 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]lib return nil, errors.Wrapf(err, "container %q not found", c.NetMode.ConnectedContainer()) } options = append(options, libpod.WithNetNSFrom(connectedCtr)) + } else if IsPod(string(c.NetMode)) { + options = append(options, libpod.WithNetNSFromPod()) } else if !c.NetMode.IsHost() && !c.NetMode.IsNone() { isRootless := rootless.IsRootless() postConfigureNetNS := isRootless || (len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0) && !c.UsernsMode.IsHost() @@ -398,6 +403,10 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]lib options = append(options, libpod.WithPIDNSFrom(connectedCtr)) } + if IsPod(string(c.PidMode)) { + options = append(options, libpod.WithPIDNSFromPod()) + } + if c.IpcMode.IsContainer() { connectedCtr, err := c.Runtime.LookupContainer(c.IpcMode.Container()) if err != nil { @@ -406,7 +415,15 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]lib options = append(options, libpod.WithIPCNSFrom(connectedCtr)) } + if IsPod(string(c.IpcMode)) { + options = append(options, libpod.WithIPCNSFromPod()) + } + + if IsPod(string(c.UtsMode)) { + options = append(options, libpod.WithUTSNSFromPod()) + } + // TODO: MNT, USER, CGROUP options = append(options, libpod.WithStopSignal(c.StopSignal)) options = append(options, libpod.WithStopTimeout(c.StopTimeout)) if len(c.DNSSearch) > 0 { diff --git a/pkg/spec/parse.go b/pkg/spec/parse.go index d34e10760..4cdc62de6 100644 --- a/pkg/spec/parse.go +++ b/pkg/spec/parse.go @@ -18,12 +18,29 @@ func (w *weightDevice) String() string { return fmt.Sprintf("%s:%d", w.path, w.weight) } +// LinuxNS is a struct that contains namespace information +// It implemented Valid to show it is a valid namespace +type LinuxNS interface { + Valid() bool +} + // IsNS returns if the specified string has a ns: prefix func IsNS(s string) bool { parts := strings.SplitN(s, ":", 2) return len(parts) > 1 && parts[0] == "ns" } +// IsPod returns if the specified string is pod +func IsPod(s string) bool { + return s == "pod" +} + +// Valid checks the validity of a linux namespace +// s should be the string representation of ns +func Valid(s string, ns LinuxNS) bool { + return IsPod(s) || IsNS(s) || ns.Valid() +} + // NS is the path to the namespace to join. func NS(s string) string { parts := strings.SplitN(s, ":", 2) diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go index 7323b2d2b..8d8a07a2e 100644 --- a/pkg/spec/spec.go +++ b/pkg/spec/spec.go @@ -349,6 +349,9 @@ func addPidNS(config *CreateConfig, g *generate.Generator) error { if pidMode.IsContainer() { logrus.Debug("using container pidmode") } + if IsPod(string(pidMode)) { + logrus.Debug("using pod pidmode") + } return nil } @@ -384,6 +387,9 @@ func addNetNS(config *CreateConfig, g *generate.Generator) error { } else if IsNS(string(netMode)) { logrus.Debug("Using ns netmode") return g.AddOrReplaceLinuxNamespace(spec.NetworkNamespace, NS(string(netMode))) + } else if IsPod(string(netMode)) { + logrus.Debug("Using pod netmode, unless pod is not sharing") + return nil } else if netMode.IsUserDefined() { logrus.Debug("Using user defined netmode") return nil diff --git a/pkg/varlinkapi/pods.go b/pkg/varlinkapi/pods.go index 9e49ab687..6252d815b 100644 --- a/pkg/varlinkapi/pods.go +++ b/pkg/varlinkapi/pods.go @@ -23,7 +23,7 @@ func (i *LibpodAPI) CreatePod(call iopodman.VarlinkCall, create iopodman.PodCrea } options = append(options, libpod.WithPodCgroups()) - pod, err := i.Runtime.NewPod(options...) + pod, err := i.Runtime.NewPod(getContext(), options...) if err != nil { return call.ReplyErrorOccurred(err.Error()) } diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go index 466f79ae9..019ba9377 100644 --- a/test/e2e/libpod_suite_test.go +++ b/test/e2e/libpod_suite_test.go @@ -32,7 +32,7 @@ var ( CGROUP_MANAGER = "systemd" STORAGE_OPTIONS = "--storage-driver vfs" ARTIFACT_DIR = "/tmp/.artifacts" - CACHE_IMAGES = []string{ALPINE, BB, fedoraMinimal, nginx, redis, registry} + CACHE_IMAGES = []string{ALPINE, BB, fedoraMinimal, nginx, redis, registry, pause} RESTORE_IMAGES = []string{ALPINE, BB} ALPINE = "docker.io/library/alpine:latest" BB = "docker.io/library/busybox:latest" @@ -41,6 +41,7 @@ var ( nginx = "quay.io/baude/alpine_nginx:latest" redis = "docker.io/library/redis:alpine" registry = "docker.io/library/registry:2" + pause = "k8s.gcr.io/pause:3.1" defaultWaitTimeout = 90 ) @@ -422,6 +423,18 @@ func (p *PodmanTest) RestoreAllArtifacts() error { return nil } +// CreatePod creates a pod with no pause container +// it optionally takes a pod name +func (p *PodmanTest) CreatePod(name string) (*PodmanSession, int, string) { + var podmanArgs = []string{"pod", "create", "--pause=false", "--share", ""} + if name != "" { + podmanArgs = append(podmanArgs, "--name", name) + } + session := p.Podman(podmanArgs) + session.WaitWithDefaultTimeout() + return session, session.ExitCode(), session.OutputToString() +} + //RunTopContainer runs a simple container in the background that // runs top. If the name passed != "", it will have a name func (p *PodmanTest) RunTopContainer(name string) *PodmanSession { diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index a81751c41..4cf685ce2 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -32,23 +32,20 @@ var _ = Describe("Podman pod create", func() { }) It("podman create pod", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - cid := session.OutputToString() - Expect(session.ExitCode()).To(Equal(0)) + _, ec, podID := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) check := podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc"}) check.WaitWithDefaultTimeout() - match, _ := check.GrepString(cid) + match, _ := check.GrepString(podID) Expect(match).To(BeTrue()) Expect(len(check.OutputToStringArray())).To(Equal(1)) }) It("podman create pod with name", func() { name := "test" - session := podmanTest.Podman([]string{"pod", "create", "--name", name}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + _, ec, _ := podmanTest.CreatePod(name) + Expect(ec).To(Equal(0)) check := podmanTest.Podman([]string{"pod", "ps", "--no-trunc"}) check.WaitWithDefaultTimeout() @@ -58,13 +55,11 @@ var _ = Describe("Podman pod create", func() { It("podman create pod with doubled name", func() { name := "test" - session := podmanTest.Podman([]string{"pod", "create", "--name", name}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + _, ec, _ := podmanTest.CreatePod(name) + Expect(ec).To(Equal(0)) - session = podmanTest.Podman([]string{"pod", "create", "--name", name}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(1))) + _, ec2, _ := podmanTest.CreatePod(name) + Expect(ec2).To(Not(Equal(0))) check := podmanTest.Podman([]string{"pod", "ps", "-q"}) check.WaitWithDefaultTimeout() @@ -77,9 +72,8 @@ var _ = Describe("Podman pod create", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"pod", "create", "--name", name}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(1))) + _, ec, _ := podmanTest.CreatePod(name) + Expect(ec).To(Not(Equal(0))) check := podmanTest.Podman([]string{"pod", "ps", "-q"}) check.WaitWithDefaultTimeout() diff --git a/test/e2e/pod_inspect_test.go b/test/e2e/pod_inspect_test.go index b5c2bd1be..667e59f38 100644 --- a/test/e2e/pod_inspect_test.go +++ b/test/e2e/pod_inspect_test.go @@ -38,12 +38,10 @@ var _ = Describe("Podman pod inspect", func() { }) It("podman inspect a pod", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - podid := session.OutputToString() + _, ec, podid := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("", podid) + session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) diff --git a/test/e2e/pod_kill_test.go b/test/e2e/pod_kill_test.go index 427fb63b3..b29fe1e17 100644 --- a/test/e2e/pod_kill_test.go +++ b/test/e2e/pod_kill_test.go @@ -38,12 +38,10 @@ var _ = Describe("Podman pod kill", func() { }) It("podman pod kill a pod by id", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - podid := session.OutputToString() + _, ec, podid := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("", podid) + session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -58,12 +56,10 @@ var _ = Describe("Podman pod kill", func() { }) It("podman pod kill a pod by id with TERM", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - podid := session.OutputToString() + _, ec, podid := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("", podid) + session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -74,12 +70,10 @@ var _ = Describe("Podman pod kill", func() { }) It("podman pod kill a pod by name", func() { - session := podmanTest.Podman([]string{"pod", "create", "--name", "test1"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - podid := session.OutputToString() + _, ec, podid := podmanTest.CreatePod("test1") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("", podid) + session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -90,12 +84,10 @@ var _ = Describe("Podman pod kill", func() { }) It("podman pod kill a pod by id with a bogus signal", func() { - session := podmanTest.Podman([]string{"pod", "create", "--name", "test1"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - podid := session.OutputToString() + _, ec, podid := podmanTest.CreatePod("test1") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("", podid) + session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -106,19 +98,15 @@ var _ = Describe("Podman pod kill", func() { }) It("podman pod kill latest pod", func() { - session := podmanTest.Podman([]string{"pod", "create", "--name", "test1"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - podid := session.OutputToString() + _, ec, podid := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("", podid) + session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"pod", "create", "--name", "test2"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - podid2 := session.OutputToString() + _, ec, podid2 := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("", podid2) session.WaitWithDefaultTimeout() @@ -135,12 +123,10 @@ var _ = Describe("Podman pod kill", func() { }) It("podman pod kill all", func() { - session := podmanTest.Podman([]string{"pod", "create", "--name", "test1"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - podid := session.OutputToString() + _, ec, podid := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("", podid) + session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -148,10 +134,8 @@ var _ = Describe("Podman pod kill", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"pod", "create", "--name", "test2"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - podid2 := session.OutputToString() + _, ec, podid2 := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("", podid2) session.WaitWithDefaultTimeout() @@ -159,6 +143,7 @@ var _ = Describe("Podman pod kill", func() { result := podmanTest.Podman([]string{"pod", "kill", "-a"}) result.WaitWithDefaultTimeout() + fmt.Println(result.OutputToString(), result.ErrorToString()) Expect(result.ExitCode()).To(Equal(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) diff --git a/test/e2e/pod_pause_container_test.go b/test/e2e/pod_pause_container_test.go new file mode 100644 index 000000000..09a1c18d0 --- /dev/null +++ b/test/e2e/pod_pause_container_test.go @@ -0,0 +1,285 @@ +package integration + +import ( + "fmt" + "os" + "strconv" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Podman pod create", func() { + var ( + tempdir string + err error + podmanTest PodmanTest + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanCreate(tempdir) + podmanTest.RestoreAllArtifacts() + podmanTest.RestoreArtifact(pause) + }) + + AfterEach(func() { + podmanTest.CleanupPod() + f := CurrentGinkgoTestDescription() + timedResult := fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds()) + GinkgoWriter.Write([]byte(timedResult)) + }) + + It("podman create pause container", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + podID := session.OutputToString() + + check := podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc"}) + check.WaitWithDefaultTimeout() + match, _ := check.GrepString(podID) + Expect(match).To(BeTrue()) + Expect(len(check.OutputToStringArray())).To(Equal(1)) + + check = podmanTest.Podman([]string{"ps", "-qa", "--no-trunc"}) + check.WaitWithDefaultTimeout() + Expect(len(check.OutputToStringArray())).To(Equal(1)) + }) + + It("podman start pause container", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + podID := session.OutputToString() + + session = podmanTest.Podman([]string{"pod", "start", podID}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + check := podmanTest.Podman([]string{"ps", "-qa", "--no-trunc", "--filter", "status=running"}) + check.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(check.OutputToStringArray())).To(Equal(1)) + }) + + It("podman pause container namespaces", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + podID := session.OutputToString() + + session = podmanTest.Podman([]string{"pod", "start", podID}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.RunTopContainerInPod("", podID) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + check := podmanTest.Podman([]string{"ps", "-a", "--no-trunc", "--ns", "--format", "{{.IPC}} {{.NET}}"}) + check.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(check.OutputToStringArray())).To(Equal(2)) + Expect(check.OutputToStringArray()[0]).To(Equal(check.OutputToStringArray()[1])) + + }) + + It("podman pod correctly sets up NetNS", func() { + session := podmanTest.Podman([]string{"pod", "create", "--share", "net"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + podID := session.OutputToString() + + session = podmanTest.Podman([]string{"pod", "start", podID}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + podmanTest.RestoreArtifact(nginx) + session = podmanTest.Podman([]string{"run", "-d", "--pod", podID, nginx}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + podmanTest.RestoreArtifact(fedoraMinimal) + session = podmanTest.Podman([]string{"run", "--pod", podID, fedoraMinimal, "curl", "localhost:80"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"run", fedoraMinimal, "curl", "localhost"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Not(Equal(0))) + }) + + It("podman pod correctly sets up PIDNS", func() { + session := podmanTest.Podman([]string{"pod", "create", "--share", "pid", "--name", "test-pod"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + podID := session.OutputToString() + + session = podmanTest.Podman([]string{"pod", "start", podID}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.RunTopContainerInPod("test-ctr", podID) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + check := podmanTest.Podman([]string{"top", "test-ctr", "pid"}) + check.WaitWithDefaultTimeout() + Expect(check.ExitCode()).To(Equal(0)) + PIDs := check.OutputToStringArray() + Expect(len(PIDs)).To(Equal(4)) + + ctrPID, _ := strconv.Atoi(PIDs[1]) + pausePID, _ := strconv.Atoi(PIDs[2]) + Expect(ctrPID).To(BeNumerically("<", pausePID)) + }) + + It("podman pod doesn't share PIDNS if requested to not", func() { + session := podmanTest.Podman([]string{"pod", "create", "--share", "net", "--name", "test-pod"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + podID := session.OutputToString() + + session = podmanTest.Podman([]string{"pod", "start", podID}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.RunTopContainerInPod("test-ctr", podID) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + check := podmanTest.Podman([]string{"top", "test-ctr", "pid"}) + check.WaitWithDefaultTimeout() + Expect(check.ExitCode()).To(Equal(0)) + ctrTop := check.OutputToStringArray() + + check = podmanTest.Podman([]string{"top", podID[:12] + "-infra", "pid"}) + check.WaitWithDefaultTimeout() + Expect(check.ExitCode()).To(Equal(0)) + pauseTop := check.OutputToStringArray() + + ctrPID, _ := strconv.Atoi(ctrTop[1]) + pausePID, _ := strconv.Atoi(pauseTop[1]) + Expect(ctrPID).To(Equal(pausePID)) + }) + + It("podman pod container can override pod net NS", func() { + session := podmanTest.Podman([]string{"pod", "create", "--share", "net"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + podID := session.OutputToString() + + session = podmanTest.Podman([]string{"pod", "start", podID}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + podmanTest.RestoreArtifact(nginx) + session = podmanTest.Podman([]string{"run", "-d", "--pod", podID, nginx}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + podmanTest.RestoreArtifact(fedoraMinimal) + session = podmanTest.Podman([]string{"run", "--pod", podID, "--network", "bridge", fedoraMinimal, "curl", "localhost"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Not(Equal(0))) + }) + + It("podman pod container can override pod pid NS", func() { + session := podmanTest.Podman([]string{"pod", "create", "--share", "pid"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + podID := session.OutputToString() + + session = podmanTest.Podman([]string{"pod", "start", podID}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"run", "--pod", podID, "--pid", "host", "-d", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + check := podmanTest.Podman([]string{"ps", "-a", "--ns", "--format", "{{.PIDNS}}"}) + check.WaitWithDefaultTimeout() + Expect(check.ExitCode()).To(Equal(0)) + outputArray := check.OutputToStringArray() + Expect(len(outputArray)).To(Equal(2)) + + PID1 := outputArray[0] + PID2 := outputArray[1] + Expect(PID1).To(Not(Equal(PID2))) + }) + + It("podman pod container can override pod not sharing pid", func() { + session := podmanTest.Podman([]string{"pod", "create", "--share", "net"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + podID := session.OutputToString() + + session = podmanTest.Podman([]string{"pod", "start", podID}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"run", "--pod", podID, "--pid", "pod", "-d", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + check := podmanTest.Podman([]string{"ps", "-a", "--ns", "--format", "{{.PIDNS}}"}) + check.WaitWithDefaultTimeout() + Expect(check.ExitCode()).To(Equal(0)) + outputArray := check.OutputToStringArray() + Expect(len(outputArray)).To(Equal(2)) + + PID1 := outputArray[0] + PID2 := outputArray[1] + Expect(PID1).To(Equal(PID2)) + }) + + It("podman pod container can override pod ipc NS", func() { + session := podmanTest.Podman([]string{"pod", "create", "--share", "ipc"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + podID := session.OutputToString() + + session = podmanTest.Podman([]string{"pod", "start", podID}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"run", "--pod", podID, "--ipc", "host", "-d", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + check := podmanTest.Podman([]string{"ps", "-a", "--ns", "--format", "{{.IPC}}"}) + check.WaitWithDefaultTimeout() + Expect(check.ExitCode()).To(Equal(0)) + outputArray := check.OutputToStringArray() + Expect(len(outputArray)).To(Equal(2)) + + PID1 := outputArray[0] + PID2 := outputArray[1] + Expect(PID1).To(Not(Equal(PID2))) + }) + + It("podman pod pause container deletion", func() { + session := podmanTest.Podman([]string{"pod", "create", "--share", "ipc"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + podID := session.OutputToString() + + session = podmanTest.Podman([]string{"ps", "-aq"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + pauseID := session.OutputToString() + + session = podmanTest.Podman([]string{"rm", pauseID}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Not(Equal(0))) + + session = podmanTest.Podman([]string{"pod", "rm", podID}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) +}) diff --git a/test/e2e/pod_pause_test.go b/test/e2e/pod_pause_test.go index 6a5afe73b..384cbfcb7 100644 --- a/test/e2e/pod_pause_test.go +++ b/test/e2e/pod_pause_test.go @@ -46,10 +46,8 @@ var _ = Describe("Podman pod pause", func() { }) It("podman pod pause a created pod by id", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - podid := session.OutputToString() + _, ec, podid := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"pod", "pause", podid}) result.WaitWithDefaultTimeout() @@ -57,12 +55,10 @@ var _ = Describe("Podman pod pause", func() { }) It("podman pod pause a running pod by id", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - podid := session.OutputToString() + _, ec, podid := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("", podid) + session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -80,12 +76,10 @@ var _ = Describe("Podman pod pause", func() { }) It("podman unpause a running pod by id", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - podid := session.OutputToString() + _, ec, podid := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("", podid) + session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -97,11 +91,10 @@ var _ = Describe("Podman pod pause", func() { }) It("podman pod pause a running pod by name", func() { - session := podmanTest.Podman([]string{"pod", "create", "--name", "test1"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + _, ec, _ := podmanTest.CreatePod("test1") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("", "test1") + session := podmanTest.RunTopContainerInPod("", "test1") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) diff --git a/test/e2e/pod_ps_test.go b/test/e2e/pod_ps_test.go index 4e2995917..b48cb9578 100644 --- a/test/e2e/pod_ps_test.go +++ b/test/e2e/pod_ps_test.go @@ -39,12 +39,10 @@ var _ = Describe("Podman ps", func() { }) It("podman pod ps default", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + _, ec, podid := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - podid := session.OutputToString() - session = podmanTest.RunTopContainerInPod("", podid) + session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -55,12 +53,10 @@ var _ = Describe("Podman ps", func() { }) It("podman pod ps quiet flag", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + _, ec, podid := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - podid := session.OutputToString() - _, ec, _ := podmanTest.RunLsContainerInPod("", podid) + _, ec, _ = podmanTest.RunLsContainerInPod("", podid) Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"pod", "ps", "-q"}) @@ -71,14 +67,12 @@ var _ = Describe("Podman ps", func() { }) It("podman pod ps no-trunc", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - - podid := session.OutputToString() - _, ec, _ := podmanTest.RunLsContainerInPod("", podid) + _, ec, podid := podmanTest.CreatePod("") Expect(ec).To(Equal(0)) + _, ec2, _ := podmanTest.RunLsContainerInPod("", podid) + Expect(ec2).To(Equal(0)) + result := podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc"}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) @@ -87,17 +81,11 @@ var _ = Describe("Podman ps", func() { }) It("podman pod ps latest", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - - podid1 := session.OutputToString() - - session = podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + _, ec, podid1 := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - podid2 := session.OutputToString() + _, ec2, podid2 := podmanTest.CreatePod("") + Expect(ec2).To(Equal(0)) result := podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--latest"}) result.WaitWithDefaultTimeout() @@ -106,11 +94,10 @@ var _ = Describe("Podman ps", func() { Expect(result.OutputToString()).To(Not(ContainSubstring(podid1))) }) It("podman pod ps id filter flag", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + _, ec, podid := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - result := podmanTest.Podman([]string{"pod", "ps", "--filter", fmt.Sprintf("id=%s", session.OutputToString())}) + result := podmanTest.Podman([]string{"pod", "ps", "--filter", fmt.Sprintf("id=%s", podid)}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) }) @@ -123,19 +110,16 @@ var _ = Describe("Podman ps", func() { }) It("podman pod ps --sort by name", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + _, ec, _ := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - session = podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + _, ec2, _ := podmanTest.CreatePod("") + Expect(ec2).To(Equal(0)) - session = podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + _, ec3, _ := podmanTest.CreatePod("") + Expect(ec3).To(Equal(0)) - session = podmanTest.Podman([]string{"pod", "ps", "--sort=name", "--format", "{{.Name}}"}) + session := podmanTest.Podman([]string{"pod", "ps", "--sort=name", "--format", "{{.Name}}"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -146,16 +130,14 @@ var _ = Describe("Podman ps", func() { }) It("podman pod ps --ctr-names", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - podid := session.OutputToString() + _, ec, podid := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("test1", podid) + session := podmanTest.RunTopContainerInPod("test1", podid) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - _, ec, _ := podmanTest.RunLsContainerInPod("test2", podid) + _, ec, _ = podmanTest.RunLsContainerInPod("test2", podid) Expect(ec).To(Equal(0)) session = podmanTest.Podman([]string{"pod", "ps", "--format={{.ContainerInfo}}", "--ctr-names"}) @@ -166,22 +148,18 @@ var _ = Describe("Podman ps", func() { }) It("podman pod ps filter ctr attributes", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - podid1 := session.OutputToString() + _, ec, podid1 := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("test1", podid1) + session := podmanTest.RunTopContainerInPod("test1", podid1) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - podid2 := session.OutputToString() + _, ec2, podid2 := podmanTest.CreatePod("") + Expect(ec2).To(Equal(0)) - _, ec, cid := podmanTest.RunLsContainerInPod("test2", podid2) - Expect(ec).To(Equal(0)) + _, ec3, cid := podmanTest.RunLsContainerInPod("test2", podid2) + Expect(ec3).To(Equal(0)) session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-names=test1"}) session.WaitWithDefaultTimeout() @@ -195,10 +173,8 @@ var _ = Describe("Podman ps", func() { Expect(session.OutputToString()).To(ContainSubstring(podid2)) Expect(session.OutputToString()).To(Not(ContainSubstring(podid1))) - session = podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - podid3 := session.OutputToString() + _, ec3, podid3 := podmanTest.CreatePod("") + Expect(ec3).To(Equal(0)) session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-number=1"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/pod_restart_test.go b/test/e2e/pod_restart_test.go index 86132c04f..e486f8791 100644 --- a/test/e2e/pod_restart_test.go +++ b/test/e2e/pod_restart_test.go @@ -38,22 +38,19 @@ var _ = Describe("Podman pod restart", func() { }) It("podman pod restart single empty pod", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - cid := session.OutputToString() + _, ec, podid := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - session = podmanTest.Podman([]string{"pod", "restart", cid}) + session := podmanTest.Podman([]string{"pod", "restart", podid}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(125)) }) It("podman pod restart single pod by name", func() { - session := podmanTest.Podman([]string{"pod", "create", "--name", "foobar99"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + _, ec, _ := podmanTest.CreatePod("foobar99") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("test1", "foobar99") + session := podmanTest.RunTopContainerInPod("test1", "foobar99") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -70,15 +67,15 @@ var _ = Describe("Podman pod restart", func() { }) It("podman pod restart multiple pods", func() { - session := podmanTest.Podman([]string{"pod", "create", "--name", "foobar99"}) - session.WaitWithDefaultTimeout() + _, ec, _ := podmanTest.CreatePod("foobar99") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("test1", "foobar99") + session := podmanTest.RunTopContainerInPod("test1", "foobar99") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session2 := podmanTest.Podman([]string{"pod", "create", "--name", "foobar100"}) - session2.WaitWithDefaultTimeout() + _, ec, _ = podmanTest.CreatePod("foobar100") + Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("test2", "foobar100") session.WaitWithDefaultTimeout() @@ -108,15 +105,15 @@ var _ = Describe("Podman pod restart", func() { }) It("podman pod restart all pods", func() { - session := podmanTest.Podman([]string{"pod", "create", "--name", "foobar99"}) - session.WaitWithDefaultTimeout() + _, ec, _ := podmanTest.CreatePod("foobar99") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("test1", "foobar99") + session := podmanTest.RunTopContainerInPod("test1", "foobar99") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session2 := podmanTest.Podman([]string{"pod", "create", "--name", "foobar100"}) - session2.WaitWithDefaultTimeout() + _, ec, _ = podmanTest.CreatePod("foobar100") + Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("test2", "foobar100") session.WaitWithDefaultTimeout() @@ -136,15 +133,15 @@ var _ = Describe("Podman pod restart", func() { }) It("podman pod restart latest pod", func() { - session := podmanTest.Podman([]string{"pod", "create", "--name", "foobar99"}) - session.WaitWithDefaultTimeout() + _, ec, _ := podmanTest.CreatePod("foobar99") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("test1", "foobar99") + session := podmanTest.RunTopContainerInPod("test1", "foobar99") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session2 := podmanTest.Podman([]string{"pod", "create", "--name", "foobar100"}) - session2.WaitWithDefaultTimeout() + _, ec, _ = podmanTest.CreatePod("foobar100") + Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("test2", "foobar100") session.WaitWithDefaultTimeout() @@ -164,15 +161,14 @@ var _ = Describe("Podman pod restart", func() { }) It("podman pod restart multiple pods with bogus", func() { - session := podmanTest.Podman([]string{"pod", "create", "--name", "foobar99"}) - session.WaitWithDefaultTimeout() - cid1 := session.OutputToString() + _, ec, podid1 := podmanTest.CreatePod("foobar99") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("", "foobar99") + session := podmanTest.RunTopContainerInPod("", "foobar99") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"pod", "restart", cid1, "doesnotexist"}) + session = podmanTest.Podman([]string{"pod", "restart", podid1, "doesnotexist"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(125)) }) diff --git a/test/e2e/pod_rm_test.go b/test/e2e/pod_rm_test.go index 1ce0d18c7..09002e954 100644 --- a/test/e2e/pod_rm_test.go +++ b/test/e2e/pod_rm_test.go @@ -32,9 +32,8 @@ var _ = Describe("Podman pod rm", func() { }) It("podman pod rm empty pod", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - podid := session.OutputToString() + _, ec, podid := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"pod", "rm", podid}) result.WaitWithDefaultTimeout() @@ -42,13 +41,11 @@ var _ = Describe("Podman pod rm", func() { }) It("podman pod rm latest pod", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - podid := session.OutputToString() + _, ec, podid := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - session = podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - podid2 := session.OutputToString() + _, ec2, podid2 := podmanTest.CreatePod("") + Expect(ec2).To(Equal(0)) result := podmanTest.Podman([]string{"pod", "rm", "--latest"}) result.WaitWithDefaultTimeout() @@ -62,14 +59,11 @@ var _ = Describe("Podman pod rm", func() { }) It("podman pod rm doesn't remove a pod with a container", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - - podid := session.OutputToString() + _, ec, podid := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - session = podmanTest.Podman([]string{"create", "--pod", podid, ALPINE, "ls"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + _, ec2, _ := podmanTest.RunLsContainerInPod("", podid) + Expect(ec2).To(Equal(0)) result := podmanTest.Podman([]string{"pod", "rm", podid}) result.WaitWithDefaultTimeout() @@ -81,12 +75,10 @@ var _ = Describe("Podman pod rm", func() { }) It("podman pod rm -f does remove a running container", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() + _, ec, podid := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - podid := session.OutputToString() - - session = podmanTest.Podman([]string{"run", "-d", "--pod", podid, ALPINE, "top"}) + session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -100,15 +92,13 @@ var _ = Describe("Podman pod rm", func() { }) It("podman pod rm -a doesn't remove a running container", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() + _, ec, podid1 := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - podid1 := session.OutputToString() + _, ec, _ = podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - session = podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - - session = podmanTest.Podman([]string{"run", "-d", "--pod", podid1, ALPINE, "top"}) + session := podmanTest.RunTopContainerInPod("", podid1) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -127,20 +117,16 @@ var _ = Describe("Podman pod rm", func() { }) It("podman pod rm -fa removes everything", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - - podid1 := session.OutputToString() - - session = podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() + _, ec, podid1 := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - podid2 := session.OutputToString() + _, ec, podid2 := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - session = podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() + _, ec, _ = podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - session = podmanTest.Podman([]string{"run", "-d", "--pod", podid1, ALPINE, "top"}) + session := podmanTest.RunTopContainerInPod("", podid1) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -148,12 +134,12 @@ var _ = Describe("Podman pod rm", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"run", "-d", "--pod", podid2, ALPINE, "ls"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + _, ec, _ = podmanTest.RunLsContainerInPod("", podid2) + Expect(ec).To(Equal(0)) - session = podmanTest.Podman([]string{"run", "-d", "--pod", podid2, ALPINE, "top"}) + session = podmanTest.RunTopContainerInPod("", podid2) session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) result := podmanTest.Podman([]string{"pod", "rm", "-fa"}) result.WaitWithDefaultTimeout() diff --git a/test/e2e/pod_start_test.go b/test/e2e/pod_start_test.go index 17296c4b3..9d2ea9b26 100644 --- a/test/e2e/pod_start_test.go +++ b/test/e2e/pod_start_test.go @@ -38,22 +38,19 @@ var _ = Describe("Podman pod start", func() { }) It("podman pod start single empty pod", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - cid := session.OutputToString() + _, ec, podid := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - session = podmanTest.Podman([]string{"pod", "start", cid}) + session := podmanTest.Podman([]string{"pod", "start", podid}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(125)) }) It("podman pod start single pod by name", func() { - session := podmanTest.Podman([]string{"pod", "create", "--name", "foobar99"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + _, ec, _ := podmanTest.CreatePod("foobar99") + Expect(ec).To(Equal(0)) - session = podmanTest.Podman([]string{"create", "--pod", "foobar99", ALPINE, "ls"}) + session := podmanTest.Podman([]string{"create", "--pod", "foobar99", ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -63,38 +60,36 @@ var _ = Describe("Podman pod start", func() { }) It("podman pod start multiple pods", func() { - session := podmanTest.Podman([]string{"pod", "create", "--name", "foobar99"}) - session.WaitWithDefaultTimeout() - cid1 := session.OutputToString() + _, ec, podid1 := podmanTest.CreatePod("foobar99") + Expect(ec).To(Equal(0)) - session = podmanTest.Podman([]string{"create", "--pod", "foobar99", ALPINE, "top"}) + session := podmanTest.Podman([]string{"create", "--pod", "foobar99", ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session2 := podmanTest.Podman([]string{"pod", "create", "--name", "foobar100"}) - session2.WaitWithDefaultTimeout() - cid2 := session2.OutputToString() + _, ec2, podid2 := podmanTest.CreatePod("foobar100") + Expect(ec2).To(Equal(0)) session = podmanTest.Podman([]string{"create", "--pod", "foobar100", ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"pod", "start", cid1, cid2}) + session = podmanTest.Podman([]string{"pod", "start", podid1, podid2}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) }) It("podman pod start all pods", func() { - session := podmanTest.Podman([]string{"pod", "create", "--name", "foobar99"}) - session.WaitWithDefaultTimeout() + _, ec, _ := podmanTest.CreatePod("foobar99") + Expect(ec).To(Equal(0)) - session = podmanTest.Podman([]string{"create", "--pod", "foobar99", ALPINE, "top"}) + session := podmanTest.Podman([]string{"create", "--pod", "foobar99", ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session2 := podmanTest.Podman([]string{"pod", "create", "--name", "foobar100"}) - session2.WaitWithDefaultTimeout() + _, ec, _ = podmanTest.CreatePod("foobar100") + Expect(ec).To(Equal(0)) session = podmanTest.Podman([]string{"create", "--pod", "foobar100", ALPINE, "top"}) session.WaitWithDefaultTimeout() @@ -107,15 +102,15 @@ var _ = Describe("Podman pod start", func() { }) It("podman pod start latest pod", func() { - session := podmanTest.Podman([]string{"pod", "create", "--name", "foobar99"}) - session.WaitWithDefaultTimeout() + _, ec, _ := podmanTest.CreatePod("foobar99") + Expect(ec).To(Equal(0)) - session = podmanTest.Podman([]string{"create", "--pod", "foobar99", ALPINE, "top"}) + session := podmanTest.Podman([]string{"create", "--pod", "foobar99", ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session2 := podmanTest.Podman([]string{"pod", "create", "--name", "foobar100"}) - session2.WaitWithDefaultTimeout() + _, ec, _ = podmanTest.CreatePod("foobar100") + Expect(ec).To(Equal(0)) session = podmanTest.Podman([]string{"create", "--pod", "foobar100", ALPINE, "top"}) session.WaitWithDefaultTimeout() @@ -128,15 +123,14 @@ var _ = Describe("Podman pod start", func() { }) It("podman pod start multiple pods with bogus", func() { - session := podmanTest.Podman([]string{"pod", "create", "--name", "foobar99"}) - session.WaitWithDefaultTimeout() - cid1 := session.OutputToString() + _, ec, podid := podmanTest.CreatePod("foobar99") + Expect(ec).To(Equal(0)) - session = podmanTest.Podman([]string{"create", "--pod", "foobar99", ALPINE, "top"}) + session := podmanTest.Podman([]string{"create", "--pod", "foobar99", ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"pod", "start", cid1, "doesnotexist"}) + session = podmanTest.Podman([]string{"pod", "start", podid, "doesnotexist"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(125)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) diff --git a/test/e2e/pod_stop_test.go b/test/e2e/pod_stop_test.go index 0eb41b696..32f8559ad 100644 --- a/test/e2e/pod_stop_test.go +++ b/test/e2e/pod_stop_test.go @@ -38,22 +38,19 @@ var _ = Describe("Podman pod stop", func() { }) It("podman pod stop single empty pod", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - cid := session.OutputToString() + _, ec, podid := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - session = podmanTest.Podman([]string{"pod", "stop", cid}) + session := podmanTest.Podman([]string{"pod", "stop", podid}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) }) It("podman pod stop single pod by name", func() { - session := podmanTest.Podman([]string{"pod", "create", "--name", "foobar99"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + _, ec, _ := podmanTest.CreatePod("foobar99") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("", "foobar99") + session := podmanTest.RunTopContainerInPod("", "foobar99") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -64,38 +61,36 @@ var _ = Describe("Podman pod stop", func() { }) It("podman pod stop multiple pods", func() { - session := podmanTest.Podman([]string{"pod", "create", "--name", "foobar99"}) - session.WaitWithDefaultTimeout() - cid1 := session.OutputToString() + _, ec, podid1 := podmanTest.CreatePod("foobar99") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("", "foobar99") + session := podmanTest.RunTopContainerInPod("", "foobar99") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session2 := podmanTest.Podman([]string{"pod", "create", "--name", "foobar100"}) - session2.WaitWithDefaultTimeout() - cid2 := session2.OutputToString() + _, ec2, podid2 := podmanTest.CreatePod("foobar100") + Expect(ec2).To(Equal(0)) session = podmanTest.RunTopContainerInPod("", "foobar100") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"pod", "stop", cid1, cid2}) + session = podmanTest.Podman([]string{"pod", "stop", podid1, podid2}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) It("podman pod stop all pods", func() { - session := podmanTest.Podman([]string{"pod", "create", "--name", "foobar99"}) - session.WaitWithDefaultTimeout() + _, ec, _ := podmanTest.CreatePod("foobar99") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("", "foobar99") + session := podmanTest.RunTopContainerInPod("", "foobar99") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session2 := podmanTest.Podman([]string{"pod", "create", "--name", "foobar100"}) - session2.WaitWithDefaultTimeout() + _, ec, _ = podmanTest.CreatePod("foobar100") + Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("", "foobar100") session.WaitWithDefaultTimeout() @@ -108,15 +103,15 @@ var _ = Describe("Podman pod stop", func() { }) It("podman pod stop latest pod", func() { - session := podmanTest.Podman([]string{"pod", "create", "--name", "foobar99"}) - session.WaitWithDefaultTimeout() + _, ec, _ := podmanTest.CreatePod("foobar99") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("", "foobar99") + session := podmanTest.RunTopContainerInPod("", "foobar99") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session2 := podmanTest.Podman([]string{"pod", "create", "--name", "foobar100"}) - session2.WaitWithDefaultTimeout() + _, ec, _ = podmanTest.CreatePod("foobar100") + Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("", "foobar100") session.WaitWithDefaultTimeout() @@ -129,15 +124,14 @@ var _ = Describe("Podman pod stop", func() { }) It("podman pod stop multiple pods with bogus", func() { - session := podmanTest.Podman([]string{"pod", "create", "--name", "foobar99"}) - session.WaitWithDefaultTimeout() - cid1 := session.OutputToString() + _, ec, podid1 := podmanTest.CreatePod("foobar99") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("", "foobar99") + session := podmanTest.RunTopContainerInPod("", "foobar99") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"pod", "stop", cid1, "doesnotexist"}) + session = podmanTest.Podman([]string{"pod", "stop", podid1, "doesnotexist"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(125)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index 5dfb385a8..f5d79193b 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -248,12 +248,10 @@ var _ = Describe("Podman ps", func() { }) It("podman --pod", func() { - session := podmanTest.Podman([]string{"pod", "create"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - podid := session.OutputToString() + _, ec, podid := podmanTest.CreatePod("") + Expect(ec).To(Equal(0)) - session = podmanTest.RunTopContainerInPod("", podid) + session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) |