runsc/restore: must be able to restore to prepared container

runsc restore <container id> must be able to restore into a
prepared container to allow the user to configure container
specific namespaces as otherwise there is no other means to
override different bundle parameters and especially namespaces
configuration.

Specifically, otherwise, it is impossible to configure networking
other than by cloning the bundle config and preparing special
netns for the container instead of just configuring it on the fly
without whole management complexity.

This fix allows to do the following:

- runsc create
- configure container netns (even with the netlink api)
- restore into the container with nice networking

It also fixes the restore command to behave exactly as it is
stated in the documentation:

    runsc create <container id>

    runsc restore --image-path=<path> <container id>

at
https://github.com/google/gvisor/blob/master/g3doc/user_guide/checkpoint_restore.md

Generally, user shall not try to do a checkpoint restore into
the void. If container is not found, we keep back compatibility,
but print a warning.

Signed-off-by: Ivan Prisyazhnyy <john.koepi@gmail.com>
This commit is contained in:
Ivan Prisyazhnyy
2023-03-17 16:17:54 +01:00
parent 9abcf28e60
commit 9b1705598c
2 changed files with 57 additions and 9 deletions
+1
View File
@@ -53,6 +53,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/atomicbitops",
"//pkg/cleanup",
"//pkg/coretag",
"//pkg/coverage",
"//pkg/cpuid",
+56 -9
View File
@@ -16,10 +16,13 @@ package cmd
import (
"context"
"os"
"path/filepath"
"github.com/google/subcommands"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/cleanup"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/runsc/cmd/util"
"gvisor.dev/gvisor/runsc/config"
"gvisor.dev/gvisor/runsc/container"
@@ -89,32 +92,76 @@ func (r *Restore) Execute(_ context.Context, f *flag.FlagSet, args ...any) subco
if bundleDir == "" {
bundleDir = getwdOrDie()
}
spec, err := specutils.ReadSpec(bundleDir, conf)
if err != nil {
return util.Errorf("reading spec: %v", err)
}
specutils.LogSpecDebug(spec, conf.OCISeccomp)
if r.imagePath == "" {
return util.Errorf("image-path flag must be provided")
}
var cu cleanup.Cleanup
defer cu.Clean()
conf.RestoreFile = filepath.Join(r.imagePath, checkpointFileName)
runArgs := container.Args{
ID: id,
Spec: spec,
Spec: nil,
BundleDir: bundleDir,
ConsoleSocket: r.consoleSocket,
PIDFile: r.pidFile,
UserLog: r.userLog,
Attached: !r.detach,
}
ws, err := container.Run(conf, runArgs)
log.Debugf("Restore container, cid: %s, rootDir: %q", id, conf.RootDir)
c, err := container.Load(conf.RootDir, container.FullID{ContainerID: id}, container.LoadOpts{})
if err != nil {
return util.Errorf("running container: %v", err)
if err != os.ErrNotExist {
return util.Errorf("loading container: %v", err)
}
log.Warningf("Container not found, creating new one, cid: %s, spec from: %s", id, bundleDir)
// Read the spec again here to ensure flag annotations from the spec are
// applied to "conf".
if runArgs.Spec, err = specutils.ReadSpec(bundleDir, conf); err != nil {
return util.Errorf("reading spec: %v", err)
}
specutils.LogSpecDebug(runArgs.Spec, conf.OCISeccomp)
if c, err = container.New(conf, runArgs); err != nil {
return util.Errorf("creating container: %v", err)
}
// Clean up partially created container if an error occurs.
// Any errors returned by Destroy() itself are ignored.
cu.Add(func() {
c.Destroy()
})
} else {
runArgs.Spec = c.Spec
}
log.Debugf("Restore: %v", conf.RestoreFile)
if err := c.Restore(runArgs.Spec, conf, conf.RestoreFile); err != nil {
return util.Errorf("starting container: %v", err)
}
// If we allocate a terminal, forward signals to the sandbox process.
// Otherwise, Ctrl+C will terminate this process and its children,
// including the terminal.
if c.Spec.Process.Terminal {
stopForwarding := c.ForwardSignals(0, true /* fgProcess */)
defer stopForwarding()
}
var ws unix.WaitStatus
if runArgs.Attached {
if ws, err = c.Wait(); err != nil {
return util.Errorf("running container: %v", err)
}
}
*waitStatus = ws
cu.Release()
return subcommands.ExitSuccess
}