mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
committed by
Shentubot
parent
7a10df454b
commit
711a9869e5
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user