From 102b8abd266de73950301aae0106f2ca582c2de3 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Fri, 11 Feb 2022 12:07:29 -0800 Subject: [PATCH] libct: rm BaseContainer and Container interfaces The only implementation of these is linuxContainer. It does not make sense to have an interface with a single implementation, and we do not foresee other types of containers being added to runc. Remove BaseContainer and Container interfaces, moving their methods documentation to linuxContainer. Rename linuxContainer to Container. Adopt users from using interface to using struct. Signed-off-by: Kir Kolyshkin --- delete.go | 2 +- libcontainer/container.go | 67 --------- libcontainer/container_linux.go | 189 +++++++++++++------------ libcontainer/container_linux_test.go | 6 +- libcontainer/factory_linux.go | 8 +- libcontainer/factory_linux_test.go | 8 +- libcontainer/integration/utils_test.go | 4 +- libcontainer/process_linux.go | 2 +- libcontainer/state_linux.go | 16 +-- libcontainer/state_linux_test.go | 10 +- notify_socket.go | 2 +- utils_linux.go | 8 +- 12 files changed, 128 insertions(+), 194 deletions(-) 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