summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJustine Olshan <justineolshan@google.com>2018-06-15 16:08:20 -0700
committerShentubot <shentubot@google.com>2018-06-15 16:09:09 -0700
commit0786707cd94b8feffaeb083077eccaf10873e682 (patch)
tree9dba432e5b695936e5f9907a92dae12bd5dc666a
parentbd2d1aaa16474202b1a2c1edbf62e6782fa2dc36 (diff)
Added code for a pause command for a container process.
Like runc, the pause command will pause the processes of the given container. It will set that container's status to "paused." A resume command will be be added to unpause and continue running the process. PiperOrigin-RevId: 200789624 Change-Id: I72a5d7813d90ecfc4d01cc252d6018855016b1ea
-rw-r--r--runsc/boot/controller.go9
-rw-r--r--runsc/cmd/BUILD1
-rw-r--r--runsc/cmd/pause.go67
-rw-r--r--runsc/container/container.go19
-rw-r--r--runsc/container/container_test.go34
-rw-r--r--runsc/container/status.go18
-rw-r--r--runsc/sandbox/sandbox.go15
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 {