diff options
author | Googler <noreply@google.com> | 2018-06-06 11:43:01 -0700 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2018-06-06 11:43:55 -0700 |
commit | 722275c3d1a7b420915e6e6a3d623ae941c494cf (patch) | |
tree | f38faf0a5cc1dcd57815ea71973c9487c2913e9d | |
parent | ff7b4a156f95a587b5df4de89a22c200fceabb96 (diff) |
Added a function to the controller to checkpoint a container.
Functionality for checkpoint is not complete, more to come.
PiperOrigin-RevId: 199500803
Change-Id: Iafb0fcde68c584270000fea898e6657a592466f7
-rw-r--r-- | pkg/sentry/control/BUILD | 4 | ||||
-rw-r--r-- | pkg/sentry/control/state.go | 73 | ||||
-rw-r--r-- | runsc/boot/controller.go | 19 | ||||
-rw-r--r-- | runsc/boot/loader.go | 6 |
4 files changed, 99 insertions, 3 deletions
diff --git a/pkg/sentry/control/BUILD b/pkg/sentry/control/BUILD index 4d1d0d019..6169891f7 100644 --- a/pkg/sentry/control/BUILD +++ b/pkg/sentry/control/BUILD @@ -7,6 +7,7 @@ go_library( srcs = [ "control.go", "proc.go", + "state.go", ], importpath = "gvisor.googlesource.com/gvisor/pkg/sentry/control", visibility = [ @@ -14,6 +15,7 @@ go_library( ], deps = [ "//pkg/abi/linux", + "//pkg/log", "//pkg/sentry/fs", "//pkg/sentry/fs/host", "//pkg/sentry/kernel", @@ -21,7 +23,9 @@ go_library( "//pkg/sentry/kernel/kdefs", "//pkg/sentry/kernel/time", "//pkg/sentry/limits", + "//pkg/sentry/state", "//pkg/sentry/usage", + "//pkg/sentry/watchdog", "//pkg/urpc", ], ) diff --git a/pkg/sentry/control/state.go b/pkg/sentry/control/state.go new file mode 100644 index 000000000..cee4db636 --- /dev/null +++ b/pkg/sentry/control/state.go @@ -0,0 +1,73 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package control + +import ( + "errors" + + "gvisor.googlesource.com/gvisor/pkg/log" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" + "gvisor.googlesource.com/gvisor/pkg/sentry/state" + "gvisor.googlesource.com/gvisor/pkg/sentry/watchdog" + "gvisor.googlesource.com/gvisor/pkg/urpc" +) + +// ErrInvalidFiles is returned when the urpc call to Save does not include an +// appropriate file payload (e.g. there is no output file!). +var ErrInvalidFiles = errors.New("exactly one file must be provided") + +// State includes state-related functions. +type State struct { + Kernel *kernel.Kernel + Watchdog *watchdog.Watchdog +} + +// SaveOpts contains options for the Save RPC call. +type SaveOpts struct { + // Key is used for state integrity check. + Key []byte `json:"key"` + + // Metadata is the set of metadata to prepend to the state file. + Metadata map[string]string `json:"metadata"` + + // FilePayload contains the destination for the state. + urpc.FilePayload +} + +// Save saves the running system. +func (s *State) Save(o *SaveOpts, _ *struct{}) error { + // Create an output stream. + if len(o.FilePayload.Files) != 1 { + return ErrInvalidFiles + } + defer o.FilePayload.Files[0].Close() + + // Save to the first provided stream. + saveOpts := state.SaveOpts{ + Destination: o.FilePayload.Files[0], + Key: o.Key, + Metadata: o.Metadata, + Callback: func(err error) { + if err == nil { + log.Infof("Save succeeded: exiting...") + } else { + log.Warningf("Save failed: exiting...") + s.Kernel.SetExitError(err) + } + s.Kernel.Kill(kernel.ExitStatus{}) + }, + } + return saveOpts.Save(s.Kernel, s.Watchdog) +} diff --git a/runsc/boot/controller.go b/runsc/boot/controller.go index 8fc0a9076..095b0a9b9 100644 --- a/runsc/boot/controller.go +++ b/runsc/boot/controller.go @@ -22,9 +22,13 @@ import ( "gvisor.googlesource.com/gvisor/pkg/sentry/control" "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" "gvisor.googlesource.com/gvisor/pkg/sentry/socket/epsocket" + "gvisor.googlesource.com/gvisor/pkg/sentry/watchdog" ) const ( + // ContainerCheckpoint checkpoints a container. + ContainerCheckpoint = "containerManager.Checkpoint" + // ContainerEvent is the URPC endpoint for getting stats about the // container used by "runsc events". ContainerEvent = "containerManager.Event" @@ -69,7 +73,7 @@ type controller struct { } // newController creates a new controller and starts it listening. -func newController(fd int, k *kernel.Kernel) (*controller, error) { +func newController(fd int, k *kernel.Kernel, w *watchdog.Watchdog) (*controller, error) { srv, err := server.CreateFromFD(fd) if err != nil { return nil, err @@ -79,6 +83,7 @@ func newController(fd int, k *kernel.Kernel) (*controller, error) { startChan: make(chan struct{}), startResultChan: make(chan error), k: k, + watchdog: w, } srv.Register(manager) @@ -113,6 +118,9 @@ type containerManager struct { // k is the emulated linux kernel on which the sandboxed // containers run. k *kernel.Kernel + + // watchdog is the kernel watchdog. + watchdog *watchdog.Watchdog } // StartRoot will start the root container process. @@ -136,6 +144,15 @@ func (cm *containerManager) Execute(e *control.ExecArgs, waitStatus *uint32) err return nil } +// Checkpoint pauses a sandbox and saves its state. +func (cm *containerManager) Checkpoint(o *control.SaveOpts, _ *struct{}) error { + state := control.State{ + Kernel: cm.k, + Watchdog: cm.watchdog, + } + return state.Save(o, nil) +} + // Wait waits for the init process in the given container. func (cm *containerManager) Wait(cid *string, waitStatus *uint32) error { // TODO: Use the cid and wait on the init process in that diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 76edbb905..41d1ee50d 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -186,6 +186,9 @@ func New(spec *specs.Spec, conf *Config, controllerFD int, ioFDs []int, console atomic.StoreUint32(&sniffer.LogPackets, 0) } + // Create a watchdog. + watchdog := watchdog.New(k, watchdog.DefaultTimeout, watchdog.LogWarning) + // Create the control server using the provided FD. // // This must be done *after* we have initialized the kernel since the @@ -195,7 +198,7 @@ func New(spec *specs.Spec, conf *Config, controllerFD int, ioFDs []int, console // misconfigured process will cause an error, and we want the control // server up before that so that we don't time out trying to connect to // it. - ctrl, err := newController(controllerFD, k) + ctrl, err := newController(controllerFD, k, watchdog) if err != nil { return nil, fmt.Errorf("error creating control server: %v", err) } @@ -254,7 +257,6 @@ func New(spec *specs.Spec, conf *Config, controllerFD int, ioFDs []int, console // the emulated kernel. stopSignalForwarding := sighandling.StartForwarding(k) - watchdog := watchdog.New(k, watchdog.DefaultTimeout, watchdog.LogWarning) return &Loader{ k: k, ctrl: ctrl, |