From bdb19b82ef2aa1638d98da4b1c55ae7928437f55 Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Tue, 18 Jun 2019 14:45:50 -0700 Subject: [PATCH] Add Container/Sandbox args struct for creation There were 3 string arguments that could be easily misplaced and it makes it easier to add new arguments, especially for Container that has dozens of callers. PiperOrigin-RevId: 253872074 --- runsc/cmd/capability_test.go | 7 +- runsc/cmd/checkpoint.go | 7 +- runsc/cmd/create.go | 10 +- runsc/cmd/do.go | 7 +- runsc/cmd/restore.go | 10 +- runsc/cmd/run.go | 10 +- runsc/container/console_test.go | 29 ++- runsc/container/container.go | 86 ++++++--- runsc/container/container_test.go | 225 +++++++++++++++++++----- runsc/container/multi_container_test.go | 56 +++++- runsc/container/shared_volume_test.go | 14 +- runsc/sandbox/sandbox.go | 68 +++++-- 12 files changed, 416 insertions(+), 113 deletions(-) diff --git a/runsc/cmd/capability_test.go b/runsc/cmd/capability_test.go index 79863efa3..3ae25a257 100644 --- a/runsc/cmd/capability_test.go +++ b/runsc/cmd/capability_test.go @@ -97,7 +97,12 @@ func TestCapabilities(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - c, err := container.Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + args := container.Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + c, err := container.New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } diff --git a/runsc/cmd/checkpoint.go b/runsc/cmd/checkpoint.go index 7298a0828..d8b3a8573 100644 --- a/runsc/cmd/checkpoint.go +++ b/runsc/cmd/checkpoint.go @@ -133,7 +133,12 @@ func (c *Checkpoint) Execute(_ context.Context, f *flag.FlagSet, args ...interfa Fatalf("destroying container: %v", err) } - cont, err = container.Create(id, spec, conf, bundleDir, "", "", "") + contArgs := container.Args{ + ID: id, + Spec: spec, + BundleDir: bundleDir, + } + cont, err = container.New(conf, contArgs) if err != nil { Fatalf("restoring container: %v", err) } diff --git a/runsc/cmd/create.go b/runsc/cmd/create.go index 42663c05c..a4e3071b3 100644 --- a/runsc/cmd/create.go +++ b/runsc/cmd/create.go @@ -99,7 +99,15 @@ func (c *Create) Execute(_ context.Context, f *flag.FlagSet, args ...interface{} // Create the container. A new sandbox will be created for the // container unless the metadata specifies that it should be run in an // existing container. - if _, err := container.Create(id, spec, conf, bundleDir, c.consoleSocket, c.pidFile, c.userLog); err != nil { + contArgs := container.Args{ + ID: id, + Spec: spec, + BundleDir: bundleDir, + ConsoleSocket: c.consoleSocket, + PIDFile: c.pidFile, + UserLog: c.userLog, + } + if _, err := container.New(conf, contArgs); err != nil { return Errorf("creating container: %v", err) } return subcommands.ExitSuccess diff --git a/runsc/cmd/do.go b/runsc/cmd/do.go index 876e674c4..16d135b51 100644 --- a/runsc/cmd/do.go +++ b/runsc/cmd/do.go @@ -164,7 +164,12 @@ func (c *Do) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) su return Errorf("Error write spec: %v", err) } - ws, err := container.Run(cid, spec, conf, tmpDir, "", "", "", false) + runArgs := container.Args{ + ID: cid, + Spec: spec, + BundleDir: tmpDir, + } + ws, err := container.Run(conf, runArgs, false) if err != nil { return Errorf("running container: %v", err) } diff --git a/runsc/cmd/restore.go b/runsc/cmd/restore.go index a5124697d..e18910325 100644 --- a/runsc/cmd/restore.go +++ b/runsc/cmd/restore.go @@ -100,7 +100,15 @@ func (r *Restore) Execute(_ context.Context, f *flag.FlagSet, args ...interface{ conf.RestoreFile = filepath.Join(r.imagePath, checkpointFileName) - ws, err := container.Run(id, spec, conf, bundleDir, r.consoleSocket, r.pidFile, r.userLog, r.detach) + runArgs := container.Args{ + ID: id, + Spec: spec, + BundleDir: bundleDir, + ConsoleSocket: r.consoleSocket, + PIDFile: r.pidFile, + UserLog: r.userLog, + } + ws, err := container.Run(conf, runArgs, r.detach) if err != nil { return Errorf("running container: %v", err) } diff --git a/runsc/cmd/run.go b/runsc/cmd/run.go index c1734741d..ee14dc3d9 100644 --- a/runsc/cmd/run.go +++ b/runsc/cmd/run.go @@ -81,7 +81,15 @@ func (r *Run) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) s } specutils.LogSpec(spec) - ws, err := container.Run(id, spec, conf, bundleDir, r.consoleSocket, r.pidFile, r.userLog, r.detach) + runArgs := container.Args{ + ID: id, + Spec: spec, + BundleDir: bundleDir, + ConsoleSocket: r.consoleSocket, + PIDFile: r.pidFile, + UserLog: r.userLog, + } + ws, err := container.Run(conf, runArgs, r.detach) if err != nil { return Errorf("running container: %v", err) } diff --git a/runsc/container/console_test.go b/runsc/container/console_test.go index e3ca3d387..e9372989f 100644 --- a/runsc/container/console_test.go +++ b/runsc/container/console_test.go @@ -138,8 +138,13 @@ func TestConsoleSocket(t *testing.T) { defer cleanup() // Create the container and pass the socket name. - id := testutil.UniqueContainerID() - c, err := Create(id, spec, conf, bundleDir, sock, "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + ConsoleSocket: sock, + } + c, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -167,7 +172,12 @@ func TestJobControlSignalExec(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + c, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -186,7 +196,7 @@ func TestJobControlSignalExec(t *testing.T) { defer ptySlave.Close() // Exec bash and attach a terminal. - args := &control.ExecArgs{ + execArgs := &control.ExecArgs{ Filename: "/bin/bash", // Don't let bash execute from profile or rc files, otherwise // our PID counts get messed up. @@ -198,7 +208,7 @@ func TestJobControlSignalExec(t *testing.T) { StdioIsPty: true, } - pid, err := c.Execute(args) + pid, err := c.Execute(execArgs) if err != nil { t.Fatalf("error executing: %v", err) } @@ -296,8 +306,13 @@ func TestJobControlSignalRootContainer(t *testing.T) { defer cleanup() // Create the container and pass the socket name. - id := testutil.UniqueContainerID() - c, err := Create(id, spec, conf, bundleDir, sock, "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + ConsoleSocket: sock, + } + c, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } diff --git a/runsc/container/container.go b/runsc/container/container.go index e67f99742..3a358224c 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -242,16 +242,39 @@ func List(rootDir string) ([]string, error) { return out, nil } +// Args is used to configure a new container. +type Args struct { + // ID is the container unique identifier. + ID string + + // Spec is the OCI spec that describes the container. + Spec *specs.Spec + + // BundleDir is the directory containing the container bundle. + BundleDir string + + // ConsoleSocket is the path to a unix domain socket that will receive + // the console FD. It may be empty. + ConsoleSocket string + + // PIDFile is the filename where the container's root process PID will be + // written to. It may be empty. + PIDFile string + + // UserLog is the filename to send user-visible logs to. It may be empty. + UserLog string +} + // Create creates the container in a new Sandbox process, unless the metadata // indicates that an existing Sandbox should be used. The caller must call // Destroy() on the container. -func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, pidFile, userLog string) (*Container, error) { - log.Debugf("Create container %q in root dir: %s", id, conf.RootDir) - if err := validateID(id); err != nil { +func New(conf *boot.Config, args Args) (*Container, error) { + log.Debugf("Create container %q in root dir: %s", args.ID, conf.RootDir) + if err := validateID(args.ID); err != nil { return nil, err } - unlockRoot, err := maybeLockRootContainer(spec, conf.RootDir) + unlockRoot, err := maybeLockRootContainer(args.Spec, conf.RootDir) if err != nil { return nil, err } @@ -259,7 +282,7 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo // Lock the container metadata file to prevent concurrent creations of // containers with the same id. - containerRoot := filepath.Join(conf.RootDir, id) + containerRoot := filepath.Join(conf.RootDir, args.ID) unlock, err := lockContainerMetadata(containerRoot) if err != nil { return nil, err @@ -269,16 +292,16 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo // Check if the container already exists by looking for the metadata // file. if _, err := os.Stat(filepath.Join(containerRoot, metadataFilename)); err == nil { - return nil, fmt.Errorf("container with id %q already exists", id) + return nil, fmt.Errorf("container with id %q already exists", args.ID) } else if !os.IsNotExist(err) { return nil, fmt.Errorf("looking for existing container in %q: %v", containerRoot, err) } c := &Container{ - ID: id, - Spec: spec, - ConsoleSocket: consoleSocket, - BundleDir: bundleDir, + ID: args.ID, + Spec: args.Spec, + ConsoleSocket: args.ConsoleSocket, + BundleDir: args.BundleDir, Root: containerRoot, Status: Creating, CreatedAt: time.Now(), @@ -294,31 +317,46 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo // started in an existing sandbox, we must do so. The metadata will // indicate the ID of the sandbox, which is the same as the ID of the // init container in the sandbox. - if isRoot(spec) { - log.Debugf("Creating new sandbox for container %q", id) + if isRoot(args.Spec) { + log.Debugf("Creating new sandbox for container %q", args.ID) // Create and join cgroup before processes are created to ensure they are // part of the cgroup from the start (and all tneir children processes). - cg, err := cgroup.New(spec) + cg, err := cgroup.New(args.Spec) if err != nil { return nil, err } if cg != nil { // If there is cgroup config, install it before creating sandbox process. - if err := cg.Install(spec.Linux.Resources); err != nil { + if err := cg.Install(args.Spec.Linux.Resources); err != nil { return nil, fmt.Errorf("configuring cgroup: %v", err) } } if err := runInCgroup(cg, func() error { - ioFiles, specFile, err := c.createGoferProcess(spec, conf, bundleDir) + ioFiles, specFile, err := c.createGoferProcess(args.Spec, conf, args.BundleDir) if err != nil { return err } // Start a new sandbox for this container. Any errors after this point // must destroy the container. - c.Sandbox, err = sandbox.New(id, spec, conf, bundleDir, consoleSocket, userLog, ioFiles, specFile, cg) - return err + sandArgs := &sandbox.Args{ + ID: args.ID, + Spec: args.Spec, + BundleDir: args.BundleDir, + ConsoleSocket: args.ConsoleSocket, + UserLog: args.UserLog, + IOFiles: ioFiles, + MountsFile: specFile, + Cgroup: cg, + } + sand, err := sandbox.New(conf, sandArgs) + if err != nil { + return err + } + c.Sandbox = sand + return nil + }); err != nil { return nil, err } @@ -331,7 +369,7 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo // * A container struct whose sandbox ID is equal to the above // container/sandbox ID, but that has a different container // ID. This is the child container. - sbid, ok := specutils.SandboxID(spec) + sbid, ok := specutils.SandboxID(args.Spec) if !ok { return nil, fmt.Errorf("no sandbox ID found when creating container") } @@ -356,8 +394,8 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo // Write the PID file. Containerd considers the create complete after // this file is created, so it must be the last thing we do. - if pidFile != "" { - if err := ioutil.WriteFile(pidFile, []byte(strconv.Itoa(c.SandboxPid())), 0644); err != nil { + if args.PIDFile != "" { + if err := ioutil.WriteFile(args.PIDFile, []byte(strconv.Itoa(c.SandboxPid())), 0644); err != nil { return nil, fmt.Errorf("error writing PID file: %v", err) } } @@ -461,9 +499,9 @@ func (c *Container) Restore(spec *specs.Spec, conf *boot.Config, restoreFile str } // Run is a helper that calls Create + Start + Wait. -func Run(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, pidFile, userLog string, detach bool) (syscall.WaitStatus, error) { - log.Debugf("Run container %q in root dir: %s", id, conf.RootDir) - c, err := Create(id, spec, conf, bundleDir, consoleSocket, pidFile, userLog) +func Run(conf *boot.Config, args Args, detach bool) (syscall.WaitStatus, error) { + log.Debugf("Run container %q in root dir: %s", args.ID, conf.RootDir) + c, err := New(conf, args) if err != nil { return 0, fmt.Errorf("creating container: %v", err) } @@ -476,7 +514,7 @@ func Run(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocke if conf.RestoreFile != "" { log.Debugf("Restore: %v", conf.RestoreFile) - if err := c.Restore(spec, conf, conf.RestoreFile); err != nil { + if err := c.Restore(args.Spec, conf, conf.RestoreFile); err != nil { return 0, fmt.Errorf("starting container: %v", err) } } else { diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index 0e3a736b3..610dd1bf8 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -211,7 +211,12 @@ func run(spec *specs.Spec, conf *boot.Config) error { defer os.RemoveAll(bundleDir) // Create, start and wait for the container. - ws, err := Run(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "", false) + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + ws, err := Run(conf, args, false) if err != nil { return fmt.Errorf("running container: %v", err) } @@ -295,15 +300,19 @@ func TestLifecycle(t *testing.T) { }, } // Create the container. - id := testutil.UniqueContainerID() - c, err := Create(id, spec, conf, bundleDir, "", "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + c, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } defer c.Destroy() // Load the container from disk and check the status. - c, err = Load(rootDir, id) + c, err = Load(rootDir, args.ID) if err != nil { t.Fatalf("error loading container: %v", err) } @@ -316,7 +325,7 @@ func TestLifecycle(t *testing.T) { if err != nil { t.Fatalf("error listing containers: %v", err) } - if got, want := ids, []string{id}; !reflect.DeepEqual(got, want) { + if got, want := ids, []string{args.ID}; !reflect.DeepEqual(got, want) { t.Errorf("container list got %v, want %v", got, want) } @@ -326,7 +335,7 @@ func TestLifecycle(t *testing.T) { } // Load the container from disk and check the status. - c, err = Load(rootDir, id) + c, err = Load(rootDir, args.ID) if err != nil { t.Fatalf("error loading container: %v", err) } @@ -367,7 +376,7 @@ func TestLifecycle(t *testing.T) { wg.Wait() // Load the container from disk and check the status. - c, err = Load(rootDir, id) + c, err = Load(rootDir, args.ID) if err != nil { t.Fatalf("error loading container: %v", err) } @@ -390,7 +399,7 @@ func TestLifecycle(t *testing.T) { } // Loading the container by id should fail. - if _, err = Load(rootDir, id); err == nil { + if _, err = Load(rootDir, args.ID); err == nil { t.Errorf("expected loading destroyed container to fail, but it did not") } } @@ -417,7 +426,12 @@ func TestExePath(t *testing.T) { t.Fatalf("exec: %s, error setting up container: %v", test.path, err) } - ws, err := Run(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "", false) + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + ws, err := Run(conf, args, false) os.RemoveAll(rootDir) os.RemoveAll(bundleDir) @@ -450,7 +464,12 @@ func TestAppExitStatus(t *testing.T) { defer os.RemoveAll(rootDir) defer os.RemoveAll(bundleDir) - ws, err := Run(testutil.UniqueContainerID(), succSpec, conf, bundleDir, "", "", "", false) + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: succSpec, + BundleDir: bundleDir, + } + ws, err := Run(conf, args, false) if err != nil { t.Fatalf("error running container: %v", err) } @@ -469,7 +488,12 @@ func TestAppExitStatus(t *testing.T) { defer os.RemoveAll(rootDir2) defer os.RemoveAll(bundleDir2) - ws, err = Run(testutil.UniqueContainerID(), errSpec, conf, bundleDir2, "", "", "", false) + args2 := Args{ + ID: testutil.UniqueContainerID(), + Spec: errSpec, + BundleDir: bundleDir2, + } + ws, err = Run(conf, args2, false) if err != nil { t.Fatalf("error running container: %v", err) } @@ -494,7 +518,12 @@ func TestExec(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + cont, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -526,7 +555,7 @@ func TestExec(t *testing.T) { t.Error(err) } - args := &control.ExecArgs{ + execArgs := &control.ExecArgs{ Filename: "/bin/sleep", Argv: []string{"/bin/sleep", "5"}, WorkingDirectory: "/", @@ -537,7 +566,7 @@ func TestExec(t *testing.T) { // First, start running exec (whick blocks). status := make(chan error, 1) go func() { - exitStatus, err := cont.executeSync(args) + exitStatus, err := cont.executeSync(execArgs) if err != nil { log.Debugf("error executing: %v", err) status <- err @@ -585,7 +614,12 @@ func TestKillPid(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + cont, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -656,7 +690,12 @@ func TestCheckpointRestore(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + cont, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -702,7 +741,12 @@ func TestCheckpointRestore(t *testing.T) { defer outputFile2.Close() // Restore into a new container. - cont2, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + args2 := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + cont2, err := New(conf, args2) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -741,7 +785,12 @@ func TestCheckpointRestore(t *testing.T) { defer outputFile3.Close() // Restore into a new container. - cont3, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + args3 := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + cont3, err := New(conf, args3) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -820,7 +869,12 @@ func TestUnixDomainSockets(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + cont, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -867,7 +921,12 @@ func TestUnixDomainSockets(t *testing.T) { defer outputFile2.Close() // Restore into a new container. - contRestore, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + argsRestore := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + contRestore, err := New(conf, argsRestore) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -921,7 +980,12 @@ func TestPauseResume(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + cont, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -949,7 +1013,7 @@ func TestPauseResume(t *testing.T) { } script := fmt.Sprintf("while [[ -f %q ]]; do sleep 0.1; done", lock.Name()) - args := &control.ExecArgs{ + execArgs := &control.ExecArgs{ Filename: "/bin/bash", Argv: []string{"bash", "-c", script}, WorkingDirectory: "/", @@ -957,7 +1021,7 @@ func TestPauseResume(t *testing.T) { } // First, start running exec. - _, err = cont.Execute(args) + _, err = cont.Execute(execArgs) if err != nil { t.Fatalf("error executing: %v", err) } @@ -1026,7 +1090,12 @@ func TestPauseResumeStatus(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + cont, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -1090,7 +1159,12 @@ func TestCapabilities(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + cont, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -1132,7 +1206,7 @@ func TestCapabilities(t *testing.T) { // Need to traverse the intermediate directory. os.Chmod(rootDir, 0755) - args := &control.ExecArgs{ + execArgs := &control.ExecArgs{ Filename: exePath, Argv: []string{exePath}, WorkingDirectory: "/", @@ -1142,16 +1216,16 @@ func TestCapabilities(t *testing.T) { } // "exe" should fail because we don't have the necessary permissions. - if _, err := cont.executeSync(args); err == nil { + if _, err := cont.executeSync(execArgs); err == nil { t.Fatalf("container executed without error, but an error was expected") } // Now we run with the capability enabled and should succeed. - args.Capabilities = &auth.TaskCapabilities{ + execArgs.Capabilities = &auth.TaskCapabilities{ EffectiveCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE), } // "exe" should not fail this time. - if _, err := cont.executeSync(args); err != nil { + if _, err := cont.executeSync(execArgs); err != nil { t.Fatalf("container failed to exec %v: %v", args, err) } } @@ -1232,7 +1306,12 @@ func TestReadonlyRoot(t *testing.T) { defer os.RemoveAll(bundleDir) // Create, start and wait for the container. - c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + c, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -1300,7 +1379,12 @@ func TestUIDMap(t *testing.T) { defer os.RemoveAll(bundleDir) // Create, start and wait for the container. - c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + c, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -1352,7 +1436,12 @@ func TestReadonlyMount(t *testing.T) { defer os.RemoveAll(bundleDir) // Create, start and wait for the container. - c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + c, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -1396,7 +1485,12 @@ func TestAbbreviatedIDs(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - cont, err := Create(cid, spec, conf, bundleDir, "", "", "") + args := Args{ + ID: cid, + Spec: spec, + BundleDir: bundleDir, + } + cont, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -1441,7 +1535,12 @@ func TestGoferExits(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + c, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -1520,7 +1619,13 @@ func TestUserLog(t *testing.T) { userLog := filepath.Join(dir, "user.log") // Create, start and wait for the container. - ws, err := Run(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", userLog, false) + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + UserLog: userLog, + } + ws, err := Run(conf, args, false) if err != nil { t.Fatalf("error running container: %v", err) } @@ -1554,7 +1659,12 @@ func TestWaitOnExitedSandbox(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and Start the container. - c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + c, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -1597,7 +1707,12 @@ func TestDestroyNotStarted(t *testing.T) { defer os.RemoveAll(bundleDir) // Create the container and check that it can be destroyed. - c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + c, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -1619,15 +1734,19 @@ func TestDestroyStarting(t *testing.T) { defer os.RemoveAll(bundleDir) // Create the container and check that it can be destroyed. - id := testutil.UniqueContainerID() - c, err := Create(id, spec, conf, bundleDir, "", "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + c, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } // Container is not thread safe, so load another instance to run in // concurrently. - startCont, err := Load(rootDir, id) + startCont, err := Load(rootDir, args.ID) if err != nil { t.Fatalf("error loading container: %v", err) } @@ -1732,7 +1851,12 @@ func TestMountPropagation(t *testing.T) { defer os.RemoveAll(rootDir) defer os.RemoveAll(bundleDir) - cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + cont, err := New(conf, args) if err != nil { t.Fatalf("creating container: %v", err) } @@ -1750,21 +1874,21 @@ func TestMountPropagation(t *testing.T) { // Check that mount didn't propagate to private mount. privFile := filepath.Join(priv, "mnt", "file") - args := &control.ExecArgs{ + execArgs := &control.ExecArgs{ Filename: "/usr/bin/test", Argv: []string{"test", "!", "-f", privFile}, } - if ws, err := cont.executeSync(args); err != nil || ws != 0 { + if ws, err := cont.executeSync(execArgs); err != nil || ws != 0 { t.Fatalf("exec: test ! -f %q, ws: %v, err: %v", privFile, ws, err) } // Check that mount propagated to slave mount. slaveFile := filepath.Join(slave, "mnt", "file") - args = &control.ExecArgs{ + execArgs = &control.ExecArgs{ Filename: "/usr/bin/test", Argv: []string{"test", "-f", slaveFile}, } - if ws, err := cont.executeSync(args); err != nil || ws != 0 { + if ws, err := cont.executeSync(execArgs); err != nil || ws != 0 { t.Fatalf("exec: test -f %q, ws: %v, err: %v", privFile, ws, err) } } @@ -1813,7 +1937,12 @@ func TestMountSymlink(t *testing.T) { defer os.RemoveAll(rootDir) defer os.RemoveAll(bundleDir) - cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + cont, err := New(conf, args) if err != nil { t.Fatalf("creating container: %v", err) } @@ -1826,11 +1955,11 @@ func TestMountSymlink(t *testing.T) { // Check that symlink was resolved and mount was created where the symlink // is pointing to. file := path.Join(target, "file") - args := &control.ExecArgs{ + execArgs := &control.ExecArgs{ Filename: "/usr/bin/test", Argv: []string{"test", "-f", file}, } - if ws, err := cont.executeSync(args); err != nil || ws != 0 { + if ws, err := cont.executeSync(execArgs); err != nil || ws != 0 { t.Fatalf("exec: test -f %q, ws: %v, err: %v", file, ws, err) } } diff --git a/runsc/container/multi_container_test.go b/runsc/container/multi_container_test.go index 83fe24d64..c0f9b372c 100644 --- a/runsc/container/multi_container_test.go +++ b/runsc/container/multi_container_test.go @@ -84,7 +84,12 @@ func startContainers(conf *boot.Config, specs []*specs.Spec, ids []string) ([]*C } bundles = append(bundles, bundleDir) - cont, err := Create(ids[i], spec, conf, bundleDir, "", "", "") + args := Args{ + ID: ids[i], + Spec: spec, + BundleDir: bundleDir, + } + cont, err := New(conf, args) if err != nil { cleanup() return nil, nil, fmt.Errorf("error creating container: %v", err) @@ -661,7 +666,12 @@ func TestMultiContainerDestroyNotStarted(t *testing.T) { } defer os.RemoveAll(rootBundleDir) - root, err := Create(ids[0], specs[0], conf, rootBundleDir, "", "", "") + rootArgs := Args{ + ID: ids[0], + Spec: specs[0], + BundleDir: rootBundleDir, + } + root, err := New(conf, rootArgs) if err != nil { t.Fatalf("error creating root container: %v", err) } @@ -677,7 +687,12 @@ func TestMultiContainerDestroyNotStarted(t *testing.T) { } defer os.RemoveAll(bundleDir) - cont, err := Create(ids[1], specs[1], conf, bundleDir, "", "", "") + args := Args{ + ID: ids[1], + Spec: specs[1], + BundleDir: bundleDir, + } + cont, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -712,7 +727,12 @@ func TestMultiContainerDestroyStarting(t *testing.T) { } defer os.RemoveAll(rootBundleDir) - root, err := Create(ids[0], specs[0], conf, rootBundleDir, "", "", "") + rootArgs := Args{ + ID: ids[0], + Spec: specs[0], + BundleDir: rootBundleDir, + } + root, err := New(conf, rootArgs) if err != nil { t.Fatalf("error creating root container: %v", err) } @@ -733,7 +753,12 @@ func TestMultiContainerDestroyStarting(t *testing.T) { } defer os.RemoveAll(bundleDir) - cont, err := Create(ids[i], specs[i], conf, bundleDir, "", "", "") + rootArgs := Args{ + ID: ids[i], + Spec: specs[i], + BundleDir: rootBundleDir, + } + cont, err := New(conf, rootArgs) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -807,7 +832,12 @@ func TestMultiContainerGoferStop(t *testing.T) { // Start root container. conf := testutil.TestConfigWithRoot(rootDir) - root, err := Create(rootID, rootSpec, conf, bundleDir, "", "", "") + rootArgs := Args{ + ID: rootID, + Spec: rootSpec, + BundleDir: bundleDir, + } + root, err := New(conf, rootArgs) if err != nil { t.Fatalf("error creating root container: %v", err) } @@ -831,7 +861,12 @@ func TestMultiContainerGoferStop(t *testing.T) { } defer os.RemoveAll(bundleDir) - child, err := Create(ids[j], spec, conf, bundleDir, "", "", "") + args := Args{ + ID: ids[j], + Spec: spec, + BundleDir: bundleDir, + } + child, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -1087,7 +1122,12 @@ func TestMultiContainerSharedMountRestart(t *testing.T) { } defer os.RemoveAll(bundleDir) - containers[1], err = Create(ids[1], podSpec[1], conf, bundleDir, "", "", "") + args := Args{ + ID: ids[1], + Spec: podSpec[1], + BundleDir: bundleDir, + } + containers[1], err = New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } diff --git a/runsc/container/shared_volume_test.go b/runsc/container/shared_volume_test.go index 51a7f99df..1f90d2462 100644 --- a/runsc/container/shared_volume_test.go +++ b/runsc/container/shared_volume_test.go @@ -52,7 +52,12 @@ func TestSharedVolume(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + c, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } @@ -206,7 +211,12 @@ func TestSharedVolumeFile(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") + args := Args{ + ID: testutil.UniqueContainerID(), + Spec: spec, + BundleDir: bundleDir, + } + c, err := New(conf, args) if err != nil { t.Fatalf("error creating container: %v", err) } diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index a19b1d124..bf17f62d9 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -73,15 +73,47 @@ type Sandbox struct { statusMu sync.Mutex } +// Args is used to configure a new sandbox. +type Args struct { + // ID is the sandbox unique identifier. + ID string + + // Spec is the OCI spec that describes the container. + Spec *specs.Spec + + // BundleDir is the directory containing the container bundle. + BundleDir string + + // ConsoleSocket is the path to a unix domain socket that will receive + // the console FD. It may be empty. + ConsoleSocket string + + // UserLog is the filename to send user-visible logs to. It may be empty. + UserLog string + + // IOFiles is the list of files that connect to a 9P endpoint for the mounts + // points using Gofers. They must be in the same order as mounts appear in + // the spec. + IOFiles []*os.File + + // MountsFile is a file container mount information from the spec. It's + // equivalent to the mounts from the spec, except that all paths have been + // resolved to their final absolute location. + MountsFile *os.File + + // Gcgroup is the cgroup that the sandbox is part of. + Cgroup *cgroup.Cgroup +} + // New creates the sandbox process. The caller must call Destroy() on the // sandbox. -func New(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, userLog string, ioFiles []*os.File, specFile *os.File, cg *cgroup.Cgroup) (*Sandbox, error) { - s := &Sandbox{ID: id, Cgroup: cg} +func New(conf *boot.Config, args *Args) (*Sandbox, error) { + s := &Sandbox{ID: args.ID, Cgroup: args.Cgroup} // The Cleanup object cleans up partially created sandboxes when an error // occurs. Any errors occurring during cleanup itself are ignored. c := specutils.MakeCleanup(func() { err := s.destroy() - log.Warningf("error destroying sandbox: %v", err) + log.Warningf("error Ndestroying sandbox: %v", err) }) defer c.Clean() @@ -93,7 +125,7 @@ func New(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocke defer clientSyncFile.Close() // Create the sandbox process. - err = s.createSandboxProcess(spec, conf, bundleDir, consoleSocket, userLog, ioFiles, specFile, sandboxSyncFile) + err = s.createSandboxProcess(conf, args, sandboxSyncFile) // sandboxSyncFile has to be closed to be able to detect when the sandbox // process exits unexpectedly. sandboxSyncFile.Close() @@ -291,7 +323,7 @@ func (s *Sandbox) connError(err error) error { // createSandboxProcess starts the sandbox as a subprocess by running the "boot" // command, passing in the bundle dir. -func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, userLog string, ioFiles []*os.File, mountsFile, startSyncFile *os.File) error { +func (s *Sandbox) createSandboxProcess(conf *boot.Config, args *Args, startSyncFile *os.File) error { // nextFD is used to get unused FDs that we can pass to the sandbox. It // starts at 3 because 0, 1, and 2 are taken by stdin/out/err. nextFD := 3 @@ -327,7 +359,7 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund // Add the "boot" command to the args. // // All flags after this must be for the boot command - cmd.Args = append(cmd.Args, "boot", "--bundle="+bundleDir) + cmd.Args = append(cmd.Args, "boot", "--bundle="+args.BundleDir) // Create a socket for the control server and donate it to the sandbox. addr := boot.ControlSocketAddr(s.ID) @@ -342,12 +374,12 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund cmd.Args = append(cmd.Args, "--controller-fd="+strconv.Itoa(nextFD)) nextFD++ - defer mountsFile.Close() - cmd.ExtraFiles = append(cmd.ExtraFiles, mountsFile) + defer args.MountsFile.Close() + cmd.ExtraFiles = append(cmd.ExtraFiles, args.MountsFile) cmd.Args = append(cmd.Args, "--mounts-fd="+strconv.Itoa(nextFD)) nextFD++ - specFile, err := specutils.OpenSpec(bundleDir) + specFile, err := specutils.OpenSpec(args.BundleDir) if err != nil { return err } @@ -361,7 +393,7 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund nextFD++ // If there is a gofer, sends all socket ends to the sandbox. - for _, f := range ioFiles { + for _, f := range args.IOFiles { defer f.Close() cmd.ExtraFiles = append(cmd.ExtraFiles, f) cmd.Args = append(cmd.Args, "--io-fds="+strconv.Itoa(nextFD)) @@ -389,14 +421,14 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund // If the console control socket file is provided, then create a new // pty master/slave pair and set the TTY on the sandbox process. - if consoleSocket != "" { + if args.ConsoleSocket != "" { cmd.Args = append(cmd.Args, "--console=true") // console.NewWithSocket will send the master on the given // socket, and return the slave. - tty, err := console.NewWithSocket(consoleSocket) + tty, err := console.NewWithSocket(args.ConsoleSocket) if err != nil { - return fmt.Errorf("setting up console with socket %q: %v", consoleSocket, err) + return fmt.Errorf("setting up console with socket %q: %v", args.ConsoleSocket, err) } defer tty.Close() @@ -469,7 +501,7 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund // Joins the network namespace if network is enabled. the sandbox talks // directly to the host network, which may have been configured in the // namespace. - if ns, ok := specutils.GetNS(specs.NetworkNamespace, spec); ok && conf.Network != boot.NetworkNone { + if ns, ok := specutils.GetNS(specs.NetworkNamespace, args.Spec); ok && conf.Network != boot.NetworkNone { log.Infof("Sandbox will be started in the container's network namespace: %+v", ns) nss = append(nss, ns) } else if conf.Network == boot.NetworkHost { @@ -483,10 +515,10 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund // inside the user namespace specified in the spec or the current namespace // if none is configured. if conf.Network == boot.NetworkHost { - if userns, ok := specutils.GetNS(specs.UserNamespace, spec); ok { + if userns, ok := specutils.GetNS(specs.UserNamespace, args.Spec); ok { log.Infof("Sandbox will be started in container's user namespace: %+v", userns) nss = append(nss, userns) - specutils.SetUIDGIDMappings(cmd, spec) + specutils.SetUIDGIDMappings(cmd, args.Spec) } else { log.Infof("Sandbox will be started in the current user namespace") } @@ -598,8 +630,8 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund } } - if userLog != "" { - f, err := os.OpenFile(userLog, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664) + if args.UserLog != "" { + f, err := os.OpenFile(args.UserLog, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664) if err != nil { return fmt.Errorf("opening compat log file: %v", err) }