diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container.go | 22 | ||||
-rw-r--r-- | libpod/container_ffjson.go | 485 | ||||
-rw-r--r-- | libpod/container_inspect.go | 1 | ||||
-rw-r--r-- | libpod/container_internal.go | 35 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 49 | ||||
-rw-r--r-- | libpod/options.go | 280 | ||||
-rw-r--r-- | libpod/pod.go | 95 | ||||
-rw-r--r-- | libpod/pod_api.go | 7 | ||||
-rw-r--r-- | libpod/pod_ffjson.go | 745 | ||||
-rw-r--r-- | libpod/pod_internal.go | 1 | ||||
-rw-r--r-- | libpod/runtime.go | 11 | ||||
-rw-r--r-- | libpod/runtime_ctr.go | 42 | ||||
-rw-r--r-- | libpod/runtime_pod.go | 10 | ||||
-rw-r--r-- | libpod/runtime_pod_linux.go | 49 | ||||
-rw-r--r-- | libpod/runtime_pod_pause_linux.go | 60 | ||||
-rw-r--r-- | libpod/runtime_pod_unsupported.go | 2 |
16 files changed, 1828 insertions, 66 deletions
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 } |