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 <kolyshkin@gmail.com>
This commit is contained in:
Kir Kolyshkin
2022-02-11 12:07:29 -08:00
parent c4c48896eb
commit 102b8abd26
12 changed files with 128 additions and 194 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
func killContainer(container libcontainer.Container) error { func killContainer(container *libcontainer.Container) error {
_ = container.Signal(unix.SIGKILL, false) _ = container.Signal(unix.SIGKILL, false)
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
-67
View File
@@ -5,11 +5,9 @@
package libcontainer package libcontainer
import ( import (
"os"
"time" "time"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runtime-spec/specs-go"
) )
// Status is the status of a container. // Status is the status of a container.
@@ -59,68 +57,3 @@ type BaseState struct {
// Config is the container's configuration. // Config is the container's configuration.
Config configs.Config `json:"config"` 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
}
+97 -92
View File
@@ -36,7 +36,8 @@ import (
const stdioFdCount = 3 const stdioFdCount = 3
type linuxContainer struct { // Container is a libcontainer container object.
type Container struct {
id string id string
root string root string
config *configs.Config config *configs.Config
@@ -80,69 +81,44 @@ type State struct {
IntelRdtPath string `json:"intel_rdt_path"` 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 // ID returns the container's unique ID
func (c *linuxContainer) ID() string { func (c *Container) ID() string {
return c.id return c.id
} }
// Config returns the container's configuration // Config returns the container's configuration
func (c *linuxContainer) Config() configs.Config { func (c *Container) Config() configs.Config {
return *c.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() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
return c.currentStatus() 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() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
return c.currentState() 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() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
return c.currentOCIState() 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 var pids []int
status, err := c.currentStatus() status, err := c.currentStatus()
if err != nil { if err != nil {
@@ -160,7 +136,8 @@ func (c *linuxContainer) Processes() ([]int, error) {
return pids, nil return pids, nil
} }
func (c *linuxContainer) Stats() (*Stats, error) { // Stats returns statistics for the container.
func (c *Container) Stats() (*Stats, error) {
var ( var (
err error err error
stats = &Stats{} stats = &Stats{}
@@ -186,7 +163,9 @@ func (c *linuxContainer) Stats() (*Stats, error) {
return stats, nil 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() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
status, err := c.currentStatus() status, err := c.currentStatus()
@@ -221,7 +200,9 @@ func (c *linuxContainer) Set(config configs.Config) error {
return err 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() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
if c.config.Cgroups.Resources.SkipDevices { if c.config.Cgroups.Resources.SkipDevices {
@@ -241,7 +222,10 @@ func (c *linuxContainer) Start(process *Process) error {
return nil 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 { if err := c.Start(process); err != nil {
return err return err
} }
@@ -251,13 +235,14 @@ func (c *linuxContainer) Run(process *Process) error {
return nil 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() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
return c.exec() return c.exec()
} }
func (c *linuxContainer) exec() error { func (c *Container) exec() error {
path := filepath.Join(c.root, execFifoFilename) path := filepath.Join(c.root, execFifoFilename)
pid := c.initProcess.pid() pid := c.initProcess.pid()
blockingFifoOpenCh := awaitFifoOpen(path) blockingFifoOpenCh := awaitFifoOpen(path)
@@ -329,7 +314,7 @@ type openResult struct {
err error err error
} }
func (c *linuxContainer) start(process *Process) (retErr error) { func (c *Container) start(process *Process) (retErr error) {
parent, err := c.newParentProcess(process) parent, err := c.newParentProcess(process)
if err != nil { if err != nil {
return fmt.Errorf("unable to create new parent process: %w", err) 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 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() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
status, err := c.currentStatus() status, err := c.currentStatus()
@@ -402,7 +387,7 @@ func (c *linuxContainer) Signal(s os.Signal, all bool) error {
return ErrNotRunning return ErrNotRunning
} }
func (c *linuxContainer) createExecFifo() error { func (c *Container) createExecFifo() error {
rootuid, err := c.Config().HostRootUID() rootuid, err := c.Config().HostRootUID()
if err != nil { if err != nil {
return err return err
@@ -425,7 +410,7 @@ func (c *linuxContainer) createExecFifo() error {
return os.Chown(fifoName, rootuid, rootgid) return os.Chown(fifoName, rootuid, rootgid)
} }
func (c *linuxContainer) deleteExecFifo() { func (c *Container) deleteExecFifo() {
fifoName := filepath.Join(c.root, execFifoFilename) fifoName := filepath.Join(c.root, execFifoFilename)
os.Remove(fifoName) os.Remove(fifoName)
} }
@@ -434,7 +419,7 @@ func (c *linuxContainer) deleteExecFifo() {
// container cannot access the statedir (and the FIFO itself remains // 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 // un-opened). It then adds the FifoFd to the given exec.Cmd as an inherited
// fd, with _LIBCONTAINER_FIFOFD set to its fd number. // 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) fifoName := filepath.Join(c.root, execFifoFilename)
fifo, err := os.OpenFile(fifoName, unix.O_PATH|unix.O_CLOEXEC, 0) fifo, err := os.OpenFile(fifoName, unix.O_PATH|unix.O_CLOEXEC, 0)
if err != nil { if err != nil {
@@ -448,7 +433,7 @@ func (c *linuxContainer) includeExecFifo(cmd *exec.Cmd) error {
return nil return nil
} }
func (c *linuxContainer) newParentProcess(p *Process) (parentProcess, error) { func (c *Container) newParentProcess(p *Process) (parentProcess, error) {
parentInitPipe, childInitPipe, err := utils.NewSockPair("init") parentInitPipe, childInitPipe, err := utils.NewSockPair("init")
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to create init pipe: %w", err) 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) 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 := exec.Command("/proc/self/exe", "init")
cmd.Args[0] = os.Args[0] cmd.Args[0] = os.Args[0]
cmd.Stdin = p.Stdin 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 // shouldSendMountSources says whether the child process must setup bind mounts with
// the source pre-opened (O_PATH) in the host user namespace. // the source pre-opened (O_PATH) in the host user namespace.
// See https://github.com/opencontainers/runc/issues/2484 // 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 // Passing the mount sources via SCM_RIGHTS is only necessary when
// both userns and mntns are active. // both userns and mntns are active.
if !c.config.Namespaces.Contains(configs.NEWUSER) || if !c.config.Namespaces.Contains(configs.NEWUSER) ||
@@ -543,7 +528,7 @@ func (c *linuxContainer) shouldSendMountSources() bool {
return false 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)) cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initStandard))
nsMaps := make(map[configs.NamespaceType]string) nsMaps := make(map[configs.NamespaceType]string)
for _, ns := range c.config.Namespaces { for _, ns := range c.config.Namespaces {
@@ -602,7 +587,7 @@ func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, messageSockPa
return init, nil 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)) cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initSetns))
state, err := c.currentState() state, err := c.currentState()
if err != nil { if err != nil {
@@ -659,7 +644,7 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, messageSockP
return proc, nil return proc, nil
} }
func (c *linuxContainer) newInitConfig(process *Process) *initConfig { func (c *Container) newInitConfig(process *Process) *initConfig {
cfg := &initConfig{ cfg := &initConfig{
Config: c.config, Config: c.config,
Args: process.Args, Args: process.Args,
@@ -699,13 +684,23 @@ func (c *linuxContainer) newInitConfig(process *Process) *initConfig {
return cfg 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() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
return c.state.destroy() 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() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
status, err := c.currentStatus() status, err := c.currentStatus()
@@ -724,7 +719,11 @@ func (c *linuxContainer) Pause() error {
return ErrNotRunning 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() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
status, err := c.currentStatus() 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. // XXX(cyphar): This requires cgroups.
if c.config.RootlessCgroups { if c.config.RootlessCgroups {
logrus.Warn("getting OOM notifications may fail if you don't have the full access to cgroups") 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) 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. // XXX(cyphar): This requires cgroups.
if c.config.RootlessCgroups { if c.config.RootlessCgroups {
logrus.Warn("getting memory pressure notifications may fail if you don't have the full access to cgroups") 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 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 t := criurpc.CriuReqType_FEATURE_CHECK
// make sure the features we are looking for are really not from // 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 return nil
} }
// checkCriuVersion checks Criu version greater than or equal to minVersion // checkCriuVersion checks CRIU version greater than or equal to minVersion.
func (c *linuxContainer) checkCriuVersion(minVersion int) error { func (c *Container) checkCriuVersion(minVersion int) error {
// If the version of criu has already been determined there is no need // 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. // to ask criu for the version again. Use the value from c.criuVersion.
if c.criuVersion != 0 { if c.criuVersion != 0 {
@@ -844,7 +847,7 @@ func (c *linuxContainer) checkCriuVersion(minVersion int) error {
const descriptorsFilename = "descriptors.json" 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) mountDest := strings.TrimPrefix(m.Destination, c.config.Rootfs)
if dest, err := securejoin.SecureJoin(c.config.Rootfs, mountDest); err == nil { if dest, err := securejoin.SecureJoin(c.config.Rootfs, mountDest); err == nil {
mountDest = dest[len(c.config.Rootfs):] 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) 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 { for _, path := range c.config.MaskPaths {
fi, err := os.Stat(fmt.Sprintf("/proc/%d/root/%s", c.initProcess.pid(), path)) fi, err := os.Stat(fmt.Sprintf("/proc/%d/root/%s", c.initProcess.pid(), path))
if err != nil { if err != nil {
@@ -878,7 +881,7 @@ func (c *linuxContainer) addMaskPaths(req *criurpc.CriuReq) error {
return nil 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. // CRIU will evaluate a configuration starting with release 3.11.
// Settings in the configuration file will overwrite RPC settings. // Settings in the configuration file will overwrite RPC settings.
// Look for annotations. The annotation 'org.criu.config' // 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 var minVersion int
switch t { switch t {
case configs.NEWNET: 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 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) { if !c.criuSupportsExtNS(t) {
return nil return nil
} }
@@ -945,7 +948,7 @@ func (c *linuxContainer) handleCheckpointingExternalNamespaces(rpcOpts *criurpc.
return nil 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 { for _, ns := range c.config.Namespaces {
switch ns.Type { switch ns.Type {
case configs.NEWNET, configs.NEWPID: case configs.NEWNET, configs.NEWPID:
@@ -983,7 +986,7 @@ func (c *linuxContainer) handleRestoringNamespaces(rpcOpts *criurpc.CriuOpts, ex
return nil 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) { if !c.criuSupportsExtNS(t) {
return nil return nil
} }
@@ -1014,7 +1017,7 @@ func (c *linuxContainer) handleRestoringExternalNamespaces(rpcOpts *criurpc.Criu
return nil return nil
} }
func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error { func (c *Container) Checkpoint(criuOpts *CriuOpts) error {
c.m.Lock() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
@@ -1222,7 +1225,7 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
return nil 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) mountDest := strings.TrimPrefix(m.Destination, c.config.Rootfs)
if dest, err := securejoin.SecureJoin(c.config.Rootfs, mountDest); err == nil { if dest, err := securejoin.SecureJoin(c.config.Rootfs, mountDest); err == nil {
mountDest = dest[len(c.config.Rootfs):] 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) 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 { for _, iface := range c.config.Networks {
switch iface.Type { switch iface.Type {
case "veth": case "veth":
@@ -1257,7 +1260,7 @@ func (c *linuxContainer) restoreNetwork(req *criurpc.CriuReq, criuOpts *CriuOpts
// makeCriuRestoreMountpoints makes the actual mountpoints for the // makeCriuRestoreMountpoints makes the actual mountpoints for the
// restore using CRIU. This function is inspired from the code in // restore using CRIU. This function is inspired from the code in
// rootfs_linux.go // rootfs_linux.go
func (c *linuxContainer) makeCriuRestoreMountpoints(m *configs.Mount) error { func (c *Container) makeCriuRestoreMountpoints(m *configs.Mount) error {
switch m.Device { switch m.Device {
case "cgroup": case "cgroup":
// No mount point(s) need to be created: // 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. // runc modifies the rootfs to add mountpoints which do not exist.
// This function also creates missing mountpoints as long as they // This function also creates missing mountpoints as long as they
// are not on top of a tmpfs, as CRIU will restore tmpfs content anyway. // 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 // First get a list of a all tmpfs mounts
tmpfs := []string{} tmpfs := []string{}
for _, m := range mounts { for _, m := range mounts {
@@ -1366,7 +1369,9 @@ func (c *linuxContainer) prepareCriuRestoreMounts(mounts []*configs.Mount) error
return nil 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() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
@@ -1540,7 +1545,7 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
return err 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 // need to apply cgroups only on restore
if req.GetType() != criurpc.CriuReqType_RESTORE { if req.GetType() != criurpc.CriuReqType_RESTORE {
return nil return nil
@@ -1577,7 +1582,7 @@ func (c *linuxContainer) criuApplyCgroups(pid int, req *criurpc.CriuReq) error {
return nil 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) fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_SEQPACKET|unix.SOCK_CLOEXEC, 0)
if err != nil { if err != nil {
return err return err
@@ -1795,7 +1800,7 @@ func unlockNetwork(config *configs.Config) error {
return nil 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() notify := resp.GetNotify()
if notify == nil { if notify == nil {
return fmt.Errorf("invalid response: %s", resp.String()) return fmt.Errorf("invalid response: %s", resp.String())
@@ -1893,7 +1898,7 @@ func (c *linuxContainer) criuNotifications(resp *criurpc.CriuResp, process *Proc
return nil return nil
} }
func (c *linuxContainer) updateState(process parentProcess) (*State, error) { func (c *Container) updateState(process parentProcess) (*State, error) {
if process != nil { if process != nil {
c.initProcess = process c.initProcess = process
} }
@@ -1908,7 +1913,7 @@ func (c *linuxContainer) updateState(process parentProcess) (*State, error) {
return state, nil 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-") tmpFile, err := os.CreateTemp(c.root, "state-")
if err != nil { if err != nil {
return err return err
@@ -1934,7 +1939,7 @@ func (c *linuxContainer) saveState(s *State) (retErr error) {
return os.Rename(tmpFile.Name(), stateFilePath) return os.Rename(tmpFile.Name(), stateFilePath)
} }
func (c *linuxContainer) currentStatus() (Status, error) { func (c *Container) currentStatus() (Status, error) {
if err := c.refreshState(); err != nil { if err := c.refreshState(); err != nil {
return -1, err 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 // 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 // out of process we need to verify the container's status based on runtime
// information and not rely on our in process info. // information and not rely on our in process info.
func (c *linuxContainer) refreshState() error { func (c *Container) refreshState() error {
paused, err := c.isPaused() paused, err := c.isPaused()
if err != nil { if err != nil {
return err return err
@@ -1963,7 +1968,7 @@ func (c *linuxContainer) refreshState() error {
return c.state.transition(&stoppedState{c: c}) return c.state.transition(&stoppedState{c: c})
} }
func (c *linuxContainer) runType() Status { func (c *Container) runType() Status {
if c.initProcess == nil { if c.initProcess == nil {
return Stopped return Stopped
} }
@@ -1983,7 +1988,7 @@ func (c *linuxContainer) runType() Status {
return Running return Running
} }
func (c *linuxContainer) isPaused() (bool, error) { func (c *Container) isPaused() (bool, error) {
state, err := c.cgroupManager.GetFreezerState() state, err := c.cgroupManager.GetFreezerState()
if err != nil { if err != nil {
return false, err return false, err
@@ -1991,7 +1996,7 @@ func (c *linuxContainer) isPaused() (bool, error) {
return state == configs.Frozen, nil return state == configs.Frozen, nil
} }
func (c *linuxContainer) currentState() (*State, error) { func (c *Container) currentState() (*State, error) {
var ( var (
startTime uint64 startTime uint64
externalDescriptors []string externalDescriptors []string
@@ -2038,7 +2043,7 @@ func (c *linuxContainer) currentState() (*State, error) {
return state, nil return state, nil
} }
func (c *linuxContainer) currentOCIState() (*specs.State, error) { func (c *Container) currentOCIState() (*specs.State, error) {
bundle, annotations := utils.Annotations(c.config.Labels) bundle, annotations := utils.Annotations(c.config.Labels)
state := &specs.State{ state := &specs.State{
Version: specs.Version, 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 // orderNamespacePaths sorts namespace paths into a list of paths that we
// can setns in order. // 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{} paths := []string{}
for _, ns := range configs.NamespaceTypes() { 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 // such as one that uses nsenter package to bootstrap the container's
// init process correctly, i.e. with correct namespaces, uid/gid // init process correctly, i.e. with correct namespaces, uid/gid
// mapping etc. // 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 // create the netlink message
r := nl.NewNetlinkRequest(int(InitMsg), 0) r := nl.NewNetlinkRequest(int(InitMsg), 0)
+3 -3
View File
@@ -115,7 +115,7 @@ func TestGetContainerPids(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("can't stat pid %d, got %v", pid, err) t.Fatalf("can't stat pid %d, got %v", pid, err)
} }
container := &linuxContainer{ container := &Container{
id: "myid", id: "myid",
config: &configs.Config{}, config: &configs.Config{},
cgroupManager: &mockCgroupManager{ cgroupManager: &mockCgroupManager{
@@ -148,7 +148,7 @@ func TestGetContainerState(t *testing.T) {
expectedMemoryPath = "/sys/fs/cgroup/memory/myid" expectedMemoryPath = "/sys/fs/cgroup/memory/myid"
expectedNetworkPath = fmt.Sprintf("/proc/%d/ns/net", pid) expectedNetworkPath = fmt.Sprintf("/proc/%d/ns/net", pid)
) )
container := &linuxContainer{ container := &Container{
id: "myid", id: "myid",
config: &configs.Config{ config: &configs.Config{
Namespaces: []configs.Namespace{ Namespaces: []configs.Namespace{
@@ -232,7 +232,7 @@ func TestGetContainerStateAfterUpdate(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
container := &linuxContainer{ container := &Container{
root: t.TempDir(), root: t.TempDir(),
id: "myid", id: "myid",
config: &configs.Config{ config: &configs.Config{
+4 -4
View File
@@ -37,7 +37,7 @@ var idRegex = regexp.MustCompile(`^[\w+-\.]+$`)
// The id must not be empty and consist of only the following characters: // The id must not be empty and consist of only the following characters:
// ASCII letters, digits, underscore, plus, minus, period. The id must be // ASCII letters, digits, underscore, plus, minus, period. The id must be
// unique and non-existent for the given root path. // 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 == "" { if root == "" {
return nil, errors.New("root not set") 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 { if err := os.Mkdir(containerRoot, 0o711); err != nil {
return nil, err return nil, err
} }
c := &linuxContainer{ c := &Container{
id: id, id: id,
root: containerRoot, root: containerRoot,
config: config, 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 // 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 // container, and returns a Container object reconstructed from the saved
// state. This presents a read only view of the container. // 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 == "" { if root == "" {
return nil, errors.New("root not set") return nil, errors.New("root not set")
} }
@@ -137,7 +137,7 @@ func Load(root, id string) (Container, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
c := &linuxContainer{ c := &Container{
initProcess: r, initProcess: r,
initProcessStartTime: state.InitProcessStartTime, initProcessStartTime: state.InitProcessStartTime,
id: id, id: id,
+2 -6
View File
@@ -75,12 +75,8 @@ func TestFactoryLoadContainer(t *testing.T) {
if !reflect.DeepEqual(config.Hooks, expectedHooks) { if !reflect.DeepEqual(config.Hooks, expectedHooks) {
t.Fatalf("expects hooks %q but received %q", expectedHooks, config.Hooks) t.Fatalf("expects hooks %q but received %q", expectedHooks, config.Hooks)
} }
lcontainer, ok := container.(*linuxContainer) if container.initProcess.pid() != expectedState.InitProcessPid {
if !ok { t.Fatalf("expected init pid %d but received %d", expectedState.InitProcessPid, container.initProcess.pid())
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())
} }
} }
+2 -2
View File
@@ -165,7 +165,7 @@ func copyBusybox(dest string) error {
return nil 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) name := strings.ReplaceAll(t.Name(), "/", "_") + strconv.FormatInt(-int64(time.Now().Nanosecond()), 35)
root := t.TempDir() root := t.TempDir()
@@ -212,6 +212,6 @@ func runContainer(t *testing.T, config *configs.Config, args ...string) (buffers
return return
} }
func destroyContainer(container libcontainer.Container) { func destroyContainer(container *libcontainer.Container) {
_ = container.Destroy() _ = container.Destroy()
} }
+1 -1
View File
@@ -300,7 +300,7 @@ type initProcess struct {
config *initConfig config *initConfig
manager cgroups.Manager manager cgroups.Manager
intelRdtManager *intelrdt.Manager intelRdtManager *intelrdt.Manager
container *linuxContainer container *Container
fds []string fds []string
process *Process process *Process
bootstrapData io.Reader bootstrapData io.Reader
+8 -8
View File
@@ -35,7 +35,7 @@ type containerState interface {
status() Status status() Status
} }
func destroy(c *linuxContainer) error { func destroy(c *Container) error {
if !c.config.Namespaces.Contains(configs.NEWPID) || if !c.config.Namespaces.Contains(configs.NEWPID) ||
c.config.Namespaces.PathOf(configs.NEWPID) != "" { c.config.Namespaces.PathOf(configs.NEWPID) != "" {
if err := signalAllProcesses(c.cgroupManager, unix.SIGKILL); err != nil { if err := signalAllProcesses(c.cgroupManager, unix.SIGKILL); err != nil {
@@ -59,7 +59,7 @@ func destroy(c *linuxContainer) error {
return err return err
} }
func runPoststopHooks(c *linuxContainer) error { func runPoststopHooks(c *Container) error {
hooks := c.config.Hooks hooks := c.config.Hooks
if hooks == nil { if hooks == nil {
return nil return nil
@@ -80,7 +80,7 @@ func runPoststopHooks(c *linuxContainer) error {
// stoppedState represents a container is a stopped/destroyed state. // stoppedState represents a container is a stopped/destroyed state.
type stoppedState struct { type stoppedState struct {
c *linuxContainer c *Container
} }
func (b *stoppedState) status() Status { func (b *stoppedState) status() Status {
@@ -104,7 +104,7 @@ func (b *stoppedState) destroy() error {
// runningState represents a container that is currently running. // runningState represents a container that is currently running.
type runningState struct { type runningState struct {
c *linuxContainer c *Container
} }
func (r *runningState) status() Status { func (r *runningState) status() Status {
@@ -136,7 +136,7 @@ func (r *runningState) destroy() error {
} }
type createdState struct { type createdState struct {
c *linuxContainer c *Container
} }
func (i *createdState) status() Status { 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 // pausedState represents a container that is currently pause. It cannot be destroyed in a
// paused state and must transition back to running first. // paused state and must transition back to running first.
type pausedState struct { type pausedState struct {
c *linuxContainer c *Container
} }
func (p *pausedState) status() Status { 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. // information that maybe need destroyed when the container is stopped and destroy is called.
type restoredState struct { type restoredState struct {
imageDir string imageDir string
c *linuxContainer c *Container
} }
func (r *restoredState) status() Status { 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 // loadedState is used whenever a container is restored, loaded, or setting additional
// processes inside and it should not be destroyed when it is exiting. // processes inside and it should not be destroyed when it is exiting.
type loadedState struct { type loadedState struct {
c *linuxContainer c *Container
s Status s Status
} }
+5 -5
View File
@@ -53,7 +53,7 @@ func testTransitions(t *testing.T, initialState containerState, valid []containe
func TestStoppedStateTransition(t *testing.T) { func TestStoppedStateTransition(t *testing.T) {
testTransitions( testTransitions(
t, t,
&stoppedState{c: &linuxContainer{}}, &stoppedState{c: &Container{}},
[]containerState{ []containerState{
&stoppedState{}, &stoppedState{},
&runningState{}, &runningState{},
@@ -65,7 +65,7 @@ func TestStoppedStateTransition(t *testing.T) {
func TestPausedStateTransition(t *testing.T) { func TestPausedStateTransition(t *testing.T) {
testTransitions( testTransitions(
t, t,
&pausedState{c: &linuxContainer{}}, &pausedState{c: &Container{}},
[]containerState{ []containerState{
&pausedState{}, &pausedState{},
&runningState{}, &runningState{},
@@ -77,7 +77,7 @@ func TestPausedStateTransition(t *testing.T) {
func TestRestoredStateTransition(t *testing.T) { func TestRestoredStateTransition(t *testing.T) {
testTransitions( testTransitions(
t, t,
&restoredState{c: &linuxContainer{}}, &restoredState{c: &Container{}},
[]containerState{ []containerState{
&stoppedState{}, &stoppedState{},
&runningState{}, &runningState{},
@@ -88,7 +88,7 @@ func TestRestoredStateTransition(t *testing.T) {
func TestRunningStateTransition(t *testing.T) { func TestRunningStateTransition(t *testing.T) {
testTransitions( testTransitions(
t, t,
&runningState{c: &linuxContainer{}}, &runningState{c: &Container{}},
[]containerState{ []containerState{
&stoppedState{}, &stoppedState{},
&pausedState{}, &pausedState{},
@@ -100,7 +100,7 @@ func TestRunningStateTransition(t *testing.T) {
func TestCreatedStateTransition(t *testing.T) { func TestCreatedStateTransition(t *testing.T) {
testTransitions( testTransitions(
t, t,
&createdState{c: &linuxContainer{}}, &createdState{c: &Container{}},
[]containerState{ []containerState{
&stoppedState{}, &stoppedState{},
&pausedState{}, &pausedState{},
+1 -1
View File
@@ -91,7 +91,7 @@ func notifySocketStart(context *cli.Context, notifySocketHost, id string) (*noti
return notifySocket, nil return notifySocket, nil
} }
func (s *notifySocket) waitForContainer(container libcontainer.Container) error { func (s *notifySocket) waitForContainer(container *libcontainer.Container) error {
state, err := container.State() state, err := container.State()
if err != nil { if err != nil {
return err return err
+4 -4
View File
@@ -25,7 +25,7 @@ var errEmptyID = errors.New("container id cannot be empty")
// getContainer returns the specified container instance by loading it from // getContainer returns the specified container instance by loading it from
// a state directory (root). // a state directory (root).
func getContainer(context *cli.Context) (libcontainer.Container, error) { func getContainer(context *cli.Context) (*libcontainer.Container, error) {
id := context.Args().First() id := context.Args().First()
if id == "" { if id == "" {
return nil, errEmptyID return nil, errEmptyID
@@ -82,7 +82,7 @@ func newProcess(p specs.Process) (*libcontainer.Process, error) {
return lp, nil return lp, nil
} }
func destroy(container libcontainer.Container) { func destroy(container *libcontainer.Container) {
if err := container.Destroy(); err != nil { if err := container.Destroy(); err != nil {
logrus.Error(err) logrus.Error(err)
} }
@@ -162,7 +162,7 @@ func createPidFile(path string, process *libcontainer.Process) error {
return os.Rename(tmpName, path) 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) rootlessCg, err := shouldUseRootlessCgroupManager(context)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -193,7 +193,7 @@ type runner struct {
preserveFDs int preserveFDs int
pidFile string pidFile string
consoleSocket string consoleSocket string
container libcontainer.Container container *libcontainer.Container
action CtAct action CtAct
notifySocket *notifySocket notifySocket *notifySocket
criuOpts *libcontainer.CriuOpts criuOpts *libcontainer.CriuOpts