Allow sandbox to start without any tasks.

This is the first set of changes to allow multiple containers in a sandbox.
- Changes to allow kernel.Start() without any tasks.
- New control message to StartContainer() in root namespaces.
- Added new function StartSandbox() to keep the existing behavior separate from
when the multi-containers is enabled.
- Test to verify the new control message with one container.

PiperOrigin-RevId: 446792577
This commit is contained in:
Nayana Bidari 2022-05-05 12:51:33 -07:00 committed by gVisor bot
parent bb36c43e97
commit 0c54ff1ffe
2 changed files with 146 additions and 3 deletions

View File

@ -15,12 +15,152 @@
package control
import (
"fmt"
"strings"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/fd"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/fs/user"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/limits"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/urpc"
)
// Lifecycle provides functions related to starting and stopping tasks.
type Lifecycle struct {
// Kernel is the kernel where the tasks belong to.
Kernel *kernel.Kernel
// Sends a message to the sentry that the task has been started.
StartedCh chan struct{}
// TODO(b/202052732): Root mount namespace. When running multiple
// containers, create the mount namespace using the mount spec in
// the StartContainerArgs.
MountNamespaceVFS2 *vfs.MountNamespace
}
// StartContainerArgs is the set of arguments to start a container.
type StartContainerArgs struct {
// Filename is the filename to load.
//
// If this is provided as "", then the file will be guessed via Argv[0].
Filename string `json:"filename"`
// Argv is a list of arguments.
Argv []string `json:"argv"`
// Envv is a list of environment variables.
Envv []string `json:"envv"`
// WorkingDirectory defines the working directory for the new process.
WorkingDirectory string `json:"wd"`
// KUID is the UID to run with in the root user namespace. Defaults to
// root if not set explicitly.
KUID auth.KUID `json:"KUID"`
// KGID is the GID to run with in the root user namespace. Defaults to
// the root group if not set explicitly.
KGID auth.KGID `json:"KGID"`
// ExtraKGIDs is the list of additional groups to which the user belongs.
ExtraKGIDs []auth.KGID `json:"extraKGID"`
// Capabilities is the list of capabilities to give to the process.
Capabilities *auth.TaskCapabilities `json:"capabilities"`
// FilePayload determines the files to give to the new process.
urpc.FilePayload
// ContainerID is the container for the process being executed.
ContainerID string `json:"containerID"`
// Limits is the limit set for the process being executed.
Limits *limits.LimitSet `json:"limits"`
}
// String prints the StartContainerArgs.argv as a string.
func (args StartContainerArgs) String() string {
if len(args.Argv) == 0 {
return args.Filename
}
a := make([]string, len(args.Argv))
copy(a, args.Argv)
if args.Filename != "" {
a[0] = args.Filename
}
return strings.Join(a, " ")
}
// StartContainer will start a new container in the sandbox.
func (l *Lifecycle) StartContainer(args *StartContainerArgs, _ *uint32) error {
// Import file descriptors.
fdTable := l.Kernel.NewFDTable()
creds := auth.NewUserCredentials(
args.KUID,
args.KGID,
args.ExtraKGIDs,
args.Capabilities,
l.Kernel.RootUserNamespace())
limitSet := args.Limits
if limitSet == nil {
limitSet = limits.NewLimitSet()
}
initArgs := kernel.CreateProcessArgs{
Filename: args.Filename,
Argv: args.Argv,
Envv: args.Envv,
WorkingDirectory: args.WorkingDirectory,
Credentials: creds,
FDTable: fdTable,
Umask: 0022,
Limits: limitSet,
MaxSymlinkTraversals: linux.MaxSymlinkTraversals,
UTSNamespace: l.Kernel.RootUTSNamespace(),
IPCNamespace: l.Kernel.RootIPCNamespace(),
AbstractSocketNamespace: l.Kernel.RootAbstractSocketNamespace(),
ContainerID: args.ContainerID,
PIDNamespace: l.Kernel.RootPIDNamespace(),
MountNamespaceVFS2: l.MountNamespaceVFS2,
}
ctx := initArgs.NewContext(l.Kernel)
defer fdTable.DecRef(ctx)
resolved, err := user.ResolveExecutablePath(ctx, &initArgs)
if err != nil {
return err
}
initArgs.Filename = resolved
fds, err := fd.NewFromFiles(args.Files)
if err != nil {
return fmt.Errorf("duplicating payload files: %w", err)
}
defer func() {
for _, fd := range fds {
_ = fd.Close()
}
}()
tg, _, err := l.Kernel.CreateProcess(initArgs)
if err != nil {
return err
}
// Start the newly created process.
l.Kernel.StartProcess(tg)
log.Infof("Started the new container")
l.StartedCh <- struct{}{}
return nil
}
// Pause pauses all tasks, blocking until they are stopped.

View File

@ -950,6 +950,9 @@ func (k *Kernel) CreateProcess(args CreateProcessArgs) (*ThreadGroup, ThreadID,
if VFS2Enabled {
mntnsVFS2 = args.MountNamespaceVFS2
if mntnsVFS2 == nil {
if k.globalInit == nil {
return nil, 0, fmt.Errorf("mount namespace is nil")
}
// Add a reference to the namespace, which is transferred to the new process.
mntnsVFS2 = k.globalInit.Leader().MountNamespaceVFS2()
mntnsVFS2.IncRef()
@ -983,6 +986,9 @@ func (k *Kernel) CreateProcess(args CreateProcessArgs) (*ThreadGroup, ThreadID,
} else {
mntns = args.MountNamespace
if mntns == nil {
if k.globalInit == nil {
return nil, 0, fmt.Errorf("mount namespace is nil")
}
mntns = k.GlobalInit().Leader().MountNamespace()
mntns.IncRef()
}
@ -1100,9 +1106,6 @@ func (k *Kernel) Start() error {
k.extMu.Lock()
defer k.extMu.Unlock()
if k.globalInit == nil {
return fmt.Errorf("kernel contains no tasks")
}
if k.started {
return fmt.Errorf("kernel already started")
}