From 5397963b5d4d57bd3d3668df880b5314ca2fc3d8 Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Tue, 19 Jun 2018 21:42:21 -0700 Subject: runsc: Enable container creation within existing sandboxes. Containers are created as processes in the sandbox. Of the many things that don't work yet, the biggest issue is that the fsgofer is launched with its root as the sandbox's root directory. Thus, when a container is started and wants to read anything (including the init binary of the container), the gofer tries to serve from sandbox's root (which basically just has pause), not the container's. PiperOrigin-RevId: 201294560 Change-Id: I6423aa8830538959c56ae908ce067e4199d627b1 --- runsc/container/BUILD | 1 + runsc/container/container.go | 66 +++++++++++++++++++++++++---------- runsc/container/container_test.go | 73 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 120 insertions(+), 20 deletions(-) (limited to 'runsc/container') diff --git a/runsc/container/BUILD b/runsc/container/BUILD index fe477abf2..61e05e1c3 100644 --- a/runsc/container/BUILD +++ b/runsc/container/BUILD @@ -37,6 +37,7 @@ go_test( "//pkg/sentry/kernel/auth", "//pkg/unet", "//runsc/container", + "//runsc/specutils", "//runsc/test/testutil", "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", "@org_golang_x_sys//unix:go_default_library", diff --git a/runsc/container/container.go b/runsc/container/container.go index 571784e07..3b7f95af9 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -214,22 +214,43 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo Owner: os.Getenv("USER"), } - // TODO: If the metadata annotations indicate that this - // container should be started in another 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. We can look up that - // init container by ID to get the sandbox, then we need to expose a - // way to run a new container in the sandbox. - - // Start a new sandbox for this container. Any errors after this point - // must destroy the container. - s, err := sandbox.Create(id, spec, conf, bundleDir, consoleSocket) - if err != nil { - c.Destroy() - return nil, err - } + // If the metadata annotations indicate that this container should be + // 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 specutils.ShouldCreateSandbox(spec) { + log.Debugf("Creating new sandbox for container %q", id) + // Start a new sandbox for this container. Any errors after this point + // must destroy the container. + s, err := sandbox.Create(id, spec, conf, bundleDir, consoleSocket) + if err != nil { + c.Destroy() + return nil, err + } + c.Sandbox = s + } else { + // This is sort of confusing. For a sandbox with a root + // container and a child container in it, runsc sees: + // * A container struct whose sandbox ID is equal to the + // container ID. This is the root container that is tied to + // the creation of the sandbox. + // * 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) + if !ok { + return nil, fmt.Errorf("no sandbox ID found when creating container") + } + log.Debugf("Creating new container %q in sandbox %q", c.ID, sbid) - c.Sandbox = s + // Find the sandbox associated with this ID. + sb, err := Load(conf.RootDir, sbid) + if err != nil { + c.Destroy() + return nil, err + } + c.Sandbox = sb.Sandbox + } c.Status = Created // Save the metadata file. @@ -242,7 +263,7 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo // this file is created, so it must be the last thing we do. if pidFile != "" { if err := ioutil.WriteFile(pidFile, []byte(strconv.Itoa(c.Pid())), 0644); err != nil { - s.Destroy() + c.Destroy() return nil, fmt.Errorf("error writing pid file: %v", err) } } @@ -266,9 +287,16 @@ func (c *Container) Start(conf *boot.Config) error { } } - if err := c.Sandbox.Start(c.ID, c.Spec, conf); err != nil { - c.Destroy() - return err + if specutils.ShouldCreateSandbox(c.Spec) { + if err := c.Sandbox.StartRoot(c.Spec, conf); err != nil { + c.Destroy() + return err + } + } else { + if err := c.Sandbox.Start(c.Spec, conf); err != nil { + c.Destroy() + return err + } } // "If any poststart hook fails, the runtime MUST log a warning, but diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index 7f87ea5ab..1116ca170 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -36,6 +36,7 @@ import ( "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth" "gvisor.googlesource.com/gvisor/pkg/unet" "gvisor.googlesource.com/gvisor/runsc/container" + "gvisor.googlesource.com/gvisor/runsc/specutils" "gvisor.googlesource.com/gvisor/runsc/test/testutil" ) @@ -51,7 +52,7 @@ func waitForProcessList(s *container.Container, expected []*control.Process) err var got []*control.Process for start := time.Now(); time.Now().Sub(start) < 10*time.Second; { var err error - got, err := s.Processes() + got, err = s.Processes() if err != nil { return fmt.Errorf("error getting process data from container: %v", err) } @@ -946,3 +947,73 @@ func TestAbbreviatedIDs(t *testing.T) { } } } + +// TestMultiContainerSanity checks that it is possible to run 2 dead-simple +// containers in the same sandbox. +func TestMultiContainerSanity(t *testing.T) { + containerIDs := []string{ + testutil.UniqueContainerID(), + testutil.UniqueContainerID(), + } + containerAnnotations := []map[string]string{ + // The first container creates a sandbox. + map[string]string{ + specutils.ContainerdContainerTypeAnnotation: specutils.ContainerdContainerTypeSandbox, + }, + // The second container creates a container within the first + // container's sandbox. + map[string]string{ + specutils.ContainerdContainerTypeAnnotation: specutils.ContainerdContainerTypeContainer, + specutils.ContainerdSandboxIDAnnotation: containerIDs[0], + }, + } + + rootDir, err := testutil.SetupRootDir() + if err != nil { + t.Fatalf("error creating root dir: %v", err) + } + defer os.RemoveAll(rootDir) + + // Setup the containers. + containers := make([]*container.Container, 0, len(containerIDs)) + for i, annotations := range containerAnnotations { + spec := testutil.NewSpecWithArgs("sleep", "100") + spec.Annotations = annotations + bundleDir, conf, err := testutil.SetupContainerInRoot(rootDir, spec) + if err != nil { + t.Fatalf("error setting up container: %v", err) + } + defer os.RemoveAll(bundleDir) + cont, err := container.Create(containerIDs[i], spec, conf, bundleDir, "", "") + if err != nil { + t.Fatalf("error creating container: %v", err) + } + defer cont.Destroy() + if err := cont.Start(conf); err != nil { + t.Fatalf("error starting container: %v", err) + } + containers = append(containers, cont) + } + + expectedPL := []*control.Process{ + { + UID: 0, + PID: 1, + PPID: 0, + C: 0, + Cmd: "sleep", + }, + { + UID: 0, + PID: 2, + PPID: 0, + C: 0, + Cmd: "sleep", + }, + } + + // Check via ps that multiple processes are running. + if err := waitForProcessList(containers[0], expectedPL); err != nil { + t.Errorf("failed to wait for sleep to start: %v", err) + } +} -- cgit v1.2.3