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
This commit is contained in:
Brielle Broder 2018-06-21 09:57:33 -07:00 committed by Shentubot
parent 81d13fbd4d
commit 7d6149063a
6 changed files with 90 additions and 23 deletions

View File

@ -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

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}