diff options
-rw-r--r-- | runsc/boot/controller.go | 9 | ||||
-rw-r--r-- | runsc/cmd/BUILD | 1 | ||||
-rw-r--r-- | runsc/cmd/pause.go | 67 | ||||
-rw-r--r-- | runsc/container/container.go | 19 | ||||
-rw-r--r-- | runsc/container/container_test.go | 34 | ||||
-rw-r--r-- | runsc/container/status.go | 18 | ||||
-rw-r--r-- | runsc/sandbox/sandbox.go | 15 |
7 files changed, 156 insertions, 7 deletions
diff --git a/runsc/boot/controller.go b/runsc/boot/controller.go index 095b0a9b9..564f2d271 100644 --- a/runsc/boot/controller.go +++ b/runsc/boot/controller.go @@ -37,6 +37,9 @@ const ( // container.. ContainerExecute = "containerManager.Execute" + // ContainerPause pauses the container. + ContainerPause = "containerManager.Pause" + // ContainerProcesses is the URPC endpoint for getting the list of // processes running in a container. ContainerProcesses = "containerManager.Processes" @@ -153,6 +156,12 @@ func (cm *containerManager) Checkpoint(o *control.SaveOpts, _ *struct{}) error { return state.Save(o, nil) } +// Pause suspends the process in a container. +func (cm *containerManager) Pause(_, _ *struct{}) error { + cm.k.Pause() + return 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/cmd/BUILD b/runsc/cmd/BUILD index 63d8036bd..8fbce294f 100644 --- a/runsc/cmd/BUILD +++ b/runsc/cmd/BUILD @@ -17,6 +17,7 @@ go_library( "kill.go", "list.go", "path.go", + "pause.go", "ps.go", "restore.go", "run.go", diff --git a/runsc/cmd/pause.go b/runsc/cmd/pause.go new file mode 100644 index 000000000..ac393b48e --- /dev/null +++ b/runsc/cmd/pause.go @@ -0,0 +1,67 @@ +// 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 cmd + +import ( + "context" + "flag" + "github.com/google/subcommands" + "gvisor.googlesource.com/gvisor/runsc/boot" + "gvisor.googlesource.com/gvisor/runsc/container" +) + +// Pause implements subcommands.Command for the "pause" command. +type Pause struct{} + +// Name implements subcommands.Command.Name. +func (*Pause) Name() string { + return "pause" +} + +// Synopsis implements subcommands.Command.Synopsis. +func (*Pause) Synopsis() string { + return "pause suspends all processes in a container" +} + +// Usage implements subcommands.Command.Usage. +func (*Pause) Usage() string { + return `pause <container id> - pause process in instance of container.` +} + +// SetFlags implements subcommands.Command.SetFlags. +func (*Pause) SetFlags(f *flag.FlagSet) { +} + +// Execute implements subcommands.Command.Execute. +func (*Pause) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { + if f.NArg() != 1 { + f.Usage() + return subcommands.ExitUsageError + } + + id := f.Arg(0) + conf := args[0].(*boot.Config) + + cont, err := container.Load(conf.RootDir, id) + if err != nil { + Fatalf("error loading container: %v", err) + } + + if err := cont.Pause(); err != nil { + Fatalf("pause failed: %v", err) + } + + return subcommands.ExitSuccess +} diff --git a/runsc/container/container.go b/runsc/container/container.go index 40c31ca7f..dc7fccdee 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -316,7 +316,7 @@ func (c *Container) Event() (*boot.Event, error) { // Pid returns the Pid of the sandbox the container is running in, or -1 if the // container is not running. func (c *Container) Pid() int { - if c.Status != Running && c.Status != Created { + if c.Status != Running && c.Status != Created && c.Status != Paused { return -1 } return c.Sandbox.Pid @@ -349,6 +349,23 @@ func (c *Container) Checkpoint(f *os.File) error { return c.Sandbox.Checkpoint(c.ID, f) } +// Pause suspends the container and its kernel. +// The call only succeeds if the container's status is created or running. +func (c *Container) Pause() error { + log.Debugf("Pausing container %q", c.ID) + switch c.Status { + case Created, Running: + if err := c.Sandbox.Pause(c.ID); err != nil { + return fmt.Errorf("error pausing container: %v", err) + } + c.Status = Paused + return c.save() + default: + log.Warningf("container %q not created or running, not pausing", c.ID) + return nil + } +} + // State returns the metadata of the container. func (c *Container) State() specs.State { return specs.State{ diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index b6d19bf33..5659abab3 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -459,6 +459,40 @@ func TestCheckpoint(t *testing.T) { } } +// TestPause tests that calling pause successfully pauses the container. +// It checks that no errors are returned and that the state of the container +// is in fact 'Paused.' +func TestPause(t *testing.T) { + 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) + } + + // Pause the running container. + if err := cont.Pause(); err != nil { + t.Errorf("error pausing container: %v", err) + } + + // Confirm the status of the container is paused. + if got, want := cont.Status, container.Paused; got != want { + t.Errorf("container status got %v, want %v", got, want) + } +} + // TestCapabilities verifies that: // - Running exec as non-root UID and GID will result in an error (because the // executable file can't be read). diff --git a/runsc/container/status.go b/runsc/container/status.go index 8da1b4e89..bf177e78a 100644 --- a/runsc/container/status.go +++ b/runsc/container/status.go @@ -19,13 +19,17 @@ package container type Status int const ( - // Creating indicates "the container is being created". - Creating Status = iota - // Created indicates "the runtime has finished the create operation and // the container process has neither exited nor executed the // user-specified program". - Created + Created Status = iota + + // Creating indicates "the container is being created". + Creating + + // Paused indicates that the process within the container has been + // suspended. + Paused // Running indicates "the container process has executed the // user-specified program but has not exited". @@ -39,10 +43,12 @@ const ( // CLI spec and should not be changed. func (s Status) String() string { switch s { - case Creating: - return "creating" case Created: return "created" + case Creating: + return "creating" + case Paused: + return "paused" case Running: return "running" case Stopped: diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index 1c0d23161..f9129a179 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -464,6 +464,21 @@ func (s *Sandbox) Checkpoint(cid string, f *os.File) error { return nil } +// Pause sends the pause call for a container in the sandbox. +func (s *Sandbox) Pause(cid string) error { + log.Debugf("Pause sandbox %q", s.ID) + conn, err := s.connect() + if err != nil { + return err + } + defer conn.Close() + + if err := conn.Call(boot.ContainerPause, nil, nil); err != nil { + return fmt.Errorf("err pausing container %q: %v", cid, err) + } + return nil +} + // IsRunning returns true if the sandbox or gofer process is running. func (s *Sandbox) IsRunning() bool { if s.Pid != 0 { |