Runsc checkpoint works.

This is the first iteration of checkpoint that actually saves to a file.
Tests for checkpoint are included.

Ran into an issue when private unix sockets are enabled. An error message
was added for this case and the mutex state was set.

PiperOrigin-RevId: 200269470
Change-Id: I28d29a9f92c44bf73dc4a4b12ae0509ee4070e93
This commit is contained in:
Brielle Broder
2018-06-12 13:25:23 -07:00
committed by Shentubot
parent 7a10df454b
commit 711a9869e5
7 changed files with 95 additions and 9 deletions
+3 -2
View File
@@ -28,8 +28,9 @@ import (
)
type endpointMap struct {
mu sync.RWMutex
m map[device.MultiDeviceKey]unix.BoundEndpoint
mu sync.RWMutex `state:"nosave"`
// TODO: Make map with private unix sockets savable.
m map[device.MultiDeviceKey]unix.BoundEndpoint
}
// add adds the endpoint to the map.
+9
View File
@@ -22,6 +22,15 @@ import (
"gvisor.googlesource.com/gvisor/pkg/unet"
)
// beforeSave is invoked by stateify.
//
// TODO: Make map with private unix sockets savable.
func (e *endpointMap) beforeSave() {
if len(e.m) != 0 {
panic("EndpointMap with existing private unix sockets cannot be saved")
}
}
// afterLoad is invoked by stateify.
func (s *session) afterLoad() {
// The restore environment contains the 9p connection of this mount.
+3 -1
View File
@@ -100,7 +100,9 @@ func New(spec *specs.Spec, conf *Config, controllerFD int, ioFDs []int, console
}
// Create VDSO.
vdso, err := loader.PrepareVDSO(p)
//
// Pass k as the platform since it is savable, unlike the actual platform.
vdso, err := loader.PrepareVDSO(k)
if err != nil {
return nil, fmt.Errorf("error creating vdso: %v", err)
}
+16 -1
View File
@@ -15,6 +15,8 @@
package cmd
import (
"os"
"context"
"flag"
"github.com/google/subcommands"
@@ -24,6 +26,7 @@ import (
// Checkpoint implements subcommands.Command for the "checkpoint" command.
type Checkpoint struct {
imagePath string
}
// Name implements subcommands.Command.Name.
@@ -44,6 +47,7 @@ func (*Checkpoint) Usage() string {
// SetFlags implements subcommands.Command.SetFlags.
func (c *Checkpoint) SetFlags(f *flag.FlagSet) {
f.StringVar(&c.imagePath, "image-path", "", "path to saved container image")
}
// Execute implements subcommands.Command.Execute.
@@ -62,7 +66,18 @@ func (c *Checkpoint) Execute(_ context.Context, f *flag.FlagSet, args ...interfa
Fatalf("error loading container: %v", err)
}
if err := cont.Checkpoint(); err != nil {
if c.imagePath == "" {
Fatalf("image-path flag must be provided")
}
// Create the image file and open for writing.
file, err := os.OpenFile(c.imagePath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0644)
if err != nil {
Fatalf("os.OpenFile(%q) failed: %v", c.imagePath, err)
}
defer file.Close()
if err := cont.Checkpoint(file); err != nil {
Fatalf("checkpoint failed: %v", err)
}
+3 -2
View File
@@ -339,13 +339,14 @@ func (c *Container) Signal(sig syscall.Signal) error {
}
// Checkpoint sends the checkpoint call to the container.
func (c *Container) Checkpoint() error {
// The statefile will be written to f, the file at the specified image-path.
func (c *Container) Checkpoint(f *os.File) error {
log.Debugf("Checkpoint container %q", c.ID)
if c.Status == Stopped {
log.Warningf("container %q not running, not checkpointing", c.ID)
return nil
}
return c.Sandbox.Checkpoint(c.ID)
return c.Sandbox.Checkpoint(c.ID, f)
}
// State returns the metadata of the container.
+52 -1
View File
@@ -408,6 +408,57 @@ func TestExec(t *testing.T) {
}
}
// TestCheckpoint verifies that calling checkpoint with an image-path flag succeeds.
// Since there is no current default image path, confirming that calling
// checkpoint without an image path fails.
// Checks that there is a file with the name and location given by image path.
func TestCheckpoint(t *testing.T) {
// Container will succeed.
spec := testutil.NewSpecWithArgs("sleep", "100")
rootDir, bundleDir, conf, err := testutil.SetupContainer(spec)
if err != nil {
t.Fatalf("error setting up container: %v", err)
}
defer os.RemoveAll(rootDir)
defer os.RemoveAll(bundleDir)
// Create and start the container.
cont, err := container.Create(testutil.UniqueContainerID(), 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)
}
// Set the image path, which is where the checkpoint image will be saved.
imagePath := filepath.Join(os.TempDir(), "test-image-file")
// Create the image file and open for writing.
file, err := os.OpenFile(imagePath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0644)
if err != nil {
t.Fatalf("error opening new file at imagePath: %v", err)
}
defer file.Close()
// Checkpoint running container; save state into new file.
if err := cont.Checkpoint(file); err != nil {
t.Fatalf("error checkpointing container to empty file: %v", err)
}
defer os.RemoveAll(imagePath)
// Check to see if file exists and contains data.
fileInfo, err := os.Stat(imagePath)
if err != nil {
t.Fatalf("error checkpointing container: %v", err)
}
if size := fileInfo.Size(); size == 0 {
t.Fatalf("failed checkpoint, file still appears empty: %v", err)
}
}
// TestCapabilities verifies that:
// - Running exec as non-root UID and GID will result in an error (because the
// executable file can't be read).
@@ -602,7 +653,7 @@ func TestSpecUnsupported(t *testing.T) {
}
// TestRunNonRoot checks that sandbox can be configured when running as
// non-priviledged user.
// non-privileged user.
func TestRunNonRoot(t *testing.T) {
spec := testutil.NewSpecWithArgs("/bin/true")
spec.Process.User.UID = 343
+9 -2
View File
@@ -441,7 +441,8 @@ func (s *Sandbox) Signal(cid string, sig syscall.Signal) error {
}
// Checkpoint sends the checkpoint call for a container in the sandbox.
func (s *Sandbox) Checkpoint(cid string) error {
// The statefile will be written to f.
func (s *Sandbox) Checkpoint(cid string, f *os.File) error {
log.Debugf("Checkpoint sandbox %q", s.ID)
conn, err := s.connect()
if err != nil {
@@ -449,7 +450,13 @@ func (s *Sandbox) Checkpoint(cid string) error {
}
defer conn.Close()
if err := conn.Call(boot.ContainerCheckpoint, nil, nil); err != nil {
opt := control.SaveOpts{
FilePayload: urpc.FilePayload{
Files: []*os.File{f},
},
}
if err := conn.Call(boot.ContainerCheckpoint, &opt, nil); err != nil {
return fmt.Errorf("err checkpointing container %q: %v", cid, err)
}
return nil