summaryrefslogtreecommitdiffhomepage
path: root/runsc/sandbox
diff options
context:
space:
mode:
authorNicolas Lacasse <nlacasse@google.com>2018-05-15 10:17:19 -0700
committerShentubot <shentubot@google.com>2018-05-15 10:18:03 -0700
commit205f1027e6beb84101439172b3c776c2671b5be8 (patch)
tree10294e667ee529e140c474c475e7309cb72ea1d8 /runsc/sandbox
parented02ac4f668ec41063cd51cbbd451baba9e9a6e7 (diff)
Refactor the Sandbox package into Sandbox + Container.
This is a necessary prerequisite for supporting multiple containers in a single sandbox. All the commands (in cmd package) now call operations on Containers (container package). When a Container first starts, it will create a Sandbox with the same ID. The Sandbox class is now simpler, as it only knows how to create boot/gofer processes, and how to forward commands into the running boot process. There are TODOs sprinkled around for additional support for multiple containers. Most notably, we need to detect when a container is intended to run in an existing sandbox (by reading the metadata), and then have some way to signal to the sandbox to start a new container. Other urpc calls into the sandbox need to pass the container ID, so the sandbox can run the operation on the given container. These are only half-plummed through right now. PiperOrigin-RevId: 196688269 Change-Id: I1ecf4abbb9dd8987a53ae509df19341aaf42b5b0
Diffstat (limited to 'runsc/sandbox')
-rw-r--r--runsc/sandbox/BUILD25
-rw-r--r--runsc/sandbox/hook.go111
-rw-r--r--runsc/sandbox/sandbox.go430
-rw-r--r--runsc/sandbox/sandbox_test.go665
-rw-r--r--runsc/sandbox/status.go56
5 files changed, 95 insertions, 1192 deletions
diff --git a/runsc/sandbox/BUILD b/runsc/sandbox/BUILD
index bdd95903e..e89b19552 100644
--- a/runsc/sandbox/BUILD
+++ b/runsc/sandbox/BUILD
@@ -1,16 +1,14 @@
package(licenses = ["notice"]) # Apache 2.0
-load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "sandbox",
srcs = [
"console.go",
- "hook.go",
"namespace.go",
"network.go",
"sandbox.go",
- "status.go",
],
importpath = "gvisor.googlesource.com/gvisor/runsc/sandbox",
visibility = [
@@ -30,24 +28,3 @@ go_library(
"@org_golang_x_sys//unix:go_default_library",
],
)
-
-go_test(
- name = "sandbox_test",
- size = "small",
- srcs = ["sandbox_test.go"],
- pure = "on",
- rundir = ".",
- deps = [
- "//pkg/abi/linux",
- "//pkg/log",
- "//pkg/sentry/control",
- "//pkg/sentry/kernel/auth",
- "//pkg/unet",
- "//runsc/boot",
- "//runsc/cmd",
- "//runsc/sandbox",
- "@com_github_google_subcommands//:go_default_library",
- "@com_github_opencontainers_runtime-spec//specs-go:go_default_library",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
diff --git a/runsc/sandbox/hook.go b/runsc/sandbox/hook.go
deleted file mode 100644
index 40b064cdc..000000000
--- a/runsc/sandbox/hook.go
+++ /dev/null
@@ -1,111 +0,0 @@
-// 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 sandbox
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "os/exec"
- "path/filepath"
- "strings"
- "time"
-
- specs "github.com/opencontainers/runtime-spec/specs-go"
- "gvisor.googlesource.com/gvisor/pkg/log"
-)
-
-// This file implements hooks as defined in OCI spec:
-// https://github.com/opencontainers/runtime-spec/blob/master/config.md#toc22
-//
-// "hooks":{
-// "prestart":[{
-// "path":"/usr/bin/dockerd",
-// "args":[
-// "libnetwork-setkey", "arg2",
-// ]
-// }]
-// },
-
-// executeHooksBestEffort executes hooks and logs warning in case they fail.
-// Runs all hooks, always.
-func executeHooksBestEffort(hooks []specs.Hook, s specs.State) {
- for _, h := range hooks {
- if err := executeHook(h, s); err != nil {
- log.Warningf("Failure to execute hook %+v, err: %v", h, err)
- }
- }
-}
-
-// executeHooks executes hooks until the first one fails or they all execute.
-func executeHooks(hooks []specs.Hook, s specs.State) error {
- for _, h := range hooks {
- if err := executeHook(h, s); err != nil {
- return err
- }
- }
- return nil
-}
-
-func executeHook(h specs.Hook, s specs.State) error {
- log.Debugf("Executing hook %+v, state: %+v", h, s)
-
- if strings.TrimSpace(h.Path) == "" {
- return fmt.Errorf("empty path for hook")
- }
- if !filepath.IsAbs(h.Path) {
- return fmt.Errorf("path for hook is not absolute: %q", h.Path)
- }
-
- b, err := json.Marshal(s)
- if err != nil {
- return err
- }
- var stdout, stderr bytes.Buffer
- cmd := exec.Cmd{
- Path: h.Path,
- Args: h.Args,
- Env: h.Env,
- Stdin: bytes.NewReader(b),
- Stdout: &stdout,
- Stderr: &stderr,
- }
- if err := cmd.Start(); err != nil {
- return err
- }
-
- c := make(chan error, 1)
- go func() {
- c <- cmd.Wait()
- }()
-
- var timer <-chan time.Time
- if h.Timeout != nil {
- timer = time.After(time.Duration(*h.Timeout) * time.Second)
- }
- select {
- case err := <-c:
- if err != nil {
- return fmt.Errorf("failure executing hook %q, err: %v\nstdout: %s\nstderr: %s", h.Path, err, stdout.String(), stderr.String())
- }
- case <-timer:
- cmd.Process.Kill()
- cmd.Wait()
- return fmt.Errorf("timeout executing hook %q\nstdout: %s\nstderr: %s", h.Path, stdout.String(), stderr.String())
- }
-
- log.Debugf("Execute hook %q success!", h.Path)
- return nil
-}
diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go
index 34bd6ea67..5dfa4cf0b 100644
--- a/runsc/sandbox/sandbox.go
+++ b/runsc/sandbox/sandbox.go
@@ -16,13 +16,9 @@
package sandbox
import (
- "encoding/json"
"fmt"
- "io/ioutil"
"os"
"os/exec"
- "path/filepath"
- "regexp"
"strconv"
"syscall"
"time"
@@ -38,308 +34,110 @@ import (
"gvisor.googlesource.com/gvisor/runsc/specutils"
)
-// metadataFilename is the name of the metadata file relative to sandboxRoot
-// that holds sandbox metadata.
-const metadataFilename = "meta.json"
-
-// See libcontainer/factory_linux.go
-var idRegex = regexp.MustCompile(`^[\w+-\.]+$`)
-
-// validateID validates the sandbox id.
-func validateID(id string) error {
- if !idRegex.MatchString(id) {
- return fmt.Errorf("invalid sandbox id: %v", id)
- }
- return nil
-}
-
-func validateSpec(spec *specs.Spec) error {
- if spec.Process.SelinuxLabel != "" {
- return fmt.Errorf("SELinux is not supported: %s", spec.Process.SelinuxLabel)
- }
-
- // Docker uses AppArmor by default, so just log that it's being ignored.
- if spec.Process.ApparmorProfile != "" {
- log.Warningf("AppArmor profile %q is being ignored", spec.Process.ApparmorProfile)
- }
- // TODO: Apply seccomp to application inside sandbox.
- if spec.Linux != nil && spec.Linux.Seccomp != nil {
- log.Warningf("Seccomp spec is being ignored")
- }
- return nil
-}
-
-// Sandbox wraps a child sandbox process, and is responsible for saving and
-// loading sandbox metadata to disk.
-//
-// Within a root directory, we maintain subdirectories for each sandbox named
-// with the sandbox id. The sandbox metadata is is stored as json within the
-// sandbox directory in a file named "meta.json". This metadata format is
-// defined by us, and is not part of the OCI spec.
-//
-// Sandboxes must write this metadata file after any change to their internal
-// state. The entire sandbox directory is deleted when the sandbox is
-// destroyed.
+// Sandbox wraps a sandbox process.
//
-// TODO: Protect against concurrent changes to the sandbox metadata
-// file.
+// It is used to start/stop sandbox process (and associated processes like
+// gofers), as well as for running and manipulating containers inside a running
+// sandbox.
type Sandbox struct {
- // ID is the sandbox ID.
+ // ID is the id of the sandbox. By convention, this is the same ID as
+ // the first container run in the sandbox.
ID string `json:"id"`
- // Spec is the OCI runtime spec that configures this sandbox.
- Spec *specs.Spec `json:"spec"`
-
- // BundleDir is the directory containing the sandbox bundle.
- BundleDir string `json:"bundleDir"`
-
- // SandboxRoot is the directory containing the sandbox metadata file.
- SandboxRoot string `json:"sandboxRoot"`
-
- // CreatedAt is the time the sandbox was created.
- CreatedAt time.Time `json:"createdAt"`
-
- // Owner is the sandbox owner.
- Owner string `json:"owner"`
-
- // ConsoleSocket is the path to a unix domain socket that will receive
- // the console FD. It is only used during create, so we don't need to
- // store it in the metadata.
- ConsoleSocket string `json:"-"`
-
- // Pid is the pid of the running sandbox. Only valid if Status is
- // Created or Running.
+ // Pid is the pid of the running sandbox. May be 0 is the sandbox is
+ // not running.
Pid int `json:"pid"`
- // GoferPid is the pid of the gofer running along side the sandbox. May be 0
- // if the gofer has been killed or it's not being used.
+ // GoferPid is the pid of the gofer running along side the sandbox. May
+ // be 0 if the gofer has been killed or it's not being used.
GoferPid int `json:"goferPid"`
-
- // Status is the current sandbox Status.
- Status Status `json:"status"`
}
-// Create creates the sandbox subprocess and writes the metadata file. Args
-// are additional arguments that will be passed to the sandbox process.
-func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, pidFile string, args []string) (*Sandbox, error) {
- log.Debugf("Create sandbox %q in root dir: %s", id, conf.RootDir)
- if err := validateID(id); err != nil {
- return nil, err
- }
- if err := validateSpec(spec); err != nil {
- return nil, err
- }
-
- sandboxRoot := filepath.Join(conf.RootDir, id)
- if exists(sandboxRoot) {
- return nil, fmt.Errorf("sandbox with id %q already exists: %q ", id, sandboxRoot)
- }
+// Create creates the sandbox process.
+func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket string) (*Sandbox, error) {
+ s := &Sandbox{ID: id}
- s := &Sandbox{
- ID: id,
- Spec: spec,
- ConsoleSocket: consoleSocket,
- BundleDir: bundleDir,
- SandboxRoot: sandboxRoot,
- Status: Creating,
- Owner: os.Getenv("USER"),
- }
-
- // Create sandbox process. If anything errors between now and the end of this
- // function, we MUST clean up all sandbox resources.
- if err := s.createProcesses(conf, args); err != nil {
- s.Destroy()
+ binPath, err := specutils.BinPath()
+ if err != nil {
return nil, err
}
- // Wait for the control server to come up (or timeout). The sandbox is
- // not "created" until that happens.
- if err := s.waitForCreated(10 * time.Second); err != nil {
- s.Destroy()
+ // Create the gofer process.
+ ioFiles, err := s.createGoferProcess(spec, conf, bundleDir, binPath)
+ if err != nil {
return nil, err
}
- s.Status = Created
- s.CreatedAt = time.Now()
-
- // Save the metadata file.
- if err := s.save(); err != nil {
- s.Destroy()
+ // Create the sandbox process.
+ if err := s.createSandboxProcess(spec, conf, bundleDir, consoleSocket, binPath, ioFiles); err != nil {
return nil, err
}
- // Write the pid file. Containerd consideres the create complete after
- // this file is created, so it must be the last thing we do.
- if pidFile != "" {
- if err := ioutil.WriteFile(pidFile, []byte(strconv.Itoa(s.Pid)), 0644); err != nil {
- s.Destroy()
- return nil, fmt.Errorf("error writing pid file: %v", err)
- }
- }
-
- return s, nil
-}
-
-// Run is a helper that calls Create + Start + Wait.
-func Run(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, pidFile string, args []string) (syscall.WaitStatus, error) {
- s, err := Create(id, spec, conf, bundleDir, consoleSocket, pidFile, args)
- if err != nil {
- return 0, fmt.Errorf("error creating sandbox: %v", err)
- }
- if err := s.Start(conf); err != nil {
- return 0, fmt.Errorf("error starting sandbox: %v", err)
- }
- return s.Wait()
-}
-
-// Load loads a sandbox from with the given id from a metadata file.
-func Load(rootDir, id string) (*Sandbox, error) {
- log.Debugf("Load sandbox %q %q", rootDir, id)
- if err := validateID(id); err != nil {
+ // Wait for the control server to come up (or timeout).
+ if err := s.waitForCreated(10 * time.Second); err != nil {
return nil, err
}
- sandboxRoot := filepath.Join(rootDir, id)
- if !exists(sandboxRoot) {
- return nil, fmt.Errorf("sandbox with id %q does not exist", id)
- }
- metaFile := filepath.Join(sandboxRoot, metadataFilename)
- if !exists(metaFile) {
- return nil, fmt.Errorf("sandbox with id %q does not have metadata file %q", id, metaFile)
- }
- metaBytes, err := ioutil.ReadFile(metaFile)
- if err != nil {
- return nil, fmt.Errorf("error reading sandbox metadata file %q: %v", metaFile, err)
- }
- var s Sandbox
- if err := json.Unmarshal(metaBytes, &s); err != nil {
- return nil, fmt.Errorf("error unmarshaling sandbox metadata from %q: %v", metaFile, err)
- }
-
- // If the status is "Running" or "Created", check that the process
- // still exists, and set it to Stopped if it does not.
- //
- // This is inherently racey.
- if s.Status == Running || s.Status == Created {
- // Send signal 0 to check if process exists.
- if err := s.Signal(0); err != nil {
- // Process no longer exists.
- s.Status = Stopped
- s.Pid = 0
- }
- }
- return &s, nil
-}
-
-// List returns all sandbox ids in the given root directory.
-func List(rootDir string) ([]string, error) {
- log.Debugf("List sandboxes %q", rootDir)
- fs, err := ioutil.ReadDir(rootDir)
- if err != nil {
- return nil, fmt.Errorf("ReadDir(%s) failed: %v", rootDir, err)
- }
- var out []string
- for _, f := range fs {
- out = append(out, f.Name())
- }
- return out, nil
-}
-
-// State returns the metadata of the sandbox.
-func (s *Sandbox) State() specs.State {
- return specs.State{
- Version: specs.Version,
- ID: s.ID,
- Status: s.Status.String(),
- Pid: s.Pid,
- Bundle: s.BundleDir,
- }
+ return s, nil
}
// Start starts running the containerized process inside the sandbox.
-func (s *Sandbox) Start(conf *boot.Config) error {
+func (s *Sandbox) Start(cid string, spec *specs.Spec, conf *boot.Config) error {
log.Debugf("Start sandbox %q, pid: %d", s.ID, s.Pid)
- if s.Status != Created {
- return fmt.Errorf("cannot start container in state %s", s.Status)
- }
-
- // "If any prestart hook fails, the runtime MUST generate an error,
- // stop and destroy the container".
- if s.Spec.Hooks != nil {
- if err := executeHooks(s.Spec.Hooks.Prestart, s.State()); err != nil {
- s.Destroy()
- return err
- }
- }
-
- c, err := s.connect()
+ conn, err := s.connect()
if err != nil {
- s.Destroy()
return err
}
- defer c.Close()
+ defer conn.Close()
// Configure the network.
- if err := setupNetwork(c, s.Pid, s.Spec, conf); err != nil {
- s.Destroy()
+ if err := setupNetwork(conn, s.Pid, spec, conf); err != nil {
return fmt.Errorf("error setting up network: %v", err)
}
// Send a message to the sandbox control server to start the
// application.
- if err := c.Call(boot.ApplicationStart, nil, nil); err != nil {
- s.Destroy()
- return fmt.Errorf("error starting application %v: %v", s.Spec.Process.Args, err)
- }
-
- // "If any poststart hook fails, the runtime MUST log a warning, but
- // the remaining hooks and lifecycle continue as if the hook had
- // succeeded".
- if s.Spec.Hooks != nil {
- executeHooksBestEffort(s.Spec.Hooks.Poststart, s.State())
+ //
+ // TODO: Pass in the container id (cid) here. The sandbox
+ // should start only that container.
+ if err := conn.Call(boot.ApplicationStart, nil, nil); err != nil {
+ return fmt.Errorf("error starting application %v: %v", spec.Process.Args, err)
}
- s.Status = Running
- return s.save()
+ return nil
}
-// Processes retrieves the list of processes and associated metadata inside a
-// sandbox.
-func (s *Sandbox) Processes() ([]*control.Process, error) {
- if s.Status != Running {
- return nil, fmt.Errorf("cannot get processes of container %q because it isn't running. It is in state %v", s.ID, s.Status)
- }
-
- c, err := s.connect()
+// Processes retrieves the list of processes and associated metadata for a
+// given container in this sandbox.
+func (s *Sandbox) Processes(cid string) ([]*control.Process, error) {
+ conn, err := s.connect()
if err != nil {
return nil, err
}
- defer c.Close()
+ defer conn.Close()
var pl []*control.Process
- if err := c.Call(boot.ApplicationProcesses, nil, &pl); err != nil {
+ // TODO: Pass in the container id (cid) here. The sandbox
+ // should return process info for only that container.
+ if err := conn.Call(boot.ApplicationProcesses, nil, &pl); err != nil {
return nil, fmt.Errorf("error retrieving process data from sandbox: %v", err)
}
return pl, nil
}
-// Execute runs the specified command in the sandbox.
-func (s *Sandbox) Execute(e *control.ExecArgs) (syscall.WaitStatus, error) {
- log.Debugf("Execute in sandbox %q, pid: %d, args: %+v", s.ID, s.Pid, e)
- if s.Status != Created && s.Status != Running {
- return 0, fmt.Errorf("cannot exec in container in state %s", s.Status)
- }
-
- log.Debugf("Connecting to sandbox...")
- c, err := s.connect()
+// Execute runs the specified command in the container.
+func (s *Sandbox) Execute(cid string, e *control.ExecArgs) (syscall.WaitStatus, error) {
+ conn, err := s.connect()
if err != nil {
return 0, fmt.Errorf("error connecting to control server at pid %d: %v", s.Pid, err)
}
- defer c.Close()
+ defer conn.Close()
// Send a message to the sandbox control server to start the application.
var waitStatus uint32
- if err := c.Call(boot.ApplicationExecute, e, &waitStatus); err != nil {
+ // TODO: Pass in the container id (cid) here. The sandbox
+ // should execute in the context of that container.
+ if err := conn.Call(boot.ApplicationExecute, e, &waitStatus); err != nil {
return 0, fmt.Errorf("error executing in sandbox: %v", err)
}
@@ -347,60 +145,45 @@ func (s *Sandbox) Execute(e *control.ExecArgs) (syscall.WaitStatus, error) {
}
// Event retrieves stats about the sandbox such as memory and CPU utilization.
-func (s *Sandbox) Event() (*boot.Event, error) {
- if s.Status != Running && s.Status != Created {
- return nil, fmt.Errorf("cannot get events for container in state: %s", s.Status)
- }
-
- c, err := s.connect()
+func (s *Sandbox) Event(cid string) (*boot.Event, error) {
+ conn, err := s.connect()
if err != nil {
return nil, err
}
- defer c.Close()
+ defer conn.Close()
var e boot.Event
- if err := c.Call(boot.ApplicationEvent, nil, &e); err != nil {
+ // TODO: Pass in the container id (cid) here. The sandbox
+ // should return events only for that container.
+ if err := conn.Call(boot.ApplicationEvent, nil, &e); err != nil {
return nil, fmt.Errorf("error retrieving event data from sandbox: %v", err)
}
- e.ID = s.ID
+ e.ID = cid
return &e, nil
}
func (s *Sandbox) connect() (*urpc.Client, error) {
log.Debugf("Connecting to sandbox...")
- c, err := client.ConnectTo(boot.ControlSocketAddr(s.ID))
+ conn, err := client.ConnectTo(boot.ControlSocketAddr(s.ID))
if err != nil {
return nil, fmt.Errorf("error connecting to control server at pid %d: %v", s.Pid, err)
}
- return c, nil
-}
-
-func (s *Sandbox) createProcesses(conf *boot.Config, args []string) error {
- binPath, err := specutils.BinPath()
- if err != nil {
- return err
- }
-
- ioFiles, err := s.createGoferProcess(conf, binPath, args)
- if err != nil {
- return err
- }
- return s.createSandboxProcess(conf, binPath, args, ioFiles)
+ return conn, nil
}
-func (s *Sandbox) createGoferProcess(conf *boot.Config, binPath string, commonArgs []string) ([]*os.File, error) {
+func (s *Sandbox) createGoferProcess(spec *specs.Spec, conf *boot.Config, bundleDir, binPath string) ([]*os.File, error) {
if conf.FileAccess != boot.FileAccessProxy {
// Don't start a gofer. The sandbox will access host FS directly.
return nil, nil
}
- var args []string
- args = append(args, commonArgs...)
- args = append(args, "gofer", "--bundle", s.BundleDir)
+ // Start with the general config flags.
+ args := conf.ToFlags()
+ args = append(args, "gofer", "--bundle", bundleDir)
- // Start with root mount and then add any other additional mount.
+ // Add root mount and then add any other additional mounts.
mountCount := 1
- for _, m := range s.Spec.Mounts {
+ for _, m := range spec.Mounts {
if specutils.Is9PMount(m) {
mountCount++
}
@@ -429,8 +212,8 @@ func (s *Sandbox) createGoferProcess(conf *boot.Config, binPath string, commonAr
// Setup any uid/gid mappings, and create or join the configured user
// namespace so the gofer's view of the filesystem aligns with the
// users in the sandbox.
- setUIDGIDMappings(cmd, s.Spec)
- nss := filterNS([]specs.LinuxNamespaceType{specs.UserNamespace}, s.Spec)
+ setUIDGIDMappings(cmd, spec)
+ nss := filterNS([]specs.LinuxNamespaceType{specs.UserNamespace}, spec)
// Start the gofer in the given namespace.
log.Debugf("Starting gofer: %s %v", binPath, args)
@@ -444,7 +227,7 @@ func (s *Sandbox) createGoferProcess(conf *boot.Config, binPath string, commonAr
// createSandboxProcess starts the sandbox as a subprocess by running the "boot"
// command, passing in the bundle dir.
-func (s *Sandbox) createSandboxProcess(conf *boot.Config, binPath string, commonArgs []string, ioFiles []*os.File) error {
+func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, binPath string, ioFiles []*os.File) error {
// nextFD is used to get unused FDs that we can pass to the sandbox. It
// starts at 3 because 0, 1, and 2 are taken by stdin/out/err.
nextFD := 3
@@ -457,13 +240,13 @@ func (s *Sandbox) createSandboxProcess(conf *boot.Config, binPath string, common
return fmt.Errorf("error creating control server socket for sandbox %q: %v", s.ID, err)
}
- consoleEnabled := s.ConsoleSocket != ""
+ consoleEnabled := consoleSocket != ""
- cmd := exec.Command(binPath, commonArgs...)
+ cmd := exec.Command(binPath, conf.ToFlags()...)
cmd.SysProcAttr = &syscall.SysProcAttr{}
cmd.Args = append(cmd.Args,
"boot",
- "--bundle", s.BundleDir,
+ "--bundle", bundleDir,
"--controller-fd="+strconv.Itoa(nextFD),
fmt.Sprintf("--console=%t", consoleEnabled))
nextFD++
@@ -485,9 +268,9 @@ func (s *Sandbox) createSandboxProcess(conf *boot.Config, binPath string, common
if consoleEnabled {
// setupConsole will send the master on the socket, and return
// the slave.
- tty, err := setupConsole(s.ConsoleSocket)
+ tty, err := setupConsole(consoleSocket)
if err != nil {
- return fmt.Errorf("error setting up control socket %q: %v", s.ConsoleSocket, err)
+ return fmt.Errorf("error setting up control socket %q: %v", consoleSocket, err)
}
defer tty.Close()
@@ -535,7 +318,7 @@ func (s *Sandbox) createSandboxProcess(conf *boot.Config, binPath string, common
// Joins the network namespace if network is enabled. the sandbox talks
// directly to the host network, which may have been configured in the
// namespace.
- if ns, ok := getNS(specs.NetworkNamespace, s.Spec); ok && conf.Network != boot.NetworkNone {
+ if ns, ok := getNS(specs.NetworkNamespace, spec); ok && conf.Network != boot.NetworkNone {
log.Infof("Sandbox will be started in the container's network namespace: %+v", ns)
nss = append(nss, ns)
} else {
@@ -549,10 +332,10 @@ func (s *Sandbox) createSandboxProcess(conf *boot.Config, binPath string, common
// - Gofer: when using a Gofer, the sandbox process can run isolated in an
// empty namespace.
if conf.Network == boot.NetworkHost || conf.FileAccess == boot.FileAccessDirect {
- if userns, ok := getNS(specs.UserNamespace, s.Spec); ok {
+ if userns, ok := getNS(specs.UserNamespace, spec); ok {
log.Infof("Sandbox will be started in container's user namespace: %+v", userns)
nss = append(nss, userns)
- setUIDGIDMappings(cmd, s.Spec)
+ setUIDGIDMappings(cmd, spec)
} else {
log.Infof("Sandbox will be started in the current user namespace")
}
@@ -596,8 +379,10 @@ func (s *Sandbox) waitForCreated(timeout time.Duration) error {
}
// Wait waits for the containerized process to exit, and returns its WaitStatus.
-func (s *Sandbox) Wait() (syscall.WaitStatus, error) {
- log.Debugf("Wait on sandbox %q with pid %d", s.ID, s.Pid)
+func (s *Sandbox) Wait(cid string) (syscall.WaitStatus, error) {
+ // TODO: This waits on the sandbox process. We need a way
+ // to wait on an individual container in the sandbox.
+
p, err := os.FindProcess(s.Pid)
if err != nil {
// "On Unix systems, FindProcess always succeeds and returns a
@@ -611,6 +396,13 @@ func (s *Sandbox) Wait() (syscall.WaitStatus, error) {
return ps.Sys().(syscall.WaitStatus), nil
}
+// Stop stops the container in the sandbox.
+func (s *Sandbox) Stop(cid string) error {
+ // TODO: This should stop the container with the given ID
+ // in the sandbox.
+ return nil
+}
+
// Destroy frees all resources associated with the sandbox.
func (s *Sandbox) Destroy() error {
log.Debugf("Destroy sandbox %q", s.ID)
@@ -625,60 +417,26 @@ func (s *Sandbox) Destroy() error {
sendSignal(s.GoferPid, unix.SIGKILL)
s.GoferPid = 0
}
- if err := os.RemoveAll(s.SandboxRoot); err != nil {
- log.Warningf("Failed to delete sandbox root directory %q, err: %v", s.SandboxRoot, err)
- }
-
- // "If any poststop hook fails, the runtime MUST log a warning, but the
- // remaining hooks and lifecycle continue as if the hook had succeeded".
- if s.Spec.Hooks != nil && (s.Status == Created || s.Status == Running) {
- executeHooksBestEffort(s.Spec.Hooks.Poststop, s.State())
- }
- s.Status = Stopped
return nil
}
-// Signal sends the signal to the sandbox.
-func (s *Sandbox) Signal(sig syscall.Signal) error {
+// Signal sends the signal to a container in the sandbox.
+func (s *Sandbox) Signal(cid string, sig syscall.Signal) error {
log.Debugf("Signal sandbox %q", s.ID)
- if s.Status == Stopped {
- log.Warningf("sandbox %q not running, not sending signal %v to pid %d", s.ID, sig, s.Pid)
- return nil
- }
+
+ // TODO: This sends a signal to the sandbox process, which
+ // will be forwarded to the first process in the sandbox. We need a way
+ // to send a signal to any container in the sandbox.
+ // to wait on an individual container in the sandbox.
+
return sendSignal(s.Pid, sig)
}
+// sendSignal sends a signal to the sandbox process.
func sendSignal(pid int, sig syscall.Signal) error {
if err := syscall.Kill(pid, sig); err != nil {
return fmt.Errorf("error sending signal %d to pid %d: %v", sig, pid, err)
}
return nil
}
-
-// save saves the sandbox metadata to a file.
-func (s *Sandbox) save() error {
- log.Debugf("Save sandbox %q", s.ID)
- if err := os.MkdirAll(s.SandboxRoot, 0711); err != nil {
- return fmt.Errorf("error creating sandbox root directory %q: %v", s.SandboxRoot, err)
- }
- meta, err := json.Marshal(s)
- if err != nil {
- return fmt.Errorf("error marshaling sandbox metadata: %v", err)
- }
- metaFile := filepath.Join(s.SandboxRoot, metadataFilename)
- if err := ioutil.WriteFile(metaFile, meta, 0640); err != nil {
- return fmt.Errorf("error writing sandbox metadata: %v", err)
- }
- return nil
-}
-
-// exists returns true if the given file exists.
-func exists(f string) bool {
- if _, err := os.Stat(f); err == nil {
- return true
- } else if !os.IsNotExist(err) {
- log.Warningf("error checking for file %q: %v", f, err)
- }
- return false
-}
diff --git a/runsc/sandbox/sandbox_test.go b/runsc/sandbox/sandbox_test.go
deleted file mode 100644
index 1fac38a29..000000000
--- a/runsc/sandbox/sandbox_test.go
+++ /dev/null
@@ -1,665 +0,0 @@
-// 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 sandbox_test
-
-import (
- "encoding/json"
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "os/signal"
- "path/filepath"
- "reflect"
- "strings"
- "syscall"
- "testing"
- "time"
-
- "context"
- "flag"
- "github.com/google/subcommands"
- specs "github.com/opencontainers/runtime-spec/specs-go"
- "golang.org/x/sys/unix"
- "gvisor.googlesource.com/gvisor/pkg/abi/linux"
- "gvisor.googlesource.com/gvisor/pkg/log"
- "gvisor.googlesource.com/gvisor/pkg/sentry/control"
- "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth"
- "gvisor.googlesource.com/gvisor/pkg/unet"
- "gvisor.googlesource.com/gvisor/runsc/boot"
- "gvisor.googlesource.com/gvisor/runsc/cmd"
- "gvisor.googlesource.com/gvisor/runsc/sandbox"
-)
-
-func init() {
- log.SetLevel(log.Debug)
-}
-
-// writeSpec writes the spec to disk in the given directory.
-func writeSpec(dir string, spec *specs.Spec) error {
- b, err := json.Marshal(spec)
- if err != nil {
- return err
- }
- return ioutil.WriteFile(filepath.Join(dir, "config.json"), b, 0755)
-}
-
-// newSpecWithArgs creates a simple spec with the given args suitable for use
-// in tests.
-func newSpecWithArgs(args ...string) *specs.Spec {
- spec := &specs.Spec{
- // The host filesystem root is the sandbox root.
- Root: &specs.Root{
- Path: "/",
- Readonly: true,
- },
- Process: &specs.Process{
- Args: args,
- Env: []string{
- "PATH=" + os.Getenv("PATH"),
- },
- },
- }
- return spec
-}
-
-// shutdownSignal will be sent to the sandbox in order to shut down cleanly.
-const shutdownSignal = syscall.SIGUSR2
-
-// setupSandbox creates a bundle and root dir for the sandbox, generates a test
-// config, and writes the spec to config.json in the bundle dir.
-func setupSandbox(spec *specs.Spec) (rootDir, bundleDir string, conf *boot.Config, err error) {
- rootDir, err = ioutil.TempDir("", "sandboxes")
- if err != nil {
- return "", "", nil, fmt.Errorf("error creating root dir: %v", err)
- }
-
- bundleDir, err = ioutil.TempDir("", "bundle")
- if err != nil {
- return "", "", nil, fmt.Errorf("error creating bundle dir: %v", err)
- }
-
- if err = writeSpec(bundleDir, spec); err != nil {
- return "", "", nil, fmt.Errorf("error writing spec: %v", err)
- }
-
- conf = &boot.Config{
- RootDir: rootDir,
- Network: boot.NetworkNone,
- }
-
- return rootDir, bundleDir, conf, nil
-}
-
-// uniqueSandboxID generates a unique sandbox id for each test.
-//
-// The sandbox id is used to create an abstract unix domain socket, which must
-// be unique. While the sandbox forbids creating two sandboxes with the same
-// name, sometimes between test runs the socket does not get cleaned up quickly
-// enough, causing sandbox creation to fail.
-func uniqueSandboxID() string {
- return fmt.Sprintf("test-sandbox-%d", time.Now().UnixNano())
-}
-
-// waitForProcessList waits for the given process list to show up in the sandbox.
-func waitForProcessList(s *sandbox.Sandbox, expected []*control.Process) error {
- var got []*control.Process
- for start := time.Now(); time.Now().Sub(start) < 10*time.Second; {
- var err error
- got, err := s.Processes()
- if err != nil {
- return fmt.Errorf("error getting process data from sandbox: %v", err)
- }
- if procListsEqual(got, expected) {
- return nil
- }
- // Process might not have started, try again...
- time.Sleep(10 * time.Millisecond)
- }
- return fmt.Errorf("sandbox got process list: %s, want: %s", procListToString(got), procListToString(expected))
-}
-
-// TestLifecycle tests the basic Create/Start/Signal/Destroy sandbox lifecycle.
-// It verifies after each step that the sandbox can be loaded from disk, and
-// has the correct status.
-func TestLifecycle(t *testing.T) {
- // The sandbox will just sleep for a long time. We will kill it before
- // it finishes sleeping.
- spec := newSpecWithArgs("sleep", "100")
-
- rootDir, bundleDir, conf, err := setupSandbox(spec)
- if err != nil {
- t.Fatalf("error setting up sandbox: %v", err)
- }
- defer os.RemoveAll(rootDir)
- defer os.RemoveAll(bundleDir)
-
- // expectedPL lists the expected process state of the sandbox.
- expectedPL := []*control.Process{
- {
- UID: 0,
- PID: 1,
- PPID: 0,
- C: 0,
- Cmd: "sleep",
- },
- }
- // Create the sandbox.
- id := uniqueSandboxID()
- if _, err := sandbox.Create(id, spec, conf, bundleDir, "", "", nil); err != nil {
- t.Fatalf("error creating sandbox: %v", err)
- }
- // Load the sandbox from disk and check the status.
- s, err := sandbox.Load(rootDir, id)
- if err != nil {
- t.Fatalf("error loading sandbox: %v", err)
- }
- if got, want := s.Status, sandbox.Created; got != want {
- t.Errorf("sandbox status got %v, want %v", got, want)
- }
-
- // List should return the sandbox id.
- ids, err := sandbox.List(rootDir)
- if err != nil {
- t.Fatalf("error listing sandboxes: %v", err)
- }
- if got, want := ids, []string{id}; !reflect.DeepEqual(got, want) {
- t.Errorf("sandbox list got %v, want %v", got, want)
- }
-
- // Start the sandbox.
- if err := s.Start(conf); err != nil {
- t.Fatalf("error starting sandbox: %v", err)
- }
- // Load the sandbox from disk and check the status.
- s, err = sandbox.Load(rootDir, id)
- if err != nil {
- t.Fatalf("error loading sandbox: %v", err)
- }
- if got, want := s.Status, sandbox.Running; got != want {
- t.Errorf("sandbox status got %v, want %v", got, want)
- }
-
- // Verify that "sleep 100" is running.
- if err := waitForProcessList(s, expectedPL); err != nil {
- t.Error(err)
- }
-
- // Send the sandbox a signal, which we catch and use to cleanly
- // shutdown.
- if err := s.Signal(shutdownSignal); err != nil {
- t.Fatalf("error sending signal %v to sandbox: %v", shutdownSignal, err)
- }
- // Wait for it to die.
- if _, err := s.Wait(); err != nil {
- t.Fatalf("error waiting on sandbox: %v", err)
- }
- // Load the sandbox from disk and check the status.
- s, err = sandbox.Load(rootDir, id)
- if err != nil {
- t.Fatalf("error loading sandbox: %v", err)
- }
- if got, want := s.Status, sandbox.Stopped; got != want {
- t.Errorf("sandbox status got %v, want %v", got, want)
- }
-
- // Destroy the sandbox.
- if err := s.Destroy(); err != nil {
- t.Fatalf("error destroying sandbox: %v", err)
- }
-
- // List should not return the sandbox id.
- ids, err = sandbox.List(rootDir)
- if err != nil {
- t.Fatalf("error listing sandboxes: %v", err)
- }
- if len(ids) != 0 {
- t.Errorf("expected sandbox list to be empty, but got %v", ids)
- }
-
- // Loading the sandbox by id should fail.
- if _, err = sandbox.Load(rootDir, id); err == nil {
- t.Errorf("expected loading destroyed sandbox to fail, but it did not")
- }
-}
-
-// Test the we can execute the application with different path formats.
-func TestExePath(t *testing.T) {
- for _, test := range []struct {
- path string
- success bool
- }{
- {path: "true", success: true},
- {path: "bin/true", success: true},
- {path: "/bin/true", success: true},
- {path: "thisfiledoesntexit", success: false},
- {path: "bin/thisfiledoesntexit", success: false},
- {path: "/bin/thisfiledoesntexit", success: false},
- } {
- spec := newSpecWithArgs(test.path)
- rootDir, bundleDir, conf, err := setupSandbox(spec)
- if err != nil {
- t.Fatalf("exec: %s, error setting up sandbox: %v", test.path, err)
- }
-
- ws, err := sandbox.Run(uniqueSandboxID(), spec, conf, bundleDir, "", "", nil)
-
- os.RemoveAll(rootDir)
- os.RemoveAll(bundleDir)
-
- if test.success {
- if err != nil {
- t.Errorf("exec: %s, error running sandbox: %v", test.path, err)
- }
- if ws.ExitStatus() != 0 {
- t.Errorf("exec: %s, got exit status %v want %v", test.path, ws.ExitStatus(), 0)
- }
- } else {
- if err == nil {
- t.Errorf("exec: %s, got: no error, want: error", test.path)
- }
- }
- }
-}
-
-// Test the we can retrieve the application exit status from the sandbox.
-func TestAppExitStatus(t *testing.T) {
- // First sandbox will succeed.
- succSpec := newSpecWithArgs("true")
-
- rootDir, bundleDir, conf, err := setupSandbox(succSpec)
- if err != nil {
- t.Fatalf("error setting up sandbox: %v", err)
- }
- defer os.RemoveAll(rootDir)
- defer os.RemoveAll(bundleDir)
-
- ws, err := sandbox.Run(uniqueSandboxID(), succSpec, conf, bundleDir, "", "", nil)
- if err != nil {
- t.Fatalf("error running sandbox: %v", err)
- }
- if ws.ExitStatus() != 0 {
- t.Errorf("got exit status %v want %v", ws.ExitStatus(), 0)
- }
-
- // Second sandbox exits with non-zero status.
- wantStatus := 123
- errSpec := newSpecWithArgs("bash", "-c", fmt.Sprintf("exit %d", wantStatus))
-
- rootDir2, bundleDir2, conf, err := setupSandbox(errSpec)
- if err != nil {
- t.Fatalf("error setting up sandbox: %v", err)
- }
- defer os.RemoveAll(rootDir2)
- defer os.RemoveAll(bundleDir2)
-
- ws, err = sandbox.Run(uniqueSandboxID(), succSpec, conf, bundleDir2, "", "", nil)
- if err != nil {
- t.Fatalf("error running sandbox: %v", err)
- }
- if ws.ExitStatus() != wantStatus {
- t.Errorf("got exit status %v want %v", ws.ExitStatus(), wantStatus)
- }
-}
-
-// TestExec verifies that a sandbox can exec a new program.
-func TestExec(t *testing.T) {
- const uid = 343
- spec := newSpecWithArgs("sleep", "100")
-
- rootDir, bundleDir, conf, err := setupSandbox(spec)
- if err != nil {
- t.Fatalf("error setting up sandbox: %v", err)
- }
- defer os.RemoveAll(rootDir)
- defer os.RemoveAll(bundleDir)
-
- // Create and start the sandbox.
- s, err := sandbox.Create(uniqueSandboxID(), spec, conf, bundleDir, "", "", nil)
- if err != nil {
- t.Fatalf("error creating sandbox: %v", err)
- }
- defer s.Destroy()
- if err := s.Start(conf); err != nil {
- t.Fatalf("error starting sandbox: %v", err)
- }
-
- // expectedPL lists the expected process state of the sandbox.
- expectedPL := []*control.Process{
- {
- UID: 0,
- PID: 1,
- PPID: 0,
- C: 0,
- Cmd: "sleep",
- },
- {
- UID: uid,
- PID: 2,
- PPID: 0,
- C: 0,
- Cmd: "sleep",
- },
- }
-
- // Verify that "sleep 100" is running.
- if err := waitForProcessList(s, expectedPL[:1]); err != nil {
- t.Error(err)
- }
-
- execArgs := control.ExecArgs{
- Filename: "/bin/sleep",
- Argv: []string{"sleep", "5"},
- Envv: []string{"PATH=" + os.Getenv("PATH")},
- WorkingDirectory: "/",
- KUID: uid,
- }
-
- // Verify that "sleep 100" and "sleep 5" are running after exec.
- // First, start running exec (whick blocks).
- status := make(chan error, 1)
- go func() {
- exitStatus, err := s.Execute(&execArgs)
- if err != nil {
- status <- err
- } else if exitStatus != 0 {
- status <- fmt.Errorf("failed with exit status: %v", exitStatus)
- } else {
- status <- nil
- }
- }()
-
- if err := waitForProcessList(s, expectedPL); err != nil {
- t.Fatal(err)
- }
-
- // Ensure that exec finished without error.
- select {
- case <-time.After(10 * time.Second):
- t.Fatalf("sandbox timed out waiting for exec to finish.")
- case st := <-status:
- if st != nil {
- t.Errorf("sandbox failed to exec %v: %v", execArgs, 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).
-// - Running exec as non-root with CAP_DAC_OVERRIDE succeeds because it skips
-// this check.
-func TestCapabilities(t *testing.T) {
- const uid = 343
- const gid = 2401
- spec := newSpecWithArgs("sleep", "100")
-
- // We generate files in the host temporary directory.
- spec.Mounts = append(spec.Mounts, specs.Mount{
- Destination: os.TempDir(),
- Source: os.TempDir(),
- Type: "bind",
- })
-
- rootDir, bundleDir, conf, err := setupSandbox(spec)
- if err != nil {
- t.Fatalf("error setting up sandbox: %v", err)
- }
- defer os.RemoveAll(rootDir)
- defer os.RemoveAll(bundleDir)
-
- // Create and start the sandbox.
- s, err := sandbox.Create(uniqueSandboxID(), spec, conf, bundleDir, "", "", nil)
- if err != nil {
- t.Fatalf("error creating sandbox: %v", err)
- }
- defer s.Destroy()
- if err := s.Start(conf); err != nil {
- t.Fatalf("error starting sandbox: %v", err)
- }
-
- // expectedPL lists the expected process state of the sandbox.
- expectedPL := []*control.Process{
- {
- UID: 0,
- PID: 1,
- PPID: 0,
- C: 0,
- Cmd: "sleep",
- },
- {
- UID: uid,
- PID: 2,
- PPID: 0,
- C: 0,
- Cmd: "exe",
- },
- }
- if err := waitForProcessList(s, expectedPL[:1]); err != nil {
- t.Fatalf("Failed to wait for sleep to start, err: %v", err)
- }
-
- // Create an executable that can't be run with the specified UID:GID.
- // This shouldn't be callable within the sandbox until we add the
- // CAP_DAC_OVERRIDE capability to skip the access check.
- exePath := filepath.Join(rootDir, "exe")
- if err := ioutil.WriteFile(exePath, []byte("#!/bin/sh\necho hello"), 0770); err != nil {
- t.Fatalf("couldn't create executable: %v", err)
- }
- defer os.Remove(exePath)
-
- // Need to traverse the intermediate directory.
- os.Chmod(rootDir, 0755)
-
- execArgs := control.ExecArgs{
- Filename: exePath,
- Argv: []string{exePath},
- Envv: []string{"PATH=" + os.Getenv("PATH")},
- WorkingDirectory: "/",
- KUID: uid,
- KGID: gid,
- Capabilities: &auth.TaskCapabilities{},
- }
-
- // "exe" should fail because we don't have the necessary permissions.
- if _, err := s.Execute(&execArgs); err == nil {
- t.Fatalf("sandbox executed without error, but an error was expected")
- }
-
- // Now we run with the capability enabled and should succeed.
- execArgs.Capabilities = &auth.TaskCapabilities{
- EffectiveCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE),
- }
- // "exe" should not fail this time.
- if _, err := s.Execute(&execArgs); err != nil {
- t.Fatalf("sandbox failed to exec %v: %v", execArgs, err)
- }
-}
-
-// Test that an tty FD is sent over the console socket if one is provided.
-func TestConsoleSocket(t *testing.T) {
- spec := newSpecWithArgs("true")
- rootDir, bundleDir, conf, err := setupSandbox(spec)
- if err != nil {
- t.Fatalf("error setting up sandbox: %v", err)
- }
- defer os.RemoveAll(rootDir)
- defer os.RemoveAll(bundleDir)
-
- // Create a named socket and start listening. We use a relative path
- // to avoid overflowing the unix path length limit (108 chars).
- socketPath := filepath.Join(bundleDir, "socket")
- cwd, err := os.Getwd()
- if err != nil {
- t.Fatalf("error getting cwd: %v", err)
- }
- socketRelPath, err := filepath.Rel(cwd, socketPath)
- if err != nil {
- t.Fatalf("error getting relative path for %q from cwd %q: %v", socketPath, cwd, err)
- }
- if len(socketRelPath) > len(socketPath) {
- socketRelPath = socketPath
- }
- srv, err := unet.BindAndListen(socketRelPath, false)
- if err != nil {
- t.Fatalf("error binding and listening to socket %q: %v", socketPath, err)
- }
- defer os.Remove(socketPath)
-
- // Create the sandbox and pass the socket name.
- id := uniqueSandboxID()
- s, err := sandbox.Create(id, spec, conf, bundleDir, socketRelPath, "", nil)
- if err != nil {
- t.Fatalf("error creating sandbox: %v", err)
- }
-
- // Open the othe end of the socket.
- sock, err := srv.Accept()
- if err != nil {
- t.Fatalf("error accepting socket connection: %v", err)
- }
-
- // Allow 3 fds to be received. We only expect 1.
- r := sock.Reader(true /* blocking */)
- r.EnableFDs(1)
-
- // The socket is closed right after sending the FD, so EOF is
- // an allowed error.
- b := [][]byte{{}}
- if _, err := r.ReadVec(b); err != nil && err != io.EOF {
- t.Fatalf("error reading from socket connection: %v", err)
- }
-
- // We should have gotten a control message.
- fds, err := r.ExtractFDs()
- if err != nil {
- t.Fatalf("error extracting fds from socket connection: %v", err)
- }
- if len(fds) != 1 {
- t.Fatalf("got %d fds from socket, wanted 1", len(fds))
- }
-
- // Verify that the fd is a terminal.
- if _, err := unix.IoctlGetTermios(fds[0], unix.TCGETS); err != nil {
- t.Errorf("fd is not a terminal (ioctl TGGETS got %v)", err)
- }
-
- // Shut it down.
- if err := s.Destroy(); err != nil {
- t.Fatalf("error destroying sandbox: %v", err)
- }
-
- // Close socket.
- if err := srv.Close(); err != nil {
- t.Fatalf("error destroying sandbox: %v", err)
- }
-}
-
-func TestSpecUnsupported(t *testing.T) {
- spec := newSpecWithArgs("/bin/true")
- spec.Process.SelinuxLabel = "somelabel"
-
- // These are normally set by docker and will just cause warnings to be logged.
- spec.Process.ApparmorProfile = "someprofile"
- spec.Linux = &specs.Linux{Seccomp: &specs.LinuxSeccomp{}}
-
- rootDir, bundleDir, conf, err := setupSandbox(spec)
- if err != nil {
- t.Fatalf("error setting up sandbox: %v", err)
- }
- defer os.RemoveAll(rootDir)
- defer os.RemoveAll(bundleDir)
-
- id := uniqueSandboxID()
- _, err = sandbox.Create(id, spec, conf, bundleDir, "", "", nil)
- if err == nil || !strings.Contains(err.Error(), "is not supported") {
- t.Errorf("sandbox.Create() wrong error, got: %v, want: *is not supported, spec.Process: %+v", err, spec.Process)
- }
-}
-
-// procListsEqual is used to check whether 2 Process lists are equal for all
-// implemented fields.
-func procListsEqual(got, want []*control.Process) bool {
- if len(got) != len(want) {
- return false
- }
- for i := range got {
- pd1 := got[i]
- pd2 := want[i]
- // Zero out unimplemented and timing dependant fields.
- pd1.Time, pd2.Time = "", ""
- pd1.STime, pd2.STime = "", ""
- pd1.C, pd2.C = 0, 0
- if *pd1 != *pd2 {
- return false
- }
- }
- return true
-}
-
-func procListToString(pl []*control.Process) string {
- strs := make([]string, 0, len(pl))
- for _, p := range pl {
- strs = append(strs, fmt.Sprintf("%+v", p))
- }
- return fmt.Sprintf("[%s]", strings.Join(strs, ","))
-}
-
-// TestMain acts like runsc if it is called with the "boot" argument, otherwise
-// it just runs the tests. This is required because creating a sandbox will
-// call "/proc/self/exe boot". Normally /proc/self/exe is the runsc binary,
-// but for tests we have to fake it.
-func TestMain(m *testing.M) {
- // exit writes coverage data before exiting.
- exit := func(status int) {
- os.Exit(status)
- }
-
- if !flag.Parsed() {
- flag.Parse()
- }
-
- // If we are passed one of the commands then run it.
- subcommands.Register(new(cmd.Boot), "boot")
- subcommands.Register(new(cmd.Gofer), "gofer")
- switch flag.Arg(0) {
- case "boot", "gofer":
- // Run the command in a goroutine so we can block the main
- // thread waiting for shutdownSignal.
- go func() {
- conf := &boot.Config{
- RootDir: "unused-root-dir",
- Network: boot.NetworkNone,
- }
- var ws syscall.WaitStatus
- subcmdCode := subcommands.Execute(context.Background(), conf, &ws)
- if subcmdCode != subcommands.ExitSuccess {
- panic(fmt.Sprintf("command failed to execute, err: %v", subcmdCode))
- }
- // Sandbox exited normally. Shut down this process.
- os.Exit(ws.ExitStatus())
- }()
-
- // Shutdown cleanly when the shutdownSignal is received. This
- // allows us to write coverage data before exiting.
- sigc := make(chan os.Signal, 1)
- signal.Notify(sigc, shutdownSignal)
- <-sigc
- exit(0)
- default:
- // Otherwise run the tests.
- exit(m.Run())
- }
-}
diff --git a/runsc/sandbox/status.go b/runsc/sandbox/status.go
deleted file mode 100644
index 6fc936aba..000000000
--- a/runsc/sandbox/status.go
+++ /dev/null
@@ -1,56 +0,0 @@
-// 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 sandbox
-
-// Status enumerates sandbox statuses. The statuses and their semantics are
-// part of the runtime CLI spec.
-//
-// TODO: Get precise about the transitions between statuses.
-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
-
- // Running indicates "the container process has executed the
- // user-specified program but has not exited".
- Running
-
- // Stopped indicates "the container process has exited".
- Stopped
-)
-
-// String converts a Status to a string. These strings are part of the runtime
-// CLI spec and should not be changed.
-func (s Status) String() string {
- switch s {
- case Creating:
- return "creating"
- case Created:
- return "created"
- case Running:
- return "running"
- case Stopped:
- return "stopped"
- default:
- return "unknown"
- }
-
-}