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 /cmd/podman | |
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
Diffstat (limited to 'cmd/podman')
-rw-r--r-- | cmd/podman/create.go | 74 | ||||
-rw-r--r-- | cmd/podman/libpodruntime/runtime.go | 9 | ||||
-rw-r--r-- | cmd/podman/pod_create.go | 57 | ||||
-rw-r--r-- | cmd/podman/pod_ps.go | 57 | ||||
-rw-r--r-- | cmd/podman/ps.go | 16 |
5 files changed, 176 insertions, 37 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) |