Multi Container: Add IsRunning() control message for the container.

- Adds IsRunning() control message which returns true if the container is
running.
- Adds a test to verify the result of IsRunning().

PiperOrigin-RevId: 461759652
This commit is contained in:
Nayana Bidari
2022-07-18 18:04:54 -07:00
committed by gVisor bot
parent ee0e725b60
commit 4bc44eac9b
+102 -11
View File
@@ -46,9 +46,35 @@ type Lifecycle struct {
// namespaces.
MountNamespacesMap map[string]*vfs.MountNamespace
// containerInitProcessMap is a map of container ID and the init(PID 1)
// threadgroup of the container.
containerInitProcessMap map[string]*kernel.ThreadGroup
// containerMap is a map of the container id and the container.
containerMap map[string]*Container
}
// containerState is the state of the container.
type containerState int
const (
// stateCreated is the state when the container was created. It is the
// initial state.
stateCreated containerState = iota
// stateRunning is the state when the container/application is running.
stateRunning
// stateStopped is the state when the container has exited.
stateStopped
)
// Container contains the set of parameters to represent a container.
type Container struct {
// containerID.
containerID string
// tg is the init(PID 1) threadgroup of the container.
tg *kernel.ThreadGroup
// state is the current state of the container.
state containerState
}
// StartContainerArgs is the set of arguments to start a container.
@@ -104,6 +130,38 @@ func (args StartContainerArgs) String() string {
return strings.Join(a, " ")
}
func (l *Lifecycle) updateContainerState(containerID string, newState containerState) error {
l.mu.Lock()
defer l.mu.Unlock()
c, ok := l.containerMap[containerID]
if !ok {
return fmt.Errorf("container %v not started", containerID)
}
switch newState {
case stateCreated:
// Impossible.
panic(fmt.Sprintf("invalid state transition: %v => %v", c.state, newState))
case stateRunning:
if c.state != stateCreated {
// Impossible.
panic(fmt.Sprintf("invalid state transition: %v => %v", c.state, newState))
}
case stateStopped:
// Valid state transition.
default:
// Invalid new state.
panic(fmt.Sprintf("invalid new state: %v", newState))
}
c.state = newState
return nil
}
// StartContainer will start a new container in the sandbox.
func (l *Lifecycle) StartContainer(args *StartContainerArgs, _ *uint32) error {
// Import file descriptors.
@@ -177,16 +235,24 @@ func (l *Lifecycle) StartContainer(args *StartContainerArgs, _ *uint32) error {
return err
}
c := &Container{
containerID: initArgs.ContainerID,
tg: tg,
state: stateCreated,
}
l.mu.Lock()
if l.containerMap == nil {
l.containerMap = make(map[string]*Container)
}
l.containerMap[initArgs.ContainerID] = c
l.mu.Unlock()
// Start the newly created process.
l.Kernel.StartProcess(tg)
log.Infof("Started the new container %v ", initArgs.ContainerID)
l.mu.Lock()
if l.containerInitProcessMap == nil {
l.containerInitProcessMap = make(map[string]*kernel.ThreadGroup)
}
l.containerInitProcessMap[initArgs.ContainerID] = tg
l.mu.Unlock()
l.updateContainerState(initArgs.ContainerID, stateRunning)
return nil
}
@@ -212,11 +278,11 @@ func (l *Lifecycle) getInitContainerProcess(containerID string) (*kernel.ThreadG
l.mu.Lock()
defer l.mu.Unlock()
tg, ok := l.containerInitProcessMap[containerID]
c, ok := l.containerMap[containerID]
if !ok {
return nil, fmt.Errorf("container %v not started", containerID)
}
return tg, nil
return c.tg, nil
}
// ContainerArgs is the set of arguments for container related APIs after
@@ -235,5 +301,30 @@ func (l *Lifecycle) WaitContainer(args *ContainerArgs, waitStatus *uint32) error
tg.WaitExited()
*waitStatus = uint32(tg.ExitStatus())
if err := l.updateContainerState(args.ContainerID, stateStopped); err != nil {
return err
}
return nil
}
// IsContainerRunning returns true if the container is running.
func (l *Lifecycle) IsContainerRunning(args *ContainerArgs, isRunning *bool) error {
l.mu.Lock()
defer l.mu.Unlock()
c, ok := l.containerMap[args.ContainerID]
if !ok || c.state != stateRunning {
return nil
}
// The state will not be updated to "stopped" if the
// WaitContainer(...) method is not called. In this case, check
// whether the number of non-exited tasks in tg is zero to get
// the correct state of the container.
if c.tg.Count() == 0 {
c.state = stateStopped
return nil
}
*isRunning = true
return nil
}