diff options
author | Brielle Broder <bbroder@google.com> | 2018-06-21 09:57:33 -0700 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2018-06-21 09:58:24 -0700 |
commit | 7d6149063a0bb6e563885a8f199756e7af5e69cf (patch) | |
tree | 44378246b5a4535184658b854066baa5b0bac6d0 | |
parent | 81d13fbd4d2f14b61e89faa0c9888be568f97168 (diff) |
Restore implementation added to runsc.
Restore creates a new container and uses the given image-path to load a saved
image of a previous container. Restore command is plumbed through container
and sandbox. This command does not work yet - more to come.
PiperOrigin-RevId: 201541229
Change-Id: I864a14c799ce3717d99bcdaaebc764281863d06f
-rw-r--r-- | runsc/cmd/create.go | 2 | ||||
-rw-r--r-- | runsc/cmd/restore.go | 55 | ||||
-rw-r--r-- | runsc/container/container.go | 6 | ||||
-rw-r--r-- | runsc/container/container_test.go | 22 | ||||
-rw-r--r-- | runsc/sandbox/sandbox.go | 26 | ||||
-rw-r--r-- | runsc/sandbox/sandbox_test.go | 2 |
6 files changed, 90 insertions, 23 deletions
diff --git a/runsc/cmd/create.go b/runsc/cmd/create.go index 94a889077..5a887c73c 100644 --- a/runsc/cmd/create.go +++ b/runsc/cmd/create.go @@ -87,7 +87,7 @@ 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); err != nil { + if _, err := container.Create(id, spec, conf, bundleDir, c.consoleSocket, c.pidFile, ""); err != nil { Fatalf("error creating container: %v", err) } return subcommands.ExitSuccess diff --git a/runsc/cmd/restore.go b/runsc/cmd/restore.go index a535197a4..0589a36bf 100644 --- a/runsc/cmd/restore.go +++ b/runsc/cmd/restore.go @@ -15,13 +15,23 @@ package cmd import ( + "syscall" + "context" "flag" "github.com/google/subcommands" + "gvisor.googlesource.com/gvisor/runsc/boot" + "gvisor.googlesource.com/gvisor/runsc/container" + "gvisor.googlesource.com/gvisor/runsc/specutils" ) // Restore implements subcommands.Command for the "restore" command. type Restore struct { + // Restore flags are a super-set of those for Create. + Create + + // imagePath is the path to the saved container image + imagePath string } // Name implements subcommands.Command.Name. @@ -36,16 +46,55 @@ func (*Restore) Synopsis() string { // Usage implements subcommands.Command.Usage. func (*Restore) Usage() string { - return `restore [flags] <container id> - restore last saved state of container. + return `restore [flags] <container id> - restore saved state of container. ` } // SetFlags implements subcommands.Command.SetFlags. func (r *Restore) SetFlags(f *flag.FlagSet) { + r.Create.SetFlags(f) + f.StringVar(&r.imagePath, "image-path", "", "path to saved container image") } // Execute implements subcommands.Command.Execute. func (r *Restore) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { - Fatalf("restore not implemented") - return subcommands.ExitFailure + if f.NArg() != 1 { + f.Usage() + return subcommands.ExitUsageError + } + + id := f.Arg(0) + conf := args[0].(*boot.Config) + waitStatus := args[1].(*syscall.WaitStatus) + + bundleDir := r.bundleDir + if bundleDir == "" { + bundleDir = getwdOrDie() + } + spec, err := specutils.ReadSpec(bundleDir) + if err != nil { + Fatalf("error reading spec: %v", err) + } + specutils.LogSpec(spec) + + if r.imagePath == "" { + Fatalf("image-path flag must be provided") + } + + cont, err := container.Create(id, spec, conf, bundleDir, r.consoleSocket, r.pidFile, r.imagePath) + if err != nil { + Fatalf("error restoring container: %v", err) + } + + if err := cont.Start(conf); err != nil { + Fatalf("error starting container: %v", err) + } + + ws, err := cont.Wait() + if err != nil { + Fatalf("error running container: %v", err) + } + *waitStatus = ws + + return subcommands.ExitSuccess } diff --git a/runsc/container/container.go b/runsc/container/container.go index 3b7f95af9..604708e2c 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -188,7 +188,7 @@ func List(rootDir string) ([]string, error) { // Create creates the container in a new Sandbox process, unless the metadata // indicates that an existing Sandbox should be used. -func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, pidFile string) (*Container, error) { +func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, pidFile string, restoreFile string) (*Container, error) { log.Debugf("Create container %q in root dir: %s", id, conf.RootDir) if err := validateID(id); err != nil { return nil, err @@ -222,7 +222,7 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo 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) + s, err := sandbox.Create(id, spec, conf, bundleDir, consoleSocket, restoreFile) if err != nil { c.Destroy() return nil, err @@ -313,7 +313,7 @@ func (c *Container) Start(conf *boot.Config) error { // Run is a helper that calls Create + Start + Wait. func Run(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, pidFile string) (syscall.WaitStatus, error) { log.Debugf("Run container %q in root dir: %s", id, conf.RootDir) - c, err := Create(id, spec, conf, bundleDir, consoleSocket, pidFile) + c, err := Create(id, spec, conf, bundleDir, consoleSocket, pidFile, "") if err != nil { return 0, fmt.Errorf("error creating container: %v", err) } diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index 5128f5946..a8320f614 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -117,7 +117,7 @@ func run(spec *specs.Spec) error { defer os.RemoveAll(bundleDir) // Create, start and wait for the container. - s, err := container.Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + s, err := container.Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { return fmt.Errorf("error creating container: %v", err) } @@ -162,7 +162,7 @@ func TestLifecycle(t *testing.T) { } // Create the container. id := testutil.UniqueContainerID() - if _, err := container.Create(id, spec, conf, bundleDir, "", ""); err != nil { + if _, err := container.Create(id, spec, conf, bundleDir, "", "", ""); err != nil { t.Fatalf("error creating container: %v", err) } @@ -360,7 +360,7 @@ func TestExec(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - s, err := container.Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + s, err := container.Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -445,7 +445,7 @@ func TestCheckpoint(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - cont, err := container.Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + cont, err := container.Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -497,7 +497,7 @@ func TestPauseResume(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - cont, err := container.Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + cont, err := container.Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -599,7 +599,7 @@ func TestPauseResumeStatus(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - cont, err := container.Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + cont, err := container.Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -666,7 +666,7 @@ func TestCapabilities(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - s, err := container.Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + s, err := container.Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -765,7 +765,7 @@ func TestConsoleSocket(t *testing.T) { // Create the container and pass the socket name. id := testutil.UniqueContainerID() - s, err := container.Create(id, spec, conf, bundleDir, socketRelPath, "") + s, err := container.Create(id, spec, conf, bundleDir, socketRelPath, "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -828,7 +828,7 @@ func TestSpecUnsupported(t *testing.T) { defer os.RemoveAll(bundleDir) id := testutil.UniqueContainerID() - _, err = container.Create(id, spec, conf, bundleDir, "", "") + _, err = container.Create(id, spec, conf, bundleDir, "", "", "") if err == nil || !strings.Contains(err.Error(), "is not supported") { t.Errorf("container.Create() wrong error, got: %v, want: *is not supported, spec.Process: %+v", err, spec.Process) } @@ -917,7 +917,7 @@ func TestAbbreviatedIDs(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - cont, err := container.Create(cid, spec, conf, bundleDir, "", "") + cont, err := container.Create(cid, spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -987,7 +987,7 @@ func TestMultiContainerSanity(t *testing.T) { t.Fatalf("error setting up container: %v", err) } defer os.RemoveAll(bundleDir) - cont, err := container.Create(containerIDs[i], spec, conf, bundleDir, "", "") + cont, err := container.Create(containerIDs[i], spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index 652910efa..870a0ccd3 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -54,7 +54,9 @@ type Sandbox struct { } // Create creates the sandbox process. -func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket string) (*Sandbox, error) { +// +// If restoreFile is not empty, the sandbox will be restored from file. +func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket string, restoreFile string) (*Sandbox, error) { s := &Sandbox{ID: id} binPath, err := specutils.BinPath() @@ -69,7 +71,7 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo } // Create the sandbox process. - if err := s.createSandboxProcess(spec, conf, bundleDir, consoleSocket, binPath, ioFiles); err != nil { + if err := s.createSandboxProcess(spec, conf, bundleDir, consoleSocket, binPath, ioFiles, restoreFile); err != nil { return nil, err } @@ -251,7 +253,7 @@ func (s *Sandbox) createGoferProcess(spec *specs.Spec, conf *boot.Config, bundle // 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, binPath string, ioFiles []*os.File) error { +func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, binPath string, ioFiles []*os.File, restoreFile string) 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 @@ -273,12 +275,27 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund "--bundle", bundleDir, "--controller-fd="+strconv.Itoa(nextFD), fmt.Sprintf("--console=%t", consoleEnabled)) - nextFD++ controllerFile := os.NewFile(uintptr(fd), "control_server_socket") defer controllerFile.Close() cmd.ExtraFiles = append(cmd.ExtraFiles, controllerFile) + // If a restore filename was given, open the file and append its FD to Args + // and the file to ExtraFiles. + if restoreFile != "" { + // Create the image file and open for reading. + rF, err := os.Open(restoreFile) + if err != nil { + return fmt.Errorf("os.Open(%q) failed: %v", restoreFile, err) + } + defer rF.Close() + + nextFD++ + cmd.Args = append(cmd.Args, "--restore-fd="+strconv.Itoa(nextFD)) + cmd.ExtraFiles = append(cmd.ExtraFiles, rF) + } + nextFD++ + // If there is a gofer, sends all socket ends to the sandbox. for _, f := range ioFiles { defer f.Close() @@ -379,6 +396,7 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund } s.Pid = cmd.Process.Pid log.Infof("Sandbox started, pid: %d", s.Pid) + return nil } diff --git a/runsc/sandbox/sandbox_test.go b/runsc/sandbox/sandbox_test.go index fee2de283..9db90ef07 100644 --- a/runsc/sandbox/sandbox_test.go +++ b/runsc/sandbox/sandbox_test.go @@ -39,7 +39,7 @@ func TestGoferExits(t *testing.T) { defer os.RemoveAll(bundleDir) // Create, start and wait for the container. - s, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "") + s, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") if err != nil { t.Fatalf("error creating container: %v", err) } |