diff --git a/delete.go b/delete.go index 746b0df547f..dd3041f8722 100644 --- a/delete.go +++ b/delete.go @@ -13,7 +13,7 @@ import ( "golang.org/x/sys/unix" ) -func killContainer(container libcontainer.Container) error { +func killContainer(container *libcontainer.Container) error { _ = container.Signal(unix.SIGKILL, false) for i := 0; i < 100; i++ { time.Sleep(100 * time.Millisecond) diff --git a/libcontainer/container.go b/libcontainer/container.go index c23281fb7ff..c4aa99ecf5f 100644 --- a/libcontainer/container.go +++ b/libcontainer/container.go @@ -5,11 +5,9 @@ package libcontainer import ( - "os" "time" "github.com/opencontainers/runc/libcontainer/configs" - "github.com/opencontainers/runtime-spec/specs-go" ) // Status is the status of a container. @@ -59,68 +57,3 @@ type BaseState struct { // Config is the container's configuration. Config configs.Config `json:"config"` } - -// BaseContainer is a libcontainer container object. -// -// Each container is thread-safe within the same process. Since a container can -// be destroyed by a separate process, any function may return that the container -// was not found. BaseContainer includes methods that are platform agnostic. -type BaseContainer interface { - // Returns the ID of the container - ID() string - - // Returns the current status of the container. - Status() (Status, error) - - // State returns the current container's state information. - State() (*State, error) - - // OCIState returns the current container's state information. - OCIState() (*specs.State, error) - - // Returns the current config of the container. - Config() configs.Config - - // Returns the PIDs inside this container. The PIDs are in the namespace of the calling process. - // - // Some of the returned PIDs may no longer refer to processes in the Container, unless - // the Container state is PAUSED in which case every PID in the slice is valid. - Processes() ([]int, error) - - // Returns statistics for the container. - Stats() (*Stats, error) - - // Set resources of container as configured - // - // We can use this to change resources when containers are running. - // - Set(config configs.Config) error - - // Start a process inside the container. Returns error if process fails to - // start. You can track process lifecycle with passed Process structure. - Start(process *Process) (err error) - - // Run immediately starts the process inside the container. Returns error if process - // fails to start. It does not block waiting for the exec fifo after start returns but - // opens the fifo after start returns. - Run(process *Process) (err error) - - // Destroys the container, if its in a valid state, after killing any - // remaining running processes. - // - // Any event registrations are removed before the container is destroyed. - // No error is returned if the container is already destroyed. - // - // Running containers must first be stopped using Signal(..). - // Paused containers must first be resumed using Resume(..). - Destroy() error - - // Signal sends the provided signal code to the container's initial process. - // - // If all is specified the signal is sent to all processes in the container - // including the initial process. - Signal(s os.Signal, all bool) error - - // Exec signals the container to exec the users process at the end of the init. - Exec() error -} diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go index d50c0c2e5e1..a2ceb7857db 100644 --- a/libcontainer/container_linux.go +++ b/libcontainer/container_linux.go @@ -36,7 +36,8 @@ import ( const stdioFdCount = 3 -type linuxContainer struct { +// Container is a libcontainer container object. +type Container struct { id string root string config *configs.Config @@ -80,69 +81,44 @@ type State struct { IntelRdtPath string `json:"intel_rdt_path"` } -// Container is a libcontainer container object. -// -// Each container is thread-safe within the same process. Since a container can -// be destroyed by a separate process, any function may return that the container -// was not found. -type Container interface { - BaseContainer - - // Methods below here are platform specific - - // Checkpoint checkpoints the running container's state to disk using the criu(8) utility. - Checkpoint(criuOpts *CriuOpts) error - - // Restore restores the checkpointed container to a running state using the criu(8) utility. - Restore(process *Process, criuOpts *CriuOpts) error - - // If the Container state is RUNNING or CREATED, sets the Container state to PAUSED and pauses - // the execution of any user processes. Asynchronously, when the container finished being paused the - // state is changed to PAUSED. - // If the Container state is PAUSED, do nothing. - Pause() error - - // If the Container state is PAUSED, resumes the execution of any user processes in the - // Container before setting the Container state to RUNNING. - // If the Container state is RUNNING, do nothing. - Resume() error - - // NotifyOOM returns a read-only channel signaling when the container receives an OOM notification. - NotifyOOM() (<-chan struct{}, error) - - // NotifyMemoryPressure returns a read-only channel signaling when the container reaches a given pressure level - NotifyMemoryPressure(level PressureLevel) (<-chan struct{}, error) -} - // ID returns the container's unique ID -func (c *linuxContainer) ID() string { +func (c *Container) ID() string { return c.id } // Config returns the container's configuration -func (c *linuxContainer) Config() configs.Config { +func (c *Container) Config() configs.Config { return *c.config } -func (c *linuxContainer) Status() (Status, error) { +// Status returns the current status of the container. +func (c *Container) Status() (Status, error) { c.m.Lock() defer c.m.Unlock() return c.currentStatus() } -func (c *linuxContainer) State() (*State, error) { +// State returns the current container's state information. +func (c *Container) State() (*State, error) { c.m.Lock() defer c.m.Unlock() return c.currentState() } -func (c *linuxContainer) OCIState() (*specs.State, error) { +// OCIState returns the current container's state information. +func (c *Container) OCIState() (*specs.State, error) { c.m.Lock() defer c.m.Unlock() return c.currentOCIState() } -func (c *linuxContainer) Processes() ([]int, error) { +// Processes returns the PIDs inside this container. The PIDs are in the +// namespace of the calling process. +// +// Some of the returned PIDs may no longer refer to processes in the container, +// unless the container state is PAUSED in which case every PID in the slice is +// valid. +func (c *Container) Processes() ([]int, error) { var pids []int status, err := c.currentStatus() if err != nil { @@ -160,7 +136,8 @@ func (c *linuxContainer) Processes() ([]int, error) { return pids, nil } -func (c *linuxContainer) Stats() (*Stats, error) { +// Stats returns statistics for the container. +func (c *Container) Stats() (*Stats, error) { var ( err error stats = &Stats{} @@ -186,7 +163,9 @@ func (c *linuxContainer) Stats() (*Stats, error) { return stats, nil } -func (c *linuxContainer) Set(config configs.Config) error { +// Set resources of container as configured. Can be used to change resources +// when the container is running. +func (c *Container) Set(config configs.Config) error { c.m.Lock() defer c.m.Unlock() status, err := c.currentStatus() @@ -221,7 +200,9 @@ func (c *linuxContainer) Set(config configs.Config) error { return err } -func (c *linuxContainer) Start(process *Process) error { +// Start starts a process inside the container. Returns error if process fails +// to start. You can track process lifecycle with passed Process structure. +func (c *Container) Start(process *Process) error { c.m.Lock() defer c.m.Unlock() if c.config.Cgroups.Resources.SkipDevices { @@ -241,7 +222,10 @@ func (c *linuxContainer) Start(process *Process) error { return nil } -func (c *linuxContainer) Run(process *Process) error { +// Run immediately starts the process inside the container. Returns an error if +// the process fails to start. It does not block waiting for the exec fifo +// after start returns but opens the fifo after start returns. +func (c *Container) Run(process *Process) error { if err := c.Start(process); err != nil { return err } @@ -251,13 +235,14 @@ func (c *linuxContainer) Run(process *Process) error { return nil } -func (c *linuxContainer) Exec() error { +// Exec signals the container to exec the users process at the end of the init. +func (c *Container) Exec() error { c.m.Lock() defer c.m.Unlock() return c.exec() } -func (c *linuxContainer) exec() error { +func (c *Container) exec() error { path := filepath.Join(c.root, execFifoFilename) pid := c.initProcess.pid() blockingFifoOpenCh := awaitFifoOpen(path) @@ -329,7 +314,7 @@ type openResult struct { err error } -func (c *linuxContainer) start(process *Process) (retErr error) { +func (c *Container) start(process *Process) (retErr error) { parent, err := c.newParentProcess(process) if err != nil { return fmt.Errorf("unable to create new parent process: %w", err) @@ -370,7 +355,7 @@ func (c *linuxContainer) start(process *Process) (retErr error) { return nil } -func (c *linuxContainer) Signal(s os.Signal, all bool) error { +func (c *Container) Signal(s os.Signal, all bool) error { c.m.Lock() defer c.m.Unlock() status, err := c.currentStatus() @@ -402,7 +387,7 @@ func (c *linuxContainer) Signal(s os.Signal, all bool) error { return ErrNotRunning } -func (c *linuxContainer) createExecFifo() error { +func (c *Container) createExecFifo() error { rootuid, err := c.Config().HostRootUID() if err != nil { return err @@ -425,7 +410,7 @@ func (c *linuxContainer) createExecFifo() error { return os.Chown(fifoName, rootuid, rootgid) } -func (c *linuxContainer) deleteExecFifo() { +func (c *Container) deleteExecFifo() { fifoName := filepath.Join(c.root, execFifoFilename) os.Remove(fifoName) } @@ -434,7 +419,7 @@ func (c *linuxContainer) deleteExecFifo() { // container cannot access the statedir (and the FIFO itself remains // un-opened). It then adds the FifoFd to the given exec.Cmd as an inherited // fd, with _LIBCONTAINER_FIFOFD set to its fd number. -func (c *linuxContainer) includeExecFifo(cmd *exec.Cmd) error { +func (c *Container) includeExecFifo(cmd *exec.Cmd) error { fifoName := filepath.Join(c.root, execFifoFilename) fifo, err := os.OpenFile(fifoName, unix.O_PATH|unix.O_CLOEXEC, 0) if err != nil { @@ -448,7 +433,7 @@ func (c *linuxContainer) includeExecFifo(cmd *exec.Cmd) error { return nil } -func (c *linuxContainer) newParentProcess(p *Process) (parentProcess, error) { +func (c *Container) newParentProcess(p *Process) (parentProcess, error) { parentInitPipe, childInitPipe, err := utils.NewSockPair("init") if err != nil { return nil, fmt.Errorf("unable to create init pipe: %w", err) @@ -477,7 +462,7 @@ func (c *linuxContainer) newParentProcess(p *Process) (parentProcess, error) { return c.newInitProcess(p, cmd, messageSockPair, logFilePair) } -func (c *linuxContainer) commandTemplate(p *Process, childInitPipe *os.File, childLogPipe *os.File) *exec.Cmd { +func (c *Container) commandTemplate(p *Process, childInitPipe *os.File, childLogPipe *os.File) *exec.Cmd { cmd := exec.Command("/proc/self/exe", "init") cmd.Args[0] = os.Args[0] cmd.Stdin = p.Stdin @@ -519,7 +504,7 @@ func (c *linuxContainer) commandTemplate(p *Process, childInitPipe *os.File, chi // shouldSendMountSources says whether the child process must setup bind mounts with // the source pre-opened (O_PATH) in the host user namespace. // See https://github.com/opencontainers/runc/issues/2484 -func (c *linuxContainer) shouldSendMountSources() bool { +func (c *Container) shouldSendMountSources() bool { // Passing the mount sources via SCM_RIGHTS is only necessary when // both userns and mntns are active. if !c.config.Namespaces.Contains(configs.NEWUSER) || @@ -543,7 +528,7 @@ func (c *linuxContainer) shouldSendMountSources() bool { return false } -func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, messageSockPair, logFilePair filePair) (*initProcess, error) { +func (c *Container) newInitProcess(p *Process, cmd *exec.Cmd, messageSockPair, logFilePair filePair) (*initProcess, error) { cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initStandard)) nsMaps := make(map[configs.NamespaceType]string) for _, ns := range c.config.Namespaces { @@ -602,7 +587,7 @@ func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, messageSockPa return init, nil } -func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, messageSockPair, logFilePair filePair) (*setnsProcess, error) { +func (c *Container) newSetnsProcess(p *Process, cmd *exec.Cmd, messageSockPair, logFilePair filePair) (*setnsProcess, error) { cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initSetns)) state, err := c.currentState() if err != nil { @@ -659,7 +644,7 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, messageSockP return proc, nil } -func (c *linuxContainer) newInitConfig(process *Process) *initConfig { +func (c *Container) newInitConfig(process *Process) *initConfig { cfg := &initConfig{ Config: c.config, Args: process.Args, @@ -699,13 +684,23 @@ func (c *linuxContainer) newInitConfig(process *Process) *initConfig { return cfg } -func (c *linuxContainer) Destroy() error { +// Destroy destroys the container, if its in a valid state, after killing any +// remaining running processes. +// +// Any event registrations are removed before the container is destroyed. +// No error is returned if the container is already destroyed. +// +// Running containers must first be stopped using Signal. +// Paused containers must first be resumed using Resume. +func (c *Container) Destroy() error { c.m.Lock() defer c.m.Unlock() return c.state.destroy() } -func (c *linuxContainer) Pause() error { +// Pause pauses the container, if its state is RUNNING or CREATED, changing +// its state to PAUSED. If the state is already PAUSED, does nothing. +func (c *Container) Pause() error { c.m.Lock() defer c.m.Unlock() status, err := c.currentStatus() @@ -724,7 +719,11 @@ func (c *linuxContainer) Pause() error { return ErrNotRunning } -func (c *linuxContainer) Resume() error { +// Resume resumes the execution of any user processes in the +// container before setting the container state to RUNNING. +// This is only performed if the current state is PAUSED. +// If the Container state is RUNNING, does nothing. +func (c *Container) Resume() error { c.m.Lock() defer c.m.Unlock() status, err := c.currentStatus() @@ -742,7 +741,9 @@ func (c *linuxContainer) Resume() error { }) } -func (c *linuxContainer) NotifyOOM() (<-chan struct{}, error) { +// NotifyOOM returns a read-only channel signaling when the container receives +// an OOM notification. +func (c *Container) NotifyOOM() (<-chan struct{}, error) { // XXX(cyphar): This requires cgroups. if c.config.RootlessCgroups { logrus.Warn("getting OOM notifications may fail if you don't have the full access to cgroups") @@ -754,7 +755,9 @@ func (c *linuxContainer) NotifyOOM() (<-chan struct{}, error) { return notifyOnOOM(path) } -func (c *linuxContainer) NotifyMemoryPressure(level PressureLevel) (<-chan struct{}, error) { +// NotifyMemoryPressure returns a read-only channel signaling when the +// container reaches a given pressure level. +func (c *Container) NotifyMemoryPressure(level PressureLevel) (<-chan struct{}, error) { // XXX(cyphar): This requires cgroups. if c.config.RootlessCgroups { logrus.Warn("getting memory pressure notifications may fail if you don't have the full access to cgroups") @@ -764,7 +767,7 @@ func (c *linuxContainer) NotifyMemoryPressure(level PressureLevel) (<-chan struc var criuFeatures *criurpc.CriuFeatures -func (c *linuxContainer) checkCriuFeatures(criuOpts *CriuOpts, rpcOpts *criurpc.CriuOpts, criuFeat *criurpc.CriuFeatures) error { +func (c *Container) checkCriuFeatures(criuOpts *CriuOpts, rpcOpts *criurpc.CriuOpts, criuFeat *criurpc.CriuFeatures) error { t := criurpc.CriuReqType_FEATURE_CHECK // make sure the features we are looking for are really not from @@ -824,8 +827,8 @@ func compareCriuVersion(criuVersion int, minVersion int) error { return nil } -// checkCriuVersion checks Criu version greater than or equal to minVersion -func (c *linuxContainer) checkCriuVersion(minVersion int) error { +// checkCriuVersion checks CRIU version greater than or equal to minVersion. +func (c *Container) checkCriuVersion(minVersion int) error { // If the version of criu has already been determined there is no need // to ask criu for the version again. Use the value from c.criuVersion. if c.criuVersion != 0 { @@ -844,7 +847,7 @@ func (c *linuxContainer) checkCriuVersion(minVersion int) error { const descriptorsFilename = "descriptors.json" -func (c *linuxContainer) addCriuDumpMount(req *criurpc.CriuReq, m *configs.Mount) { +func (c *Container) addCriuDumpMount(req *criurpc.CriuReq, m *configs.Mount) { mountDest := strings.TrimPrefix(m.Destination, c.config.Rootfs) if dest, err := securejoin.SecureJoin(c.config.Rootfs, mountDest); err == nil { mountDest = dest[len(c.config.Rootfs):] @@ -856,7 +859,7 @@ func (c *linuxContainer) addCriuDumpMount(req *criurpc.CriuReq, m *configs.Mount req.Opts.ExtMnt = append(req.Opts.ExtMnt, extMnt) } -func (c *linuxContainer) addMaskPaths(req *criurpc.CriuReq) error { +func (c *Container) addMaskPaths(req *criurpc.CriuReq) error { for _, path := range c.config.MaskPaths { fi, err := os.Stat(fmt.Sprintf("/proc/%d/root/%s", c.initProcess.pid(), path)) if err != nil { @@ -878,7 +881,7 @@ func (c *linuxContainer) addMaskPaths(req *criurpc.CriuReq) error { return nil } -func (c *linuxContainer) handleCriuConfigurationFile(rpcOpts *criurpc.CriuOpts) { +func (c *Container) handleCriuConfigurationFile(rpcOpts *criurpc.CriuOpts) { // CRIU will evaluate a configuration starting with release 3.11. // Settings in the configuration file will overwrite RPC settings. // Look for annotations. The annotation 'org.criu.config' @@ -903,7 +906,7 @@ func (c *linuxContainer) handleCriuConfigurationFile(rpcOpts *criurpc.CriuOpts) } } -func (c *linuxContainer) criuSupportsExtNS(t configs.NamespaceType) bool { +func (c *Container) criuSupportsExtNS(t configs.NamespaceType) bool { var minVersion int switch t { case configs.NEWNET: @@ -923,7 +926,7 @@ func criuNsToKey(t configs.NamespaceType) string { return "extRoot" + strings.Title(configs.NsName(t)) + "NS" //nolint:staticcheck // SA1019: strings.Title is deprecated } -func (c *linuxContainer) handleCheckpointingExternalNamespaces(rpcOpts *criurpc.CriuOpts, t configs.NamespaceType) error { +func (c *Container) handleCheckpointingExternalNamespaces(rpcOpts *criurpc.CriuOpts, t configs.NamespaceType) error { if !c.criuSupportsExtNS(t) { return nil } @@ -945,7 +948,7 @@ func (c *linuxContainer) handleCheckpointingExternalNamespaces(rpcOpts *criurpc. return nil } -func (c *linuxContainer) handleRestoringNamespaces(rpcOpts *criurpc.CriuOpts, extraFiles *[]*os.File) error { +func (c *Container) handleRestoringNamespaces(rpcOpts *criurpc.CriuOpts, extraFiles *[]*os.File) error { for _, ns := range c.config.Namespaces { switch ns.Type { case configs.NEWNET, configs.NEWPID: @@ -983,7 +986,7 @@ func (c *linuxContainer) handleRestoringNamespaces(rpcOpts *criurpc.CriuOpts, ex return nil } -func (c *linuxContainer) handleRestoringExternalNamespaces(rpcOpts *criurpc.CriuOpts, extraFiles *[]*os.File, t configs.NamespaceType) error { +func (c *Container) handleRestoringExternalNamespaces(rpcOpts *criurpc.CriuOpts, extraFiles *[]*os.File, t configs.NamespaceType) error { if !c.criuSupportsExtNS(t) { return nil } @@ -1014,7 +1017,7 @@ func (c *linuxContainer) handleRestoringExternalNamespaces(rpcOpts *criurpc.Criu return nil } -func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error { +func (c *Container) Checkpoint(criuOpts *CriuOpts) error { c.m.Lock() defer c.m.Unlock() @@ -1222,7 +1225,7 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error { return nil } -func (c *linuxContainer) addCriuRestoreMount(req *criurpc.CriuReq, m *configs.Mount) { +func (c *Container) addCriuRestoreMount(req *criurpc.CriuReq, m *configs.Mount) { mountDest := strings.TrimPrefix(m.Destination, c.config.Rootfs) if dest, err := securejoin.SecureJoin(c.config.Rootfs, mountDest); err == nil { mountDest = dest[len(c.config.Rootfs):] @@ -1234,7 +1237,7 @@ func (c *linuxContainer) addCriuRestoreMount(req *criurpc.CriuReq, m *configs.Mo req.Opts.ExtMnt = append(req.Opts.ExtMnt, extMnt) } -func (c *linuxContainer) restoreNetwork(req *criurpc.CriuReq, criuOpts *CriuOpts) { +func (c *Container) restoreNetwork(req *criurpc.CriuReq, criuOpts *CriuOpts) { for _, iface := range c.config.Networks { switch iface.Type { case "veth": @@ -1257,7 +1260,7 @@ func (c *linuxContainer) restoreNetwork(req *criurpc.CriuReq, criuOpts *CriuOpts // makeCriuRestoreMountpoints makes the actual mountpoints for the // restore using CRIU. This function is inspired from the code in // rootfs_linux.go -func (c *linuxContainer) makeCriuRestoreMountpoints(m *configs.Mount) error { +func (c *Container) makeCriuRestoreMountpoints(m *configs.Mount) error { switch m.Device { case "cgroup": // No mount point(s) need to be created: @@ -1309,7 +1312,7 @@ func isPathInPrefixList(path string, prefix []string) bool { // runc modifies the rootfs to add mountpoints which do not exist. // This function also creates missing mountpoints as long as they // are not on top of a tmpfs, as CRIU will restore tmpfs content anyway. -func (c *linuxContainer) prepareCriuRestoreMounts(mounts []*configs.Mount) error { +func (c *Container) prepareCriuRestoreMounts(mounts []*configs.Mount) error { // First get a list of a all tmpfs mounts tmpfs := []string{} for _, m := range mounts { @@ -1366,7 +1369,9 @@ func (c *linuxContainer) prepareCriuRestoreMounts(mounts []*configs.Mount) error return nil } -func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error { +// Restore restores the checkpointed container to a running state using the +// criu(8) utility. +func (c *Container) Restore(process *Process, criuOpts *CriuOpts) error { c.m.Lock() defer c.m.Unlock() @@ -1540,7 +1545,7 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error { return err } -func (c *linuxContainer) criuApplyCgroups(pid int, req *criurpc.CriuReq) error { +func (c *Container) criuApplyCgroups(pid int, req *criurpc.CriuReq) error { // need to apply cgroups only on restore if req.GetType() != criurpc.CriuReqType_RESTORE { return nil @@ -1577,7 +1582,7 @@ func (c *linuxContainer) criuApplyCgroups(pid int, req *criurpc.CriuReq) error { return nil } -func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *CriuOpts, extraFiles []*os.File) error { +func (c *Container) criuSwrk(process *Process, req *criurpc.CriuReq, opts *CriuOpts, extraFiles []*os.File) error { fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_SEQPACKET|unix.SOCK_CLOEXEC, 0) if err != nil { return err @@ -1795,7 +1800,7 @@ func unlockNetwork(config *configs.Config) error { return nil } -func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Process, cmd *exec.Cmd, opts *CriuOpts, fds []string, oob []byte) error { +func (c *Container) criuNotifications(resp *criurpc.CriuResp, process *Process, cmd *exec.Cmd, opts *CriuOpts, fds []string, oob []byte) error { notify := resp.GetNotify() if notify == nil { return fmt.Errorf("invalid response: %s", resp.String()) @@ -1893,7 +1898,7 @@ func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Proc return nil } -func (c *linuxContainer) updateState(process parentProcess) (*State, error) { +func (c *Container) updateState(process parentProcess) (*State, error) { if process != nil { c.initProcess = process } @@ -1908,7 +1913,7 @@ func (c *linuxContainer) updateState(process parentProcess) (*State, error) { return state, nil } -func (c *linuxContainer) saveState(s *State) (retErr error) { +func (c *Container) saveState(s *State) (retErr error) { tmpFile, err := os.CreateTemp(c.root, "state-") if err != nil { return err @@ -1934,7 +1939,7 @@ func (c *linuxContainer) saveState(s *State) (retErr error) { return os.Rename(tmpFile.Name(), stateFilePath) } -func (c *linuxContainer) currentStatus() (Status, error) { +func (c *Container) currentStatus() (Status, error) { if err := c.refreshState(); err != nil { return -1, err } @@ -1945,7 +1950,7 @@ func (c *linuxContainer) currentStatus() (Status, error) { // container is what is true. Because consumers of libcontainer can use it // out of process we need to verify the container's status based on runtime // information and not rely on our in process info. -func (c *linuxContainer) refreshState() error { +func (c *Container) refreshState() error { paused, err := c.isPaused() if err != nil { return err @@ -1963,7 +1968,7 @@ func (c *linuxContainer) refreshState() error { return c.state.transition(&stoppedState{c: c}) } -func (c *linuxContainer) runType() Status { +func (c *Container) runType() Status { if c.initProcess == nil { return Stopped } @@ -1983,7 +1988,7 @@ func (c *linuxContainer) runType() Status { return Running } -func (c *linuxContainer) isPaused() (bool, error) { +func (c *Container) isPaused() (bool, error) { state, err := c.cgroupManager.GetFreezerState() if err != nil { return false, err @@ -1991,7 +1996,7 @@ func (c *linuxContainer) isPaused() (bool, error) { return state == configs.Frozen, nil } -func (c *linuxContainer) currentState() (*State, error) { +func (c *Container) currentState() (*State, error) { var ( startTime uint64 externalDescriptors []string @@ -2038,7 +2043,7 @@ func (c *linuxContainer) currentState() (*State, error) { return state, nil } -func (c *linuxContainer) currentOCIState() (*specs.State, error) { +func (c *Container) currentOCIState() (*specs.State, error) { bundle, annotations := utils.Annotations(c.config.Labels) state := &specs.State{ Version: specs.Version, @@ -2061,7 +2066,7 @@ func (c *linuxContainer) currentOCIState() (*specs.State, error) { // orderNamespacePaths sorts namespace paths into a list of paths that we // can setns in order. -func (c *linuxContainer) orderNamespacePaths(namespaces map[configs.NamespaceType]string) ([]string, error) { +func (c *Container) orderNamespacePaths(namespaces map[configs.NamespaceType]string) ([]string, error) { paths := []string{} for _, ns := range configs.NamespaceTypes() { @@ -2114,7 +2119,7 @@ type netlinkError struct{ error } // such as one that uses nsenter package to bootstrap the container's // init process correctly, i.e. with correct namespaces, uid/gid // mapping etc. -func (c *linuxContainer) bootstrapData(cloneFlags uintptr, nsMaps map[configs.NamespaceType]string, it initType) (_ io.Reader, Err error) { +func (c *Container) bootstrapData(cloneFlags uintptr, nsMaps map[configs.NamespaceType]string, it initType) (_ io.Reader, Err error) { // create the netlink message r := nl.NewNetlinkRequest(int(InitMsg), 0) diff --git a/libcontainer/container_linux_test.go b/libcontainer/container_linux_test.go index 27eee624c07..6551de8085f 100644 --- a/libcontainer/container_linux_test.go +++ b/libcontainer/container_linux_test.go @@ -115,7 +115,7 @@ func TestGetContainerPids(t *testing.T) { if err != nil { t.Fatalf("can't stat pid %d, got %v", pid, err) } - container := &linuxContainer{ + container := &Container{ id: "myid", config: &configs.Config{}, cgroupManager: &mockCgroupManager{ @@ -148,7 +148,7 @@ func TestGetContainerState(t *testing.T) { expectedMemoryPath = "/sys/fs/cgroup/memory/myid" expectedNetworkPath = fmt.Sprintf("/proc/%d/ns/net", pid) ) - container := &linuxContainer{ + container := &Container{ id: "myid", config: &configs.Config{ Namespaces: []configs.Namespace{ @@ -232,7 +232,7 @@ func TestGetContainerStateAfterUpdate(t *testing.T) { t.Fatal(err) } - container := &linuxContainer{ + container := &Container{ root: t.TempDir(), id: "myid", config: &configs.Config{ diff --git a/libcontainer/factory_linux.go b/libcontainer/factory_linux.go index 9d4bc4cfe60..10ca0eeea7e 100644 --- a/libcontainer/factory_linux.go +++ b/libcontainer/factory_linux.go @@ -37,7 +37,7 @@ var idRegex = regexp.MustCompile(`^[\w+-\.]+$`) // The id must not be empty and consist of only the following characters: // ASCII letters, digits, underscore, plus, minus, period. The id must be // unique and non-existent for the given root path. -func Create(root, id string, config *configs.Config) (Container, error) { +func Create(root, id string, config *configs.Config) (*Container, error) { if root == "" { return nil, errors.New("root not set") } @@ -98,7 +98,7 @@ func Create(root, id string, config *configs.Config) (Container, error) { if err := os.Mkdir(containerRoot, 0o711); err != nil { return nil, err } - c := &linuxContainer{ + c := &Container{ id: id, root: containerRoot, config: config, @@ -112,7 +112,7 @@ func Create(root, id string, config *configs.Config) (Container, error) { // Load takes a path to the state directory (root) and an id of an existing // container, and returns a Container object reconstructed from the saved // state. This presents a read only view of the container. -func Load(root, id string) (Container, error) { +func Load(root, id string) (*Container, error) { if root == "" { return nil, errors.New("root not set") } @@ -137,7 +137,7 @@ func Load(root, id string) (Container, error) { if err != nil { return nil, err } - c := &linuxContainer{ + c := &Container{ initProcess: r, initProcessStartTime: state.InitProcessStartTime, id: id, diff --git a/libcontainer/factory_linux_test.go b/libcontainer/factory_linux_test.go index c9e2b0e0908..889ae0340dd 100644 --- a/libcontainer/factory_linux_test.go +++ b/libcontainer/factory_linux_test.go @@ -75,12 +75,8 @@ func TestFactoryLoadContainer(t *testing.T) { if !reflect.DeepEqual(config.Hooks, expectedHooks) { t.Fatalf("expects hooks %q but received %q", expectedHooks, config.Hooks) } - lcontainer, ok := container.(*linuxContainer) - if !ok { - t.Fatal("expected linux container on linux based systems") - } - if lcontainer.initProcess.pid() != expectedState.InitProcessPid { - t.Fatalf("expected init pid %d but received %d", expectedState.InitProcessPid, lcontainer.initProcess.pid()) + if container.initProcess.pid() != expectedState.InitProcessPid { + t.Fatalf("expected init pid %d but received %d", expectedState.InitProcessPid, container.initProcess.pid()) } } diff --git a/libcontainer/integration/utils_test.go b/libcontainer/integration/utils_test.go index c54a40bbe11..3bacf93d0ef 100644 --- a/libcontainer/integration/utils_test.go +++ b/libcontainer/integration/utils_test.go @@ -165,7 +165,7 @@ func copyBusybox(dest string) error { return nil } -func newContainer(t *testing.T, config *configs.Config) (libcontainer.Container, error) { +func newContainer(t *testing.T, config *configs.Config) (*libcontainer.Container, error) { name := strings.ReplaceAll(t.Name(), "/", "_") + strconv.FormatInt(-int64(time.Now().Nanosecond()), 35) root := t.TempDir() @@ -212,6 +212,6 @@ func runContainer(t *testing.T, config *configs.Config, args ...string) (buffers return } -func destroyContainer(container libcontainer.Container) { +func destroyContainer(container *libcontainer.Container) { _ = container.Destroy() } diff --git a/libcontainer/process_linux.go b/libcontainer/process_linux.go index c2ee2da6b13..ea3223ae0d7 100644 --- a/libcontainer/process_linux.go +++ b/libcontainer/process_linux.go @@ -300,7 +300,7 @@ type initProcess struct { config *initConfig manager cgroups.Manager intelRdtManager *intelrdt.Manager - container *linuxContainer + container *Container fds []string process *Process bootstrapData io.Reader diff --git a/libcontainer/state_linux.go b/libcontainer/state_linux.go index aa6259b157d..4895612e257 100644 --- a/libcontainer/state_linux.go +++ b/libcontainer/state_linux.go @@ -35,7 +35,7 @@ type containerState interface { status() Status } -func destroy(c *linuxContainer) error { +func destroy(c *Container) error { if !c.config.Namespaces.Contains(configs.NEWPID) || c.config.Namespaces.PathOf(configs.NEWPID) != "" { if err := signalAllProcesses(c.cgroupManager, unix.SIGKILL); err != nil { @@ -59,7 +59,7 @@ func destroy(c *linuxContainer) error { return err } -func runPoststopHooks(c *linuxContainer) error { +func runPoststopHooks(c *Container) error { hooks := c.config.Hooks if hooks == nil { return nil @@ -80,7 +80,7 @@ func runPoststopHooks(c *linuxContainer) error { // stoppedState represents a container is a stopped/destroyed state. type stoppedState struct { - c *linuxContainer + c *Container } func (b *stoppedState) status() Status { @@ -104,7 +104,7 @@ func (b *stoppedState) destroy() error { // runningState represents a container that is currently running. type runningState struct { - c *linuxContainer + c *Container } func (r *runningState) status() Status { @@ -136,7 +136,7 @@ func (r *runningState) destroy() error { } type createdState struct { - c *linuxContainer + c *Container } func (i *createdState) status() Status { @@ -162,7 +162,7 @@ func (i *createdState) destroy() error { // pausedState represents a container that is currently pause. It cannot be destroyed in a // paused state and must transition back to running first. type pausedState struct { - c *linuxContainer + c *Container } func (p *pausedState) status() Status { @@ -195,7 +195,7 @@ func (p *pausedState) destroy() error { // information that maybe need destroyed when the container is stopped and destroy is called. type restoredState struct { imageDir string - c *linuxContainer + c *Container } func (r *restoredState) status() Status { @@ -222,7 +222,7 @@ func (r *restoredState) destroy() error { // loadedState is used whenever a container is restored, loaded, or setting additional // processes inside and it should not be destroyed when it is exiting. type loadedState struct { - c *linuxContainer + c *Container s Status } diff --git a/libcontainer/state_linux_test.go b/libcontainer/state_linux_test.go index 413626d5118..b57e6eff716 100644 --- a/libcontainer/state_linux_test.go +++ b/libcontainer/state_linux_test.go @@ -53,7 +53,7 @@ func testTransitions(t *testing.T, initialState containerState, valid []containe func TestStoppedStateTransition(t *testing.T) { testTransitions( t, - &stoppedState{c: &linuxContainer{}}, + &stoppedState{c: &Container{}}, []containerState{ &stoppedState{}, &runningState{}, @@ -65,7 +65,7 @@ func TestStoppedStateTransition(t *testing.T) { func TestPausedStateTransition(t *testing.T) { testTransitions( t, - &pausedState{c: &linuxContainer{}}, + &pausedState{c: &Container{}}, []containerState{ &pausedState{}, &runningState{}, @@ -77,7 +77,7 @@ func TestPausedStateTransition(t *testing.T) { func TestRestoredStateTransition(t *testing.T) { testTransitions( t, - &restoredState{c: &linuxContainer{}}, + &restoredState{c: &Container{}}, []containerState{ &stoppedState{}, &runningState{}, @@ -88,7 +88,7 @@ func TestRestoredStateTransition(t *testing.T) { func TestRunningStateTransition(t *testing.T) { testTransitions( t, - &runningState{c: &linuxContainer{}}, + &runningState{c: &Container{}}, []containerState{ &stoppedState{}, &pausedState{}, @@ -100,7 +100,7 @@ func TestRunningStateTransition(t *testing.T) { func TestCreatedStateTransition(t *testing.T) { testTransitions( t, - &createdState{c: &linuxContainer{}}, + &createdState{c: &Container{}}, []containerState{ &stoppedState{}, &pausedState{}, diff --git a/notify_socket.go b/notify_socket.go index 9dde506c3e1..28c6c0ae65a 100644 --- a/notify_socket.go +++ b/notify_socket.go @@ -91,7 +91,7 @@ func notifySocketStart(context *cli.Context, notifySocketHost, id string) (*noti return notifySocket, nil } -func (s *notifySocket) waitForContainer(container libcontainer.Container) error { +func (s *notifySocket) waitForContainer(container *libcontainer.Container) error { state, err := container.State() if err != nil { return err diff --git a/utils_linux.go b/utils_linux.go index 4124d9a7e1d..4c00b2092db 100644 --- a/utils_linux.go +++ b/utils_linux.go @@ -25,7 +25,7 @@ var errEmptyID = errors.New("container id cannot be empty") // getContainer returns the specified container instance by loading it from // a state directory (root). -func getContainer(context *cli.Context) (libcontainer.Container, error) { +func getContainer(context *cli.Context) (*libcontainer.Container, error) { id := context.Args().First() if id == "" { return nil, errEmptyID @@ -82,7 +82,7 @@ func newProcess(p specs.Process) (*libcontainer.Process, error) { return lp, nil } -func destroy(container libcontainer.Container) { +func destroy(container *libcontainer.Container) { if err := container.Destroy(); err != nil { logrus.Error(err) } @@ -162,7 +162,7 @@ func createPidFile(path string, process *libcontainer.Process) error { return os.Rename(tmpName, path) } -func createContainer(context *cli.Context, id string, spec *specs.Spec) (libcontainer.Container, error) { +func createContainer(context *cli.Context, id string, spec *specs.Spec) (*libcontainer.Container, error) { rootlessCg, err := shouldUseRootlessCgroupManager(context) if err != nil { return nil, err @@ -193,7 +193,7 @@ type runner struct { preserveFDs int pidFile string consoleSocket string - container libcontainer.Container + container *libcontainer.Container action CtAct notifySocket *notifySocket criuOpts *libcontainer.CriuOpts