summaryrefslogtreecommitdiffhomepage
path: root/runsc
diff options
context:
space:
mode:
Diffstat (limited to 'runsc')
-rw-r--r--runsc/BUILD9
-rw-r--r--runsc/cgroup/BUILD4
-rw-r--r--runsc/cmd/BUILD3
-rw-r--r--runsc/cmd/capability_test.go4
-rw-r--r--runsc/cmd/install.go210
-rw-r--r--runsc/container/BUILD2
-rw-r--r--runsc/container/console_test.go2
-rw-r--r--runsc/container/container_test.go33
-rw-r--r--runsc/container/multi_container_test.go2
-rw-r--r--runsc/container/shared_volume_test.go2
-rw-r--r--runsc/container/test_app/BUILD2
-rw-r--r--runsc/container/test_app/fds.go4
-rw-r--r--runsc/container/test_app/test_app.go2
-rw-r--r--runsc/criutil/BUILD12
-rw-r--r--runsc/criutil/criutil.go (renamed from runsc/test/testutil/crictl.go)13
-rwxr-xr-xrunsc/debian/postinst.sh6
-rw-r--r--runsc/dockerutil/BUILD15
-rw-r--r--runsc/dockerutil/dockerutil.go (renamed from runsc/test/testutil/docker.go)77
-rw-r--r--runsc/main.go5
-rw-r--r--runsc/test/BUILD0
-rw-r--r--runsc/test/README.md24
-rw-r--r--runsc/test/build_defs.bzl19
-rw-r--r--runsc/test/image/BUILD31
-rw-r--r--runsc/test/image/image.go16
-rw-r--r--runsc/test/image/image_test.go350
-rw-r--r--runsc/test/image/latin10k.txt33
-rw-r--r--runsc/test/image/mysql.sql23
-rw-r--r--runsc/test/image/ruby.rb23
-rw-r--r--runsc/test/image/ruby.sh20
-rwxr-xr-xrunsc/test/install.sh93
-rw-r--r--runsc/test/integration/BUILD30
-rw-r--r--runsc/test/integration/exec_test.go161
-rw-r--r--runsc/test/integration/integration.go16
-rw-r--r--runsc/test/integration/integration_test.go344
-rw-r--r--runsc/test/integration/regression_test.go45
-rw-r--r--runsc/test/root/BUILD33
-rw-r--r--runsc/test/root/cgroup_test.go237
-rw-r--r--runsc/test/root/chroot_test.go161
-rw-r--r--runsc/test/root/crictl_test.go242
-rw-r--r--runsc/test/root/root.go16
-rw-r--r--runsc/test/root/testdata/BUILD18
-rw-r--r--runsc/test/root/testdata/busybox.go32
-rw-r--r--runsc/test/root/testdata/containerd_config.go39
-rw-r--r--runsc/test/root/testdata/httpd.go32
-rw-r--r--runsc/test/root/testdata/httpd_mount_paths.go53
-rw-r--r--runsc/test/root/testdata/sandbox.go30
-rw-r--r--runsc/test/testutil/testutil_race.go21
-rw-r--r--runsc/testutil/BUILD (renamed from runsc/test/testutil/BUILD)11
-rw-r--r--runsc/testutil/testutil.go (renamed from runsc/test/testutil/testutil.go)37
-rw-r--r--runsc/tools/dockercfg/BUILD10
-rw-r--r--runsc/tools/dockercfg/dockercfg.go193
51 files changed, 370 insertions, 2430 deletions
diff --git a/runsc/BUILD b/runsc/BUILD
index cc8852d7d..a2a465e1e 100644
--- a/runsc/BUILD
+++ b/runsc/BUILD
@@ -67,19 +67,10 @@ pkg_tar(
)
pkg_tar(
- name = "runsc-tools",
- srcs = ["//runsc/tools/dockercfg"],
- mode = "0755",
- package_dir = "/usr/libexec/runsc",
- strip_prefix = "/runsc/tools/dockercfg/linux_amd64_stripped",
-)
-
-pkg_tar(
name = "debian-data",
extension = "tar.gz",
deps = [
":runsc-bin",
- ":runsc-tools",
],
)
diff --git a/runsc/cgroup/BUILD b/runsc/cgroup/BUILD
index ab2387614..d6165f9e5 100644
--- a/runsc/cgroup/BUILD
+++ b/runsc/cgroup/BUILD
@@ -6,9 +6,7 @@ go_library(
name = "cgroup",
srcs = ["cgroup.go"],
importpath = "gvisor.dev/gvisor/runsc/cgroup",
- visibility = [
- "//runsc:__subpackages__",
- ],
+ visibility = ["//:sandbox"],
deps = [
"//pkg/log",
"//runsc/specutils",
diff --git a/runsc/cmd/BUILD b/runsc/cmd/BUILD
index 5223b9972..250845ad7 100644
--- a/runsc/cmd/BUILD
+++ b/runsc/cmd/BUILD
@@ -19,6 +19,7 @@ go_library(
"exec.go",
"gofer.go",
"help.go",
+ "install.go",
"kill.go",
"list.go",
"path.go",
@@ -81,7 +82,7 @@ go_test(
"//runsc/boot",
"//runsc/container",
"//runsc/specutils",
- "//runsc/test/testutil",
+ "//runsc/testutil",
"@com_github_google_go-cmp//cmp:go_default_library",
"@com_github_google_go-cmp//cmp/cmpopts:go_default_library",
"@com_github_opencontainers_runtime-spec//specs-go:go_default_library",
diff --git a/runsc/cmd/capability_test.go b/runsc/cmd/capability_test.go
index 3ae25a257..0c27f7313 100644
--- a/runsc/cmd/capability_test.go
+++ b/runsc/cmd/capability_test.go
@@ -15,6 +15,7 @@
package cmd
import (
+ "flag"
"fmt"
"os"
"testing"
@@ -25,7 +26,7 @@ import (
"gvisor.dev/gvisor/runsc/boot"
"gvisor.dev/gvisor/runsc/container"
"gvisor.dev/gvisor/runsc/specutils"
- "gvisor.dev/gvisor/runsc/test/testutil"
+ "gvisor.dev/gvisor/runsc/testutil"
)
func init() {
@@ -121,6 +122,7 @@ func TestCapabilities(t *testing.T) {
}
func TestMain(m *testing.M) {
+ flag.Parse()
specutils.MaybeRunAsRoot()
os.Exit(m.Run())
}
diff --git a/runsc/cmd/install.go b/runsc/cmd/install.go
new file mode 100644
index 000000000..441c1db0d
--- /dev/null
+++ b/runsc/cmd/install.go
@@ -0,0 +1,210 @@
+// Copyright 2019 The gVisor Authors.
+//
+// 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"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "path"
+
+ "flag"
+ "github.com/google/subcommands"
+)
+
+// Install implements subcommands.Command.
+type Install struct {
+ ConfigFile string
+ Runtime string
+ Experimental bool
+}
+
+// Name implements subcommands.Command.Name.
+func (*Install) Name() string {
+ return "install"
+}
+
+// Synopsis implements subcommands.Command.Synopsis.
+func (*Install) Synopsis() string {
+ return "adds a runtime to docker daemon configuration"
+}
+
+// Usage implements subcommands.Command.Usage.
+func (*Install) Usage() string {
+ return `install [flags] <name> [-- [args...]] -- if provided, args are passed to the runtime
+`
+}
+
+// SetFlags implements subcommands.Command.SetFlags.
+func (i *Install) SetFlags(fs *flag.FlagSet) {
+ fs.StringVar(&i.ConfigFile, "config_file", "/etc/docker/daemon.json", "path to Docker daemon config file")
+ fs.StringVar(&i.Runtime, "runtime", "runsc", "runtime name")
+ fs.BoolVar(&i.Experimental, "experimental", false, "enable experimental features")
+}
+
+// Execute implements subcommands.Command.Execute.
+func (i *Install) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
+ // Grab the name and arguments.
+ runtimeArgs := f.Args()
+
+ // Extract the executable.
+ path, err := os.Executable()
+ if err != nil {
+ log.Fatalf("Error reading current exectuable: %v", err)
+ }
+
+ // Load the configuration file.
+ c, err := readConfig(i.ConfigFile)
+ if err != nil {
+ log.Fatalf("Error reading config file %q: %v", i.ConfigFile, err)
+ }
+
+ // Add the given runtime.
+ var rts map[string]interface{}
+ if i, ok := c["runtimes"]; ok {
+ rts = i.(map[string]interface{})
+ } else {
+ rts = make(map[string]interface{})
+ c["runtimes"] = rts
+ }
+ rts[i.Runtime] = struct {
+ Path string `json:"path,omitempty"`
+ RuntimeArgs []string `json:"runtimeArgs,omitempty"`
+ }{
+ Path: path,
+ RuntimeArgs: runtimeArgs,
+ }
+
+ // Set experimental if required.
+ if i.Experimental {
+ c["experimental"] = true
+ }
+
+ // Write out the runtime.
+ if err := writeConfig(c, i.ConfigFile); err != nil {
+ log.Fatalf("Error writing config file %q: %v", i.ConfigFile, err)
+ }
+
+ // Success.
+ log.Printf("Added runtime %q with arguments %v to %q.", i.Runtime, runtimeArgs, i.ConfigFile)
+ return subcommands.ExitSuccess
+}
+
+// Uninstall implements subcommands.Command.
+type Uninstall struct {
+ ConfigFile string
+ Runtime string
+}
+
+// Name implements subcommands.Command.Name.
+func (*Uninstall) Name() string {
+ return "uninstall"
+}
+
+// Synopsis implements subcommands.Command.Synopsis.
+func (*Uninstall) Synopsis() string {
+ return "removes a runtime from docker daemon configuration"
+}
+
+// Usage implements subcommands.Command.Usage.
+func (*Uninstall) Usage() string {
+ return `uninstall [flags] <name>
+`
+}
+
+// SetFlags implements subcommands.Command.SetFlags.
+func (u *Uninstall) SetFlags(fs *flag.FlagSet) {
+ fs.StringVar(&u.ConfigFile, "config_file", "/etc/docker/daemon.json", "path to Docker daemon config file")
+ fs.StringVar(&u.Runtime, "runtime", "runsc", "runtime name")
+}
+
+// Execute implements subcommands.Command.Execute.
+func (u *Uninstall) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
+ log.Printf("Removing runtime %q from %q.", u.Runtime, u.ConfigFile)
+
+ c, err := readConfig(u.ConfigFile)
+ if err != nil {
+ log.Fatalf("Error reading config file %q: %v", u.ConfigFile, err)
+ }
+
+ var rts map[string]interface{}
+ if i, ok := c["runtimes"]; ok {
+ rts = i.(map[string]interface{})
+ } else {
+ log.Fatalf("runtime %q not found", u.Runtime)
+ }
+ if _, ok := rts[u.Runtime]; !ok {
+ log.Fatalf("runtime %q not found", u.Runtime)
+ }
+ delete(rts, u.Runtime)
+
+ if err := writeConfig(c, u.ConfigFile); err != nil {
+ log.Fatalf("Error writing config file %q: %v", u.ConfigFile, err)
+ }
+ return subcommands.ExitSuccess
+}
+
+func readConfig(path string) (map[string]interface{}, error) {
+ // Read the configuration data.
+ configBytes, err := ioutil.ReadFile(path)
+ if err != nil && !os.IsNotExist(err) {
+ return nil, err
+ }
+
+ // Unmarshal the configuration.
+ c := make(map[string]interface{})
+ if len(configBytes) > 0 {
+ if err := json.Unmarshal(configBytes, &c); err != nil {
+ return nil, err
+ }
+ }
+
+ return c, nil
+}
+
+func writeConfig(c map[string]interface{}, filename string) error {
+ // Marshal the configuration.
+ b, err := json.MarshalIndent(c, "", " ")
+ if err != nil {
+ return err
+ }
+
+ // Copy the old configuration.
+ old, err := ioutil.ReadFile(filename)
+ if err != nil {
+ if !os.IsNotExist(err) {
+ return fmt.Errorf("error reading config file %q: %v", filename, err)
+ }
+ } else {
+ if err := ioutil.WriteFile(filename+"~", old, 0644); err != nil {
+ return fmt.Errorf("error backing up config file %q: %v", filename, err)
+ }
+ }
+
+ // Make the necessary directories.
+ if err := os.MkdirAll(path.Dir(filename), 0755); err != nil {
+ return fmt.Errorf("error creating config directory for %q: %v", filename, err)
+ }
+
+ // Write the new configuration.
+ if err := ioutil.WriteFile(filename, b, 0644); err != nil {
+ return fmt.Errorf("error writing config file %q: %v", filename, err)
+ }
+
+ return nil
+}
diff --git a/runsc/container/BUILD b/runsc/container/BUILD
index de8202bb1..bc1fa25e3 100644
--- a/runsc/container/BUILD
+++ b/runsc/container/BUILD
@@ -56,7 +56,7 @@ go_test(
"//runsc/boot",
"//runsc/boot/platforms",
"//runsc/specutils",
- "//runsc/test/testutil",
+ "//runsc/testutil",
"@com_github_cenkalti_backoff//:go_default_library",
"@com_github_kr_pty//:go_default_library",
"@com_github_opencontainers_runtime-spec//specs-go:go_default_library",
diff --git a/runsc/container/console_test.go b/runsc/container/console_test.go
index e9372989f..7d67c3a75 100644
--- a/runsc/container/console_test.go
+++ b/runsc/container/console_test.go
@@ -30,7 +30,7 @@ import (
"gvisor.dev/gvisor/pkg/sentry/control"
"gvisor.dev/gvisor/pkg/unet"
"gvisor.dev/gvisor/pkg/urpc"
- "gvisor.dev/gvisor/runsc/test/testutil"
+ "gvisor.dev/gvisor/runsc/testutil"
)
// socketPath creates a path inside bundleDir and ensures that the returned
diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go
index 3d4f304f3..2ac12e5b6 100644
--- a/runsc/container/container_test.go
+++ b/runsc/container/container_test.go
@@ -16,6 +16,7 @@ package container
import (
"bytes"
+ "flag"
"fmt"
"io"
"io/ioutil"
@@ -39,7 +40,7 @@ import (
"gvisor.dev/gvisor/runsc/boot"
"gvisor.dev/gvisor/runsc/boot/platforms"
"gvisor.dev/gvisor/runsc/specutils"
- "gvisor.dev/gvisor/runsc/test/testutil"
+ "gvisor.dev/gvisor/runsc/testutil"
)
// waitForProcessList waits for the given process list to show up in the container.
@@ -155,12 +156,7 @@ func waitForFile(f *os.File) error {
return nil
}
- timeout := 5 * time.Second
- if testutil.RaceEnabled {
- // Race makes slow things even slow, so bump the timeout.
- timeout = 3 * timeout
- }
- return testutil.Poll(op, timeout)
+ return testutil.Poll(op, 30*time.Second)
}
// readOutputNum reads a file at given filepath and returns the int at the
@@ -254,10 +250,6 @@ func configs(opts ...configOption) []*boot.Config {
// TODO(b/112165693): KVM tests are flaky. Disable until fixed.
continue
- // TODO(b/68787993): KVM doesn't work with --race.
- if testutil.RaceEnabled {
- continue
- }
c.Platform = platforms.KVM
case nonExclusiveFS:
c.FileAccess = boot.FileAccessShared
@@ -1651,22 +1643,27 @@ func TestGoferExits(t *testing.T) {
}
func TestRootNotMount(t *testing.T) {
- if testutil.RaceEnabled {
- // Requires statically linked binary, since it's mapping the root to a
- // random dir, libs cannot be located.
- t.Skip("race makes test_app not statically linked")
- }
-
appSym, err := testutil.FindFile("runsc/container/test_app/test_app")
if err != nil {
t.Fatal("error finding test_app:", err)
}
+
app, err := filepath.EvalSymlinks(appSym)
if err != nil {
t.Fatalf("error resolving %q symlink: %v", appSym, err)
}
log.Infof("App path %q is a symlink to %q", appSym, app)
+ static, err := testutil.IsStatic(app)
+ if err != nil {
+ t.Fatalf("error reading application binary: %v", err)
+ }
+ if !static {
+ // This happens during race builds; we cannot map in shared
+ // libraries also, so we need to skip the test.
+ t.Skip()
+ }
+
root := filepath.Dir(app)
exe := "/" + filepath.Base(app)
log.Infof("Executing %q in %q", exe, root)
@@ -2067,10 +2064,10 @@ func (cont *Container) executeSync(args *control.ExecArgs) (syscall.WaitStatus,
func TestMain(m *testing.M) {
log.SetLevel(log.Debug)
+ flag.Parse()
if err := testutil.ConfigureExePath(); err != nil {
panic(err.Error())
}
specutils.MaybeRunAsRoot()
-
os.Exit(m.Run())
}
diff --git a/runsc/container/multi_container_test.go b/runsc/container/multi_container_test.go
index ae03d24b4..6e5f23ff2 100644
--- a/runsc/container/multi_container_test.go
+++ b/runsc/container/multi_container_test.go
@@ -32,7 +32,7 @@ import (
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/runsc/boot"
"gvisor.dev/gvisor/runsc/specutils"
- "gvisor.dev/gvisor/runsc/test/testutil"
+ "gvisor.dev/gvisor/runsc/testutil"
)
func createSpecs(cmds ...[]string) ([]*specs.Spec, []string) {
diff --git a/runsc/container/shared_volume_test.go b/runsc/container/shared_volume_test.go
index 1f90d2462..dc4194134 100644
--- a/runsc/container/shared_volume_test.go
+++ b/runsc/container/shared_volume_test.go
@@ -25,7 +25,7 @@ import (
"gvisor.dev/gvisor/pkg/sentry/control"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/runsc/boot"
- "gvisor.dev/gvisor/runsc/test/testutil"
+ "gvisor.dev/gvisor/runsc/testutil"
)
// TestSharedVolume checks that modifications to a volume mount are propagated
diff --git a/runsc/container/test_app/BUILD b/runsc/container/test_app/BUILD
index 82dbd54d2..9bf9e6e9d 100644
--- a/runsc/container/test_app/BUILD
+++ b/runsc/container/test_app/BUILD
@@ -13,7 +13,7 @@ go_binary(
visibility = ["//runsc/container:__pkg__"],
deps = [
"//pkg/unet",
- "//runsc/test/testutil",
+ "//runsc/testutil",
"@com_github_google_subcommands//:go_default_library",
],
)
diff --git a/runsc/container/test_app/fds.go b/runsc/container/test_app/fds.go
index c12809cab..a90cc1662 100644
--- a/runsc/container/test_app/fds.go
+++ b/runsc/container/test_app/fds.go
@@ -24,7 +24,7 @@ import (
"flag"
"github.com/google/subcommands"
"gvisor.dev/gvisor/pkg/unet"
- "gvisor.dev/gvisor/runsc/test/testutil"
+ "gvisor.dev/gvisor/runsc/testutil"
)
const fileContents = "foobarbaz"
@@ -60,7 +60,7 @@ func (fds *fdSender) Execute(ctx context.Context, f *flag.FlagSet, args ...inter
log.Fatalf("socket flag must be set")
}
- dir, err := ioutil.TempDir(testutil.TmpDir(), "")
+ dir, err := ioutil.TempDir("", "")
if err != nil {
log.Fatalf("TempDir failed: %v", err)
}
diff --git a/runsc/container/test_app/test_app.go b/runsc/container/test_app/test_app.go
index 6578c7b41..7f735c254 100644
--- a/runsc/container/test_app/test_app.go
+++ b/runsc/container/test_app/test_app.go
@@ -29,7 +29,7 @@ import (
"flag"
"github.com/google/subcommands"
- "gvisor.dev/gvisor/runsc/test/testutil"
+ "gvisor.dev/gvisor/runsc/testutil"
)
func main() {
diff --git a/runsc/criutil/BUILD b/runsc/criutil/BUILD
new file mode 100644
index 000000000..558133a0e
--- /dev/null
+++ b/runsc/criutil/BUILD
@@ -0,0 +1,12 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+package(licenses = ["notice"])
+
+go_library(
+ name = "criutil",
+ testonly = 1,
+ srcs = ["criutil.go"],
+ importpath = "gvisor.dev/gvisor/runsc/criutil",
+ visibility = ["//:sandbox"],
+ deps = ["//runsc/testutil"],
+)
diff --git a/runsc/test/testutil/crictl.go b/runsc/criutil/criutil.go
index 4f9ee0c05..c8ddf5a9a 100644
--- a/runsc/test/testutil/crictl.go
+++ b/runsc/criutil/criutil.go
@@ -12,7 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package testutil
+// Package criutil contains utility functions for interacting with the
+// Container Runtime Interface (CRI), principally via the crictl command line
+// tool. This requires critools to be installed on the local system.
+package criutil
import (
"encoding/json"
@@ -21,6 +24,8 @@ import (
"os/exec"
"strings"
"time"
+
+ "gvisor.dev/gvisor/runsc/testutil"
)
const endpointPrefix = "unix://"
@@ -160,11 +165,11 @@ func (cc *Crictl) StartPodAndContainer(image, sbSpec, contSpec string) (string,
}
// Write the specs to files that can be read by crictl.
- sbSpecFile, err := WriteTmpFile("sbSpec", sbSpec)
+ sbSpecFile, err := testutil.WriteTmpFile("sbSpec", sbSpec)
if err != nil {
return "", "", fmt.Errorf("failed to write sandbox spec: %v", err)
}
- contSpecFile, err := WriteTmpFile("contSpec", contSpec)
+ contSpecFile, err := testutil.WriteTmpFile("contSpec", contSpec)
if err != nil {
return "", "", fmt.Errorf("failed to write container spec: %v", err)
}
@@ -233,7 +238,7 @@ func (cc *Crictl) run(args ...string) (string, error) {
case err := <-errCh:
return "", err
case <-time.After(cc.timeout):
- if err := KillCommand(cmd); err != nil {
+ if err := testutil.KillCommand(cmd); err != nil {
return "", fmt.Errorf("timed out, then couldn't kill process %+v: %v", cmd, err)
}
return "", fmt.Errorf("timed out: %+v", cmd)
diff --git a/runsc/debian/postinst.sh b/runsc/debian/postinst.sh
index 03a5ff524..dc7aeee87 100755
--- a/runsc/debian/postinst.sh
+++ b/runsc/debian/postinst.sh
@@ -15,10 +15,10 @@
# limitations under the License.
if [ "$1" != configure ]; then
- exit 0
+ exit 0
fi
if [ -f /etc/docker/daemon.json ]; then
- /usr/libexec/runsc/dockercfg runtime-add runsc /usr/bin/runsc
- systemctl restart docker
+ runsc install
+ systemctl restart docker || echo "unable to restart docker; you must do so manually." >&2
fi
diff --git a/runsc/dockerutil/BUILD b/runsc/dockerutil/BUILD
new file mode 100644
index 000000000..0e0423504
--- /dev/null
+++ b/runsc/dockerutil/BUILD
@@ -0,0 +1,15 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+package(licenses = ["notice"])
+
+go_library(
+ name = "dockerutil",
+ testonly = 1,
+ srcs = ["dockerutil.go"],
+ importpath = "gvisor.dev/gvisor/runsc/dockerutil",
+ visibility = ["//:sandbox"],
+ deps = [
+ "//runsc/testutil",
+ "@com_github_kr_pty//:go_default_library",
+ ],
+)
diff --git a/runsc/test/testutil/docker.go b/runsc/dockerutil/dockerutil.go
index 94e625259..41f5fe1e8 100644
--- a/runsc/test/testutil/docker.go
+++ b/runsc/dockerutil/dockerutil.go
@@ -12,9 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package testutil
+// Package dockerutil is a collection of utility functions, primarily for
+// testing.
+package dockerutil
import (
+ "encoding/json"
"flag"
"fmt"
"io/ioutil"
@@ -29,26 +32,13 @@ import (
"time"
"github.com/kr/pty"
+ "gvisor.dev/gvisor/runsc/testutil"
)
-var runtimeType = flag.String("runtime-type", "", "specify which runtime to use: kvm, hostnet, overlay")
-
-func getRuntime() string {
- r, ok := os.LookupEnv("RUNSC_RUNTIME")
- if !ok {
- r = "runsc-test"
- }
- if *runtimeType != "" {
- r += "-" + *runtimeType
- }
- return r
-}
-
-// IsPauseResumeSupported returns true if Pause/Resume is supported by runtime.
-func IsPauseResumeSupported() bool {
- // Native host network stack can't be saved.
- return !strings.Contains(getRuntime(), "hostnet")
-}
+var (
+ runtime = flag.String("runtime", "runsc", "specify which runtime to use")
+ config = flag.String("config_path", "/etc/docker/daemon.json", "configuration file for reading paths")
+)
// EnsureSupportedDockerVersion checks if correct docker is installed.
func EnsureSupportedDockerVersion() {
@@ -69,6 +59,48 @@ func EnsureSupportedDockerVersion() {
}
}
+// RuntimePath returns the binary path for the current runtime.
+func RuntimePath() (string, error) {
+ // Read the configuration data; the file must exist.
+ configBytes, err := ioutil.ReadFile(*config)
+ if err != nil {
+ return "", err
+ }
+
+ // Unmarshal the configuration.
+ c := make(map[string]interface{})
+ if err := json.Unmarshal(configBytes, &c); err != nil {
+ return "", err
+ }
+
+ // Decode the expected configuration.
+ r, ok := c["runtimes"]
+ if !ok {
+ return "", fmt.Errorf("no runtimes declared: %v", c)
+ }
+ rs, ok := r.(map[string]interface{})
+ if !ok {
+ // The runtimes are not a map.
+ return "", fmt.Errorf("unexpected format: %v", c)
+ }
+ r, ok = rs[*runtime]
+ if !ok {
+ // The expected runtime is not declared.
+ return "", fmt.Errorf("runtime %q not found: %v", *runtime, c)
+ }
+ rs, ok = r.(map[string]interface{})
+ if !ok {
+ // The runtime is not a map.
+ return "", fmt.Errorf("unexpected format: %v", c)
+ }
+ p, ok := rs["path"].(string)
+ if !ok {
+ // The runtime does not declare a path.
+ return "", fmt.Errorf("unexpected format: %v", c)
+ }
+ return p, nil
+}
+
// MountMode describes if the mount should be ro or rw.
type MountMode int
@@ -113,7 +145,7 @@ func PrepareFiles(names ...string) (string, error) {
for _, name := range names {
src := getLocalPath(name)
dst := path.Join(dir, name)
- if err := Copy(src, dst); err != nil {
+ if err := testutil.Copy(src, dst); err != nil {
return "", fmt.Errorf("testutil.Copy(%q, %q) failed: %v", src, dst, err)
}
}
@@ -163,7 +195,10 @@ type Docker struct {
// MakeDocker sets up the struct for a Docker container.
// Names of containers will be unique.
func MakeDocker(namePrefix string) Docker {
- return Docker{Name: RandomName(namePrefix), Runtime: getRuntime()}
+ return Docker{
+ Name: testutil.RandomName(namePrefix),
+ Runtime: *runtime,
+ }
}
// logDockerID logs a container id, which is needed to find container runsc logs.
diff --git a/runsc/main.go b/runsc/main.go
index 70f06dbb8..0ff68160d 100644
--- a/runsc/main.go
+++ b/runsc/main.go
@@ -88,6 +88,11 @@ func main() {
subcommands.Register(help, "")
subcommands.Register(subcommands.FlagsCommand(), "")
+ // Installation helpers.
+ const helperGroup = "helpers"
+ subcommands.Register(new(cmd.Install), helperGroup)
+ subcommands.Register(new(cmd.Uninstall), helperGroup)
+
// Register user-facing runsc commands.
subcommands.Register(new(cmd.Checkpoint), "")
subcommands.Register(new(cmd.Create), "")
diff --git a/runsc/test/BUILD b/runsc/test/BUILD
deleted file mode 100644
index e69de29bb..000000000
--- a/runsc/test/BUILD
+++ /dev/null
diff --git a/runsc/test/README.md b/runsc/test/README.md
deleted file mode 100644
index f22a8e017..000000000
--- a/runsc/test/README.md
+++ /dev/null
@@ -1,24 +0,0 @@
-# Tests
-
-The tests defined under this path are verifying functionality beyond what unit
-tests can cover, e.g. integration and end to end tests. Due to their nature,
-they may need extra setup in the test machine and extra configuration to run.
-
-- **integration:** defines integration tests that uses `docker run` to test
- functionality.
-- **image:** basic end to end test for popular images.
-- **root:** tests that require to be run as root.
-- **testutil:** utilities library to support the tests.
-
-The following setup steps are required in order to run these tests:
-
- `./runsc/test/install.sh [--runtime <name>]`
-
-The tests expect the runtime name to be provided in the `RUNSC_RUNTIME`
-environment variable (default: `runsc-test`). To run the tests execute:
-
-```
-bazel test --test_env=RUNSC_RUNTIME=runsc-test \
- //runsc/test/image:image_test \
- //runsc/test/integration:integration_test
-```
diff --git a/runsc/test/build_defs.bzl b/runsc/test/build_defs.bzl
deleted file mode 100644
index ac28cc037..000000000
--- a/runsc/test/build_defs.bzl
+++ /dev/null
@@ -1,19 +0,0 @@
-"""Defines a rule for runsc test targets."""
-
-load("@io_bazel_rules_go//go:def.bzl", _go_test = "go_test")
-
-# runtime_test is a macro that will create targets to run the given test target
-# with different runtime options.
-def runtime_test(**kwargs):
- """Runs the given test target with different runtime options."""
- name = kwargs["name"]
- _go_test(**kwargs)
- kwargs["name"] = name + "_hostnet"
- kwargs["args"] = ["--runtime-type=hostnet"]
- _go_test(**kwargs)
- kwargs["name"] = name + "_kvm"
- kwargs["args"] = ["--runtime-type=kvm"]
- _go_test(**kwargs)
- kwargs["name"] = name + "_overlay"
- kwargs["args"] = ["--runtime-type=overlay"]
- _go_test(**kwargs)
diff --git a/runsc/test/image/BUILD b/runsc/test/image/BUILD
deleted file mode 100644
index 58758fde5..000000000
--- a/runsc/test/image/BUILD
+++ /dev/null
@@ -1,31 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-load("//runsc/test:build_defs.bzl", "runtime_test")
-
-package(licenses = ["notice"])
-
-runtime_test(
- name = "image_test",
- size = "large",
- srcs = [
- "image_test.go",
- ],
- data = [
- "latin10k.txt",
- "mysql.sql",
- "ruby.rb",
- "ruby.sh",
- ],
- embed = [":image"],
- tags = [
- # Requires docker and runsc to be configured before the test runs.
- "manual",
- "local",
- ],
- deps = ["//runsc/test/testutil"],
-)
-
-go_library(
- name = "image",
- srcs = ["image.go"],
- importpath = "gvisor.dev/gvisor/runsc/test/image",
-)
diff --git a/runsc/test/image/image.go b/runsc/test/image/image.go
deleted file mode 100644
index 297f1ab92..000000000
--- a/runsc/test/image/image.go
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// 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 image is empty. See image_test.go for description.
-package image
diff --git a/runsc/test/image/image_test.go b/runsc/test/image/image_test.go
deleted file mode 100644
index ddaa2c13b..000000000
--- a/runsc/test/image/image_test.go
+++ /dev/null
@@ -1,350 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// 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 image provides end-to-end image tests for runsc.
-
-// Each test calls docker commands to start up a container, and tests that it is
-// behaving properly, like connecting to a port or looking at the output. The
-// container is killed and deleted at the end.
-//
-// Setup instruction in runsc/test/README.md.
-package image
-
-import (
- "fmt"
- "io/ioutil"
- "log"
- "net/http"
- "os"
- "path/filepath"
- "strings"
- "testing"
- "time"
-
- "gvisor.dev/gvisor/runsc/test/testutil"
-)
-
-func TestHelloWorld(t *testing.T) {
- d := testutil.MakeDocker("hello-test")
- if err := d.Run("hello-world"); err != nil {
- t.Fatalf("docker run failed: %v", err)
- }
- defer d.CleanUp()
-
- if _, err := d.WaitForOutput("Hello from Docker!", 5*time.Second); err != nil {
- t.Fatalf("docker didn't say hello: %v", err)
- }
-}
-
-func runHTTPRequest(port int) error {
- url := fmt.Sprintf("http://localhost:%d/not-found", port)
- resp, err := http.Get(url)
- if err != nil {
- return fmt.Errorf("error reaching http server: %v", err)
- }
- if want := http.StatusNotFound; resp.StatusCode != want {
- return fmt.Errorf("Wrong response code, got: %d, want: %d", resp.StatusCode, want)
- }
-
- url = fmt.Sprintf("http://localhost:%d/latin10k.txt", port)
- resp, err = http.Get(url)
- if err != nil {
- return fmt.Errorf("Error reaching http server: %v", err)
- }
- if want := http.StatusOK; resp.StatusCode != want {
- return fmt.Errorf("Wrong response code, got: %d, want: %d", resp.StatusCode, want)
- }
-
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return fmt.Errorf("Error reading http response: %v", err)
- }
- defer resp.Body.Close()
-
- // READALL is the last word in the file. Ensures everything was read.
- if want := "READALL"; strings.HasSuffix(string(body), want) {
- return fmt.Errorf("response doesn't contain %q, resp: %q", want, body)
- }
- return nil
-}
-
-func testHTTPServer(t *testing.T, port int) {
- const requests = 10
- ch := make(chan error, requests)
- for i := 0; i < requests; i++ {
- go func() {
- start := time.Now()
- err := runHTTPRequest(port)
- log.Printf("Response time %v: %v", time.Since(start).String(), err)
- ch <- err
- }()
- }
-
- for i := 0; i < requests; i++ {
- err := <-ch
- if err != nil {
- t.Errorf("testHTTPServer(%d) failed: %v", port, err)
- }
- }
-}
-
-func TestHttpd(t *testing.T) {
- if err := testutil.Pull("httpd"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := testutil.MakeDocker("http-test")
-
- dir, err := testutil.PrepareFiles("latin10k.txt")
- if err != nil {
- t.Fatalf("PrepareFiles() failed: %v", err)
- }
-
- // Start the container.
- mountArg := testutil.MountArg(dir, "/usr/local/apache2/htdocs", testutil.ReadOnly)
- if err := d.Run("-p", "80", mountArg, "httpd"); err != nil {
- t.Fatalf("docker run failed: %v", err)
- }
- defer d.CleanUp()
-
- // Find where port 80 is mapped to.
- port, err := d.FindPort(80)
- if err != nil {
- t.Fatalf("docker.FindPort(80) failed: %v", err)
- }
-
- // Wait until it's up and running.
- if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
- t.Errorf("WaitForHTTP() timeout: %v", err)
- }
-
- testHTTPServer(t, port)
-}
-
-func TestNginx(t *testing.T) {
- if err := testutil.Pull("nginx"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := testutil.MakeDocker("net-test")
-
- dir, err := testutil.PrepareFiles("latin10k.txt")
- if err != nil {
- t.Fatalf("PrepareFiles() failed: %v", err)
- }
-
- // Start the container.
- mountArg := testutil.MountArg(dir, "/usr/share/nginx/html", testutil.ReadOnly)
- if err := d.Run("-p", "80", mountArg, "nginx"); err != nil {
- t.Fatalf("docker run failed: %v", err)
- }
- defer d.CleanUp()
-
- // Find where port 80 is mapped to.
- port, err := d.FindPort(80)
- if err != nil {
- t.Fatalf("docker.FindPort(80) failed: %v", err)
- }
-
- // Wait until it's up and running.
- if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
- t.Errorf("WaitForHTTP() timeout: %v", err)
- }
-
- testHTTPServer(t, port)
-}
-
-func TestMysql(t *testing.T) {
- if err := testutil.Pull("mysql"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := testutil.MakeDocker("mysql-test")
-
- // Start the container.
- if err := d.Run("-e", "MYSQL_ROOT_PASSWORD=foobar123", "mysql"); err != nil {
- t.Fatalf("docker run failed: %v", err)
- }
- defer d.CleanUp()
-
- // Wait until it's up and running.
- if _, err := d.WaitForOutput("port: 3306 MySQL Community Server", 3*time.Minute); err != nil {
- t.Fatalf("docker.WaitForOutput() timeout: %v", err)
- }
-
- client := testutil.MakeDocker("mysql-client-test")
- dir, err := testutil.PrepareFiles("mysql.sql")
- if err != nil {
- t.Fatalf("PrepareFiles() failed: %v", err)
- }
-
- // Tell mysql client to connect to the server and execute the file in verbose
- // mode to verify the output.
- args := []string{
- testutil.LinkArg(&d, "mysql"),
- testutil.MountArg(dir, "/sql", testutil.ReadWrite),
- "mysql",
- "mysql", "-hmysql", "-uroot", "-pfoobar123", "-v", "-e", "source /sql/mysql.sql",
- }
- if err := client.Run(args...); err != nil {
- t.Fatalf("docker run failed: %v", err)
- }
- defer client.CleanUp()
-
- // Ensure file executed to the end and shutdown mysql.
- if _, err := client.WaitForOutput("--------------\nshutdown\n--------------", 15*time.Second); err != nil {
- t.Fatalf("docker.WaitForOutput() timeout: %v", err)
- }
- if _, err := d.WaitForOutput("mysqld: Shutdown complete", 30*time.Second); err != nil {
- t.Fatalf("docker.WaitForOutput() timeout: %v", err)
- }
-}
-
-func TestPythonHello(t *testing.T) {
- // TODO(b/136503277): Once we have more complete python runtime tests,
- // we can drop this one.
- const img = "gcr.io/gvisor-presubmit/python-hello"
- if err := testutil.Pull(img); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := testutil.MakeDocker("python-hello-test")
- if err := d.Run("-p", "8080", img); err != nil {
- t.Fatalf("docker run failed: %v", err)
- }
- defer d.CleanUp()
-
- // Find where port 8080 is mapped to.
- port, err := d.FindPort(8080)
- if err != nil {
- t.Fatalf("docker.FindPort(8080) failed: %v", err)
- }
-
- // Wait until it's up and running.
- if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
- t.Fatalf("WaitForHTTP() timeout: %v", err)
- }
-
- // Ensure that content is being served.
- url := fmt.Sprintf("http://localhost:%d", port)
- resp, err := http.Get(url)
- if err != nil {
- t.Errorf("Error reaching http server: %v", err)
- }
- if want := http.StatusOK; resp.StatusCode != want {
- t.Errorf("Wrong response code, got: %d, want: %d", resp.StatusCode, want)
- }
-}
-
-func TestTomcat(t *testing.T) {
- if err := testutil.Pull("tomcat:8.0"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := testutil.MakeDocker("tomcat-test")
- if err := d.Run("-p", "8080", "tomcat:8.0"); err != nil {
- t.Fatalf("docker run failed: %v", err)
- }
- defer d.CleanUp()
-
- // Find where port 8080 is mapped to.
- port, err := d.FindPort(8080)
- if err != nil {
- t.Fatalf("docker.FindPort(8080) failed: %v", err)
- }
-
- // Wait until it's up and running.
- if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
- t.Fatalf("WaitForHTTP() timeout: %v", err)
- }
-
- // Ensure that content is being served.
- url := fmt.Sprintf("http://localhost:%d", port)
- resp, err := http.Get(url)
- if err != nil {
- t.Errorf("Error reaching http server: %v", err)
- }
- if want := http.StatusOK; resp.StatusCode != want {
- t.Errorf("Wrong response code, got: %d, want: %d", resp.StatusCode, want)
- }
-}
-
-func TestRuby(t *testing.T) {
- if err := testutil.Pull("ruby"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := testutil.MakeDocker("ruby-test")
-
- dir, err := testutil.PrepareFiles("ruby.rb", "ruby.sh")
- if err != nil {
- t.Fatalf("PrepareFiles() failed: %v", err)
- }
- if err := os.Chmod(filepath.Join(dir, "ruby.sh"), 0333); err != nil {
- t.Fatalf("os.Chmod(%q, 0333) failed: %v", dir, err)
- }
-
- if err := d.Run("-p", "8080", testutil.MountArg(dir, "/src", testutil.ReadOnly), "ruby", "/src/ruby.sh"); err != nil {
- t.Fatalf("docker run failed: %v", err)
- }
- defer d.CleanUp()
-
- // Find where port 8080 is mapped to.
- port, err := d.FindPort(8080)
- if err != nil {
- t.Fatalf("docker.FindPort(8080) failed: %v", err)
- }
-
- // Wait until it's up and running, 'gem install' can take some time.
- if err := testutil.WaitForHTTP(port, 1*time.Minute); err != nil {
- t.Fatalf("WaitForHTTP() timeout: %v", err)
- }
-
- // Ensure that content is being served.
- url := fmt.Sprintf("http://localhost:%d", port)
- resp, err := http.Get(url)
- if err != nil {
- t.Errorf("error reaching http server: %v", err)
- }
- if want := http.StatusOK; resp.StatusCode != want {
- t.Errorf("wrong response code, got: %d, want: %d", resp.StatusCode, want)
- }
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Fatalf("error reading body: %v", err)
- }
- if got, want := string(body), "Hello World"; !strings.Contains(got, want) {
- t.Errorf("invalid body content, got: %q, want: %q", got, want)
- }
-}
-
-func TestStdio(t *testing.T) {
- if err := testutil.Pull("alpine"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := testutil.MakeDocker("stdio-test")
-
- wantStdout := "hello stdout"
- wantStderr := "bonjour stderr"
- cmd := fmt.Sprintf("echo %q; echo %q 1>&2;", wantStdout, wantStderr)
- if err := d.Run("alpine", "/bin/sh", "-c", cmd); err != nil {
- t.Fatalf("docker run failed: %v", err)
- }
- defer d.CleanUp()
-
- for _, want := range []string{wantStdout, wantStderr} {
- if _, err := d.WaitForOutput(want, 5*time.Second); err != nil {
- t.Fatalf("docker didn't get output %q : %v", want, err)
- }
- }
-}
-
-func TestMain(m *testing.M) {
- testutil.EnsureSupportedDockerVersion()
- os.Exit(m.Run())
-}
diff --git a/runsc/test/image/latin10k.txt b/runsc/test/image/latin10k.txt
deleted file mode 100644
index 61341e00b..000000000
--- a/runsc/test/image/latin10k.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras ut placerat felis. Maecenas urna est, auctor a efficitur sit amet, egestas et augue. Curabitur dignissim scelerisque nunc vel cursus. Ut vehicula est pretium, consectetur nunc non, pharetra ligula. Curabitur ut ultricies metus. Suspendisse pulvinar, orci sed fermentum vestibulum, eros turpis molestie lectus, nec elementum risus dolor mattis felis. Donec ultrices ipsum sem, at pretium lacus convallis at. Mauris nulla enim, tincidunt non bibendum at, vehicula pulvinar mauris.
-
-Duis in dapibus turpis. Pellentesque maximus magna odio, ac congue libero laoreet quis. Maecenas euismod risus in justo aliquam accumsan. Nunc quis ornare arcu, sit amet sodales elit. Phasellus nec scelerisque nisl, a tincidunt arcu. Proin ornare est nunc, sed suscipit orci interdum et. Suspendisse condimentum venenatis diam in tempor. Aliquam egestas lectus in rutrum tempus. Donec id egestas eros. Donec molestie consequat purus, sed posuere odio venenatis vitae. Nunc placerat augue id vehicula varius. In hac habitasse platea dictumst. Proin at est accumsan, venenatis quam a, fermentum risus. Phasellus posuere pellentesque enim, id suscipit magna consequat ut. Quisque ut tortor ante.
-
-Cras ut vulputate metus, a laoreet lectus. Vivamus ultrices molestie odio in tristique. Morbi faucibus mi eget sollicitudin fringilla. Fusce vitae lacinia ligula. Sed egestas sed diam eu posuere. Maecenas justo nisl, venenatis vel nibh vel, cursus aliquam velit. Praesent lacinia dui id erat venenatis rhoncus. Morbi gravida felis ante, sit amet vehicula orci rhoncus vitae.
-
-Sed finibus sagittis dictum. Proin auctor suscipit sem et mattis. Phasellus libero ligula, pellentesque ut felis porttitor, fermentum sollicitudin orci. Nulla eu nulla nibh. Fusce a eros risus. Proin vel magna risus. Donec nec elit eleifend, scelerisque sapien vitae, pharetra quam. Donec porttitor mauris scelerisque, tempus orci hendrerit, dapibus felis. Nullam libero elit, sollicitudin a aliquam at, ultrices in erat. Mauris eget ligula sodales, porta turpis et, scelerisque odio. Mauris mollis leo vitae purus gravida, in tempor nunc efficitur. Nulla facilisis posuere augue, nec pellentesque lectus eleifend ac. Vestibulum convallis est a feugiat tincidunt. Donec vitae enim volutpat, tincidunt eros eu, malesuada nibh.
-
-Quisque molestie, magna ornare elementum convallis, erat enim sagittis ipsum, eget porttitor sapien arcu id purus. Donec ut cursus diam. Nulla rutrum nulla et mi fermentum, vel tempus tellus posuere. Proin vitae pharetra nulla, nec ornare ex. Nulla consequat, augue a accumsan euismod, turpis leo ornare ligula, a pulvinar enim dolor ut augue. Quisque volutpat, lectus a varius mollis, nisl eros feugiat sem, at egestas lacus justo eu elit. Vestibulum scelerisque mauris est, sagittis interdum nunc accumsan sit amet. Maecenas aliquet ex ut lacus ornare, eu sagittis nibh imperdiet. Duis ultrices nisi velit, sed sodales risus sollicitudin et. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Etiam a accumsan augue, vitae pulvinar nulla. Pellentesque euismod sodales magna, nec luctus eros mattis eget. Sed lacinia suscipit lectus, eget consectetur dui pellentesque sed. Nullam nec mattis tellus.
-
-Aliquam erat volutpat. Praesent lobortis massa porttitor eros tincidunt, nec consequat diam pharetra. Duis efficitur non lorem sed mattis. Suspendisse justo nunc, pulvinar eu porttitor at, facilisis id eros. Suspendisse potenti. Cras molestie aliquet orci ut fermentum. In tempus aliquet eros nec suscipit. Suspendisse in mauris ut lectus ultrices blandit sit amet vitae est. Nam magna massa, porttitor ut semper id, feugiat vel quam. Suspendisse dignissim posuere scelerisque. Donec scelerisque lorem efficitur suscipit suscipit. Nunc luctus ligula et scelerisque lacinia.
-
-Suspendisse potenti. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed ultrices, sem in venenatis scelerisque, tellus ipsum porttitor urna, et iaculis lectus odio ac nisi. Integer luctus dui urna, at sollicitudin elit dapibus eu. Praesent nibh ante, porttitor a ante in, ullamcorper pretium felis. Aliquam vel tortor imperdiet, imperdiet lorem et, cursus mi. Proin tempus velit est, ut hendrerit metus gravida sed. Sed nibh sapien, faucibus quis ipsum in, scelerisque lacinia elit. In nec magna eu magna laoreet rhoncus. Donec vitae rutrum mauris. Integer urna felis, consequat at rhoncus vitae, auctor quis elit. Duis a pulvinar sem, nec gravida nisl. Nam non dapibus purus. Praesent vestibulum turpis nec erat porttitor, a scelerisque purus tincidunt.
-
-Nam fringilla leo nisi, nec placerat nisl luctus eget. Aenean malesuada nunc porta sapien sodales convallis. Suspendisse ut massa tempor, ullamcorper mi ut, faucibus turpis. Vivamus at sagittis metus. Donec varius ac mi eget sodales. Nulla feugiat, nulla eu fringilla fringilla, nunc lorem sollicitudin quam, vitae lacinia velit lorem eu orci. Mauris leo urna, pellentesque ac posuere non, pellentesque sit amet quam.
-
-Vestibulum porta diam urna, a aliquet nibh vestibulum et. Proin interdum bibendum nisl sed rhoncus. Sed vel diam hendrerit, faucibus ante et, hendrerit diam. Nunc dolor augue, mattis non dolor vel, luctus sodales neque. Cras malesuada fermentum dolor eu lobortis. Integer dapibus volutpat consequat. Maecenas posuere feugiat nunc. Donec vel mollis elit, volutpat consequat enim. Nulla id nisi finibus orci imperdiet elementum. Phasellus ultrices, elit vitae consequat rutrum, nisl est congue massa, quis condimentum justo nisi vitae turpis. Maecenas aliquet risus sit amet accumsan elementum. Proin non finibus elit, sit amet lobortis augue.
-
-Morbi pretium pulvinar sem vel sollicitudin. Proin imperdiet fringilla leo, non pellentesque lacus gravida nec. Vivamus ullamcorper consectetur ligula eu consectetur. Curabitur sit amet tempus purus. Curabitur quam quam, tincidunt eu tempus vel, volutpat at ipsum. Maecenas lobortis elit ac justo interdum, sit amet mattis ligula mollis. Sed posuere ligula et felis convallis tempor. Aliquam nec mollis velit. Donec varius sit amet erat at imperdiet. Nulla ipsum justo, tempor non sollicitudin gravida, dignissim vel orci. In hac habitasse platea dictumst. Cras cursus tellus id arcu aliquet accumsan. Phasellus ac erat dui.
-
-Duis mollis metus at mi luctus aliquam. Duis varius eget erat ac porttitor. Phasellus lobortis sagittis lacinia. Etiam sagittis eget erat in pulvinar. Phasellus sodales risus nec vulputate accumsan. Cras sit amet pellentesque dui. Praesent consequat felis mi, at vulputate diam convallis a. Donec hendrerit nibh vel justo consequat dictum. In euismod, dui sit amet malesuada suscipit, mauris ex rhoncus eros, sed ornare arcu nunc eu urna. Pellentesque eget erat augue. Integer rutrum mauris sem, nec sodales nulla cursus vel. Vivamus porta, urna vel varius vulputate, nulla arcu malesuada dui, a ultrices magna ante sed nibh.
-
-Morbi ultricies aliquam lorem id bibendum. Donec sit amet nunc vitae massa gravida eleifend hendrerit vel libero. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nulla vestibulum tempus condimentum. Aliquam dolor ipsum, condimentum in sapien et, tempor iaculis nulla. Aenean non pharetra augue. Maecenas mattis dignissim maximus. Fusce elementum tincidunt massa sit amet lobortis. Phasellus nec pharetra dui, et malesuada ante. Nullam commodo pretium tellus. Praesent sollicitudin, enim eget imperdiet scelerisque, odio felis vulputate dolor, eget auctor neque tellus ac lorem.
-
-In consectetur augue et sapien feugiat varius. Nam tortor mi, consectetur ac felis non, elementum venenatis augue. Suspendisse ut tellus in est sagittis cursus. Quisque faucibus, neque sit amet semper congue, nibh augue finibus odio, vitae interdum dolor arcu eget arcu. Curabitur dictum risus massa, non tincidunt urna molestie non. Maecenas eu quam purus. Donec vulputate, dui eu accumsan blandit, mauris tortor tristique mi, sed blandit leo quam id quam. Ut venenatis sagittis malesuada. Integer non auctor orci. Duis consectetur massa felis. Fusce euismod est sit amet bibendum finibus. Vestibulum dolor ex, tempor at elit in, iaculis cursus dui. Nunc sed neque ac risus rutrum tempus sit amet at ante. In hac habitasse platea dictumst.
-
-Donec rutrum, velit nec viverra tincidunt, est velit viverra neque, quis auctor leo ex at lectus. Morbi eget purus nisi. Aliquam lacus dui, interdum vitae elit at, venenatis dignissim est. Duis ac mollis lorem. Vivamus a vestibulum quam. Maecenas non metus dolor. Praesent tortor nunc, tristique at nisl molestie, vulputate eleifend diam. Integer ultrices lacus odio, vel imperdiet enim accumsan id. Sed ligula tortor, interdum eu velit eget, pharetra pulvinar magna. Sed non lacus in eros tincidunt sagittis ac vel justo. Donec vitae leo sagittis, accumsan ante sit amet, accumsan odio. Ut volutpat ultricies tortor. Vestibulum tempus purus et est tristique sagittis quis vitae turpis.
-
-Nam iaculis neque lacus, eget euismod turpis blandit eget. In hac habitasse platea dictumst. Phasellus justo neque, scelerisque sit amet risus ut, pretium commodo nisl. Phasellus auctor sapien sed ex bibendum fermentum. Proin maximus odio a ante ornare, a feugiat lorem egestas. Etiam efficitur tortor a ante tincidunt interdum. Nullam non est ac massa congue efficitur sit amet nec eros. Nullam at ipsum vel mauris tincidunt efficitur. Duis pulvinar nisl elit, id auctor risus laoreet ac. Sed nunc mauris, tristique id leo ut, condimentum congue nunc. Sed ultricies, mauris et convallis faucibus, justo ex faucibus est, at lobortis purus justo non arcu. Integer vel facilisis elit, dapibus imperdiet mauris.
-
-Pellentesque non mattis turpis, eget bibendum velit. Fusce sollicitudin ante ac tincidunt rhoncus. Praesent porta scelerisque consequat. Donec eleifend faucibus sollicitudin. Quisque vitae purus eget tortor tempor ultrices. Maecenas mauris diam, semper vitae est non, imperdiet tempor magna. Duis elit lacus, auctor vestibulum enim eget, rhoncus porttitor tortor.
-
-Donec non rhoncus nibh. Cras dapibus justo vitae nunc accumsan, id congue erat egestas. Aenean at ante ante. Duis eleifend imperdiet dREADALL
diff --git a/runsc/test/image/mysql.sql b/runsc/test/image/mysql.sql
deleted file mode 100644
index 51554b98d..000000000
--- a/runsc/test/image/mysql.sql
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright 2018 The gVisor Authors.
-#
-# 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.
-
-SHOW databases;
-USE mysql;
-
-CREATE TABLE foo (id int);
-INSERT INTO foo VALUES(1);
-SELECT * FROM foo;
-DROP TABLE foo;
-
-shutdown;
diff --git a/runsc/test/image/ruby.rb b/runsc/test/image/ruby.rb
deleted file mode 100644
index aced49c6d..000000000
--- a/runsc/test/image/ruby.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright 2018 The gVisor Authors.
-#
-# 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.
-
-require 'sinatra'
-
-set :bind, "0.0.0.0"
-set :port, 8080
-
-get '/' do
- 'Hello World'
-end
-
diff --git a/runsc/test/image/ruby.sh b/runsc/test/image/ruby.sh
deleted file mode 100644
index ebe8d5b0e..000000000
--- a/runsc/test/image/ruby.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-
-# Copyright 2018 The gVisor Authors.
-#
-# 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.
-
-set -e
-
-gem install sinatra
-ruby /src/ruby.rb
diff --git a/runsc/test/install.sh b/runsc/test/install.sh
deleted file mode 100755
index 8f05dea20..000000000
--- a/runsc/test/install.sh
+++ /dev/null
@@ -1,93 +0,0 @@
-#!/bin/bash
-
-# Copyright 2018 The gVisor Authors.
-#
-# 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.
-
-# Fail on any error
-set -e
-
-# Defaults
-declare runtime=runsc-test
-declare uninstall=0
-
-function findExe() {
- local exe=${1}
-
- local path=$(find bazel-bin/runsc -type f -executable -name "${exe}" | head -n1)
- if [[ "${path}" == "" ]]; then
- echo "Location of ${exe} not found in bazel-bin" >&2
- exit 1
- fi
- echo "${path}"
-}
-
-while [[ $# -gt 0 ]]; do
- case "$1" in
- --runtime)
- shift
- [ "$#" -le 0 ] && echo "No runtime provided" && exit 1
- runtime=$1
- ;;
- -u)
- uninstall=1
- ;;
- *)
- echo "Unknown option: ${1}"
- echo ""
- echo "Usage: ${0} [--runtime <name>] [-u]"
- echo " --runtime sets the runtime name, default: runsc-test"
- echo " -u uninstall the runtime"
- exit 1
- esac
- shift
-done
-
-# Find location of executables.
-declare -r dockercfg=$(findExe dockercfg)
-[[ "${dockercfg}" == "" ]] && exit 1
-
-declare runsc=$(findExe runsc)
-[[ "${runsc}" == "" ]] && exit 1
-
-if [[ ${uninstall} == 0 ]]; then
- rm -rf /tmp/${runtime}
- mkdir -p /tmp/${runtime}
- cp "${runsc}" /tmp/${runtime}/runsc
- runsc=/tmp/${runtime}/runsc
-
- # Make tmp dir and runsc binary readable and executable to all users, since it
- # will run in an empty user namespace.
- chmod a+rx "${runsc}" $(dirname "${runsc}")
-
- # Make log dir executable and writable to all users for the same reason.
- declare logdir=/tmp/"${runtime?}/logs"
- mkdir -p "${logdir}"
- sudo -n chmod a+wx "${logdir}"
-
- declare -r args="--debug-log '${logdir}/' --debug --strace --log-packets"
- # experimental is needed to checkpoint/restore.
- sudo -n "${dockercfg}" --experimental=true runtime-add "${runtime}" "${runsc}" ${args}
- sudo -n "${dockercfg}" runtime-add "${runtime}"-kvm "${runsc}" --platform=kvm ${args}
- sudo -n "${dockercfg}" runtime-add "${runtime}"-hostnet "${runsc}" --network=host ${args}
- sudo -n "${dockercfg}" runtime-add "${runtime}"-overlay "${runsc}" --overlay ${args}
-
-else
- sudo -n "${dockercfg}" runtime-rm "${runtime}"
- sudo -n "${dockercfg}" runtime-rm "${runtime}"-kvm
- sudo -n "${dockercfg}" runtime-rm "${runtime}"-hostnet
- sudo -n "${dockercfg}" runtime-rm "${runtime}"-overlay
-fi
-
-echo "Restarting docker service..."
-sudo -n /etc/init.d/docker restart
diff --git a/runsc/test/integration/BUILD b/runsc/test/integration/BUILD
deleted file mode 100644
index 12065617c..000000000
--- a/runsc/test/integration/BUILD
+++ /dev/null
@@ -1,30 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-load("//runsc/test:build_defs.bzl", "runtime_test")
-
-package(licenses = ["notice"])
-
-runtime_test(
- name = "integration_test",
- size = "large",
- srcs = [
- "exec_test.go",
- "integration_test.go",
- "regression_test.go",
- ],
- embed = [":integration"],
- tags = [
- # Requires docker and runsc to be configured before the test runs.
- "manual",
- "local",
- ],
- deps = [
- "//pkg/abi/linux",
- "//runsc/test/testutil",
- ],
-)
-
-go_library(
- name = "integration",
- srcs = ["integration.go"],
- importpath = "gvisor.dev/gvisor/runsc/test/integration",
-)
diff --git a/runsc/test/integration/exec_test.go b/runsc/test/integration/exec_test.go
deleted file mode 100644
index 993136f96..000000000
--- a/runsc/test/integration/exec_test.go
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// 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 image provides end-to-end integration tests for runsc. These tests require
-// docker and runsc to be installed on the machine. To set it up, run:
-//
-// ./runsc/test/install.sh [--runtime <name>]
-//
-// The tests expect the runtime name to be provided in the RUNSC_RUNTIME
-// environment variable (default: runsc-test).
-//
-// Each test calls docker commands to start up a container, and tests that it is
-// behaving properly, with various runsc commands. The container is killed and deleted
-// at the end.
-
-package integration
-
-import (
- "fmt"
- "strconv"
- "strings"
- "syscall"
- "testing"
- "time"
-
- "gvisor.dev/gvisor/pkg/abi/linux"
- "gvisor.dev/gvisor/runsc/test/testutil"
-)
-
-func TestExecCapabilities(t *testing.T) {
- if err := testutil.Pull("alpine"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := testutil.MakeDocker("exec-test")
-
- // Start the container.
- if err := d.Run("alpine", "sh", "-c", "cat /proc/self/status; sleep 100"); err != nil {
- t.Fatalf("docker run failed: %v", err)
- }
- defer d.CleanUp()
-
- matches, err := d.WaitForOutputSubmatch("CapEff:\t([0-9a-f]+)\n", 5*time.Second)
- if err != nil {
- t.Fatalf("WaitForOutputSubmatch() timeout: %v", err)
- }
- if len(matches) != 2 {
- t.Fatalf("There should be a match for the whole line and the capability bitmask")
- }
- capString := matches[1]
- t.Log("Root capabilities:", capString)
-
- // CAP_NET_RAW was in the capability set for the container, but was
- // removed. However, `exec` does not remove it. Verify that it's not
- // set in the container, then re-add it for comparison.
- caps, err := strconv.ParseUint(capString, 16, 64)
- if err != nil {
- t.Fatalf("failed to convert capabilities %q: %v", capString, err)
- }
- if caps&(1<<uint64(linux.CAP_NET_RAW)) != 0 {
- t.Fatalf("CAP_NET_RAW should be filtered, but is set in the container: %x", caps)
- }
- caps |= 1 << uint64(linux.CAP_NET_RAW)
- want := fmt.Sprintf("CapEff:\t%016x\n", caps)
-
- // Now check that exec'd process capabilities match the root.
- got, err := d.Exec("grep", "CapEff:", "/proc/self/status")
- if err != nil {
- t.Fatalf("docker exec failed: %v", err)
- }
- if got != want {
- t.Errorf("wrong capabilities, got: %q, want: %q", got, want)
- }
-}
-
-func TestExecJobControl(t *testing.T) {
- if err := testutil.Pull("alpine"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := testutil.MakeDocker("exec-job-control-test")
-
- // Start the container.
- if err := d.Run("alpine", "sleep", "1000"); err != nil {
- t.Fatalf("docker run failed: %v", err)
- }
- defer d.CleanUp()
-
- // Exec 'sh' with an attached pty.
- cmd, ptmx, err := d.ExecWithTerminal("sh")
- if err != nil {
- t.Fatalf("docker exec failed: %v", err)
- }
- defer ptmx.Close()
-
- // Call "sleep 100 | cat" in the shell. We pipe to cat so that there
- // will be two processes in the foreground process group.
- if _, err := ptmx.Write([]byte("sleep 100 | cat\n")); err != nil {
- t.Fatalf("error writing to pty: %v", err)
- }
-
- // Give shell a few seconds to start executing the sleep.
- time.Sleep(2 * time.Second)
-
- // Send a ^C to the pty, which should kill sleep and cat, but not the
- // shell. \x03 is ASCII "end of text", which is the same as ^C.
- if _, err := ptmx.Write([]byte{'\x03'}); err != nil {
- t.Fatalf("error writing to pty: %v", err)
- }
-
- // The shell should still be alive at this point. Sleep should have
- // exited with code 2+128=130. We'll exit with 10 plus that number, so
- // that we can be sure that the shell did not get signalled.
- if _, err := ptmx.Write([]byte("exit $(expr $? + 10)\n")); err != nil {
- t.Fatalf("error writing to pty: %v", err)
- }
-
- // Exec process should exit with code 10+130=140.
- ps, err := cmd.Process.Wait()
- if err != nil {
- t.Fatalf("error waiting for exec process: %v", err)
- }
- ws := ps.Sys().(syscall.WaitStatus)
- if !ws.Exited() {
- t.Errorf("ws.Exited got false, want true")
- }
- if got, want := ws.ExitStatus(), 140; got != want {
- t.Errorf("ws.ExitedStatus got %d, want %d", got, want)
- }
-}
-
-// Test that failure to exec returns proper error message.
-func TestExecError(t *testing.T) {
- if err := testutil.Pull("alpine"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := testutil.MakeDocker("exec-error-test")
-
- // Start the container.
- if err := d.Run("alpine", "sleep", "1000"); err != nil {
- t.Fatalf("docker run failed: %v", err)
- }
- defer d.CleanUp()
-
- _, err := d.Exec("no_can_find")
- if err == nil {
- t.Fatalf("docker exec didn't fail")
- }
- if want := `error finding executable "no_can_find" in PATH`; !strings.Contains(err.Error(), want) {
- t.Fatalf("docker exec wrong error, got: %s, want: .*%s.*", err.Error(), want)
- }
-}
diff --git a/runsc/test/integration/integration.go b/runsc/test/integration/integration.go
deleted file mode 100644
index 4cd5f6c24..000000000
--- a/runsc/test/integration/integration.go
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// 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 integration is empty. See integration_test.go for description.
-package integration
diff --git a/runsc/test/integration/integration_test.go b/runsc/test/integration/integration_test.go
deleted file mode 100644
index 7cef4b9dd..000000000
--- a/runsc/test/integration/integration_test.go
+++ /dev/null
@@ -1,344 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// 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 integration provides end-to-end integration tests for runsc.
-//
-// Each test calls docker commands to start up a container, and tests that it is
-// behaving properly, with various runsc commands. The container is killed and
-// deleted at the end.
-//
-// Setup instruction in runsc/test/README.md.
-package integration
-
-import (
- "fmt"
- "net"
- "net/http"
- "os"
- "strconv"
- "strings"
- "syscall"
- "testing"
- "time"
-
- "gvisor.dev/gvisor/runsc/test/testutil"
-)
-
-// httpRequestSucceeds sends a request to a given url and checks that the status is OK.
-func httpRequestSucceeds(client http.Client, server string, port int) error {
- url := fmt.Sprintf("http://%s:%d", server, port)
- // Ensure that content is being served.
- resp, err := client.Get(url)
- if err != nil {
- return fmt.Errorf("error reaching http server: %v", err)
- }
- if want := http.StatusOK; resp.StatusCode != want {
- return fmt.Errorf("wrong response code, got: %d, want: %d", resp.StatusCode, want)
- }
- return nil
-}
-
-// TestLifeCycle tests a basic Create/Start/Stop docker container life cycle.
-func TestLifeCycle(t *testing.T) {
- if err := testutil.Pull("nginx"); err != nil {
- t.Fatal("docker pull failed:", err)
- }
- d := testutil.MakeDocker("lifecycle-test")
- if err := d.Create("-p", "80", "nginx"); err != nil {
- t.Fatal("docker create failed:", err)
- }
- if err := d.Start(); err != nil {
- d.CleanUp()
- t.Fatal("docker start failed:", err)
- }
-
- // Test that container is working
- port, err := d.FindPort(80)
- if err != nil {
- t.Fatal("docker.FindPort(80) failed: ", err)
- }
- if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
- t.Fatal("WaitForHTTP() timeout:", err)
- }
- client := http.Client{Timeout: time.Duration(2 * time.Second)}
- if err := httpRequestSucceeds(client, "localhost", port); err != nil {
- t.Error("http request failed:", err)
- }
-
- if err := d.Stop(); err != nil {
- d.CleanUp()
- t.Fatal("docker stop failed:", err)
- }
- if err := d.Remove(); err != nil {
- t.Fatal("docker rm failed:", err)
- }
-}
-
-func TestPauseResume(t *testing.T) {
- const img = "gcr.io/gvisor-presubmit/python-hello"
- if !testutil.IsPauseResumeSupported() {
- t.Log("Pause/resume is not supported, skipping test.")
- return
- }
-
- if err := testutil.Pull(img); err != nil {
- t.Fatal("docker pull failed:", err)
- }
- d := testutil.MakeDocker("pause-resume-test")
- if err := d.Run("-p", "8080", img); err != nil {
- t.Fatalf("docker run failed: %v", err)
- }
- defer d.CleanUp()
-
- // Find where port 8080 is mapped to.
- port, err := d.FindPort(8080)
- if err != nil {
- t.Fatal("docker.FindPort(8080) failed:", err)
- }
-
- // Wait until it's up and running.
- if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
- t.Fatal("WaitForHTTP() timeout:", err)
- }
-
- // Check that container is working.
- client := http.Client{Timeout: time.Duration(2 * time.Second)}
- if err := httpRequestSucceeds(client, "localhost", port); err != nil {
- t.Error("http request failed:", err)
- }
-
- if err := d.Pause(); err != nil {
- t.Fatal("docker pause failed:", err)
- }
-
- // Check if container is paused.
- switch _, err := client.Get(fmt.Sprintf("http://localhost:%d", port)); v := err.(type) {
- case nil:
- t.Errorf("http req expected to fail but it succeeded")
- case net.Error:
- if !v.Timeout() {
- t.Errorf("http req got error %v, wanted timeout", v)
- }
- default:
- t.Errorf("http req got unexpected error %v", v)
- }
-
- if err := d.Unpause(); err != nil {
- t.Fatal("docker unpause failed:", err)
- }
-
- // Wait until it's up and running.
- if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
- t.Fatal("WaitForHTTP() timeout:", err)
- }
-
- // Check if container is working again.
- if err := httpRequestSucceeds(client, "localhost", port); err != nil {
- t.Error("http request failed:", err)
- }
-}
-
-func TestCheckpointRestore(t *testing.T) {
- const img = "gcr.io/gvisor-presubmit/python-hello"
- if !testutil.IsPauseResumeSupported() {
- t.Log("Pause/resume is not supported, skipping test.")
- return
- }
- if err := testutil.Pull(img); err != nil {
- t.Fatal("docker pull failed:", err)
- }
- d := testutil.MakeDocker("save-restore-test")
- if err := d.Run("-p", "8080", img); err != nil {
- t.Fatalf("docker run failed: %v", err)
- }
- defer d.CleanUp()
-
- if err := d.Checkpoint("test"); err != nil {
- t.Fatal("docker checkpoint failed:", err)
- }
-
- if _, err := d.Wait(30 * time.Second); err != nil {
- t.Fatal(err)
- }
-
- if err := d.Restore("test"); err != nil {
- t.Fatal("docker restore failed:", err)
- }
-
- // Find where port 8080 is mapped to.
- port, err := d.FindPort(8080)
- if err != nil {
- t.Fatal("docker.FindPort(8080) failed:", err)
- }
-
- // Wait until it's up and running.
- if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
- t.Fatal("WaitForHTTP() timeout:", err)
- }
-
- // Check if container is working again.
- client := http.Client{Timeout: time.Duration(2 * time.Second)}
- if err := httpRequestSucceeds(client, "localhost", port); err != nil {
- t.Error("http request failed:", err)
- }
-}
-
-// Create client and server that talk to each other using the local IP.
-func TestConnectToSelf(t *testing.T) {
- d := testutil.MakeDocker("connect-to-self-test")
-
- // Creates server that replies "server" and exists. Sleeps at the end because
- // 'docker exec' gets killed if the init process exists before it can finish.
- if err := d.Run("ubuntu:trusty", "/bin/sh", "-c", "echo server | nc -l -p 8080 && sleep 1"); err != nil {
- t.Fatal("docker run failed:", err)
- }
- defer d.CleanUp()
-
- // Finds IP address for host.
- ip, err := d.Exec("/bin/sh", "-c", "cat /etc/hosts | grep ${HOSTNAME} | awk '{print $1}'")
- if err != nil {
- t.Fatal("docker exec failed:", err)
- }
- ip = strings.TrimRight(ip, "\n")
-
- // Runs client that sends "client" to the server and exits.
- reply, err := d.Exec("/bin/sh", "-c", fmt.Sprintf("echo client | nc %s 8080", ip))
- if err != nil {
- t.Fatal("docker exec failed:", err)
- }
-
- // Ensure both client and server got the message from each other.
- if want := "server\n"; reply != want {
- t.Errorf("Error on server, want: %q, got: %q", want, reply)
- }
- if _, err := d.WaitForOutput("^client\n$", 1*time.Second); err != nil {
- t.Fatal("docker.WaitForOutput(client) timeout:", err)
- }
-}
-
-func TestMemLimit(t *testing.T) {
- if err := testutil.Pull("alpine"); err != nil {
- t.Fatal("docker pull failed:", err)
- }
- d := testutil.MakeDocker("cgroup-test")
- cmd := "cat /proc/meminfo | grep MemTotal: | awk '{print $2}'"
- out, err := d.RunFg("--memory=500MB", "alpine", "sh", "-c", cmd)
- if err != nil {
- t.Fatal("docker run failed:", err)
- }
- defer d.CleanUp()
-
- // Remove warning message that swap isn't present.
- if strings.HasPrefix(out, "WARNING") {
- lines := strings.Split(out, "\n")
- if len(lines) != 3 {
- t.Fatalf("invalid output: %s", out)
- }
- out = lines[1]
- }
-
- got, err := strconv.ParseUint(strings.TrimSpace(out), 10, 64)
- if err != nil {
- t.Fatalf("failed to parse %q: %v", out, err)
- }
- if want := uint64(500 * 1024); got != want {
- t.Errorf("MemTotal got: %d, want: %d", got, want)
- }
-}
-
-func TestNumCPU(t *testing.T) {
- if err := testutil.Pull("alpine"); err != nil {
- t.Fatal("docker pull failed:", err)
- }
- d := testutil.MakeDocker("cgroup-test")
- cmd := "cat /proc/cpuinfo | grep 'processor.*:' | wc -l"
- out, err := d.RunFg("--cpuset-cpus=0", "alpine", "sh", "-c", cmd)
- if err != nil {
- t.Fatal("docker run failed:", err)
- }
- defer d.CleanUp()
-
- got, err := strconv.Atoi(strings.TrimSpace(out))
- if err != nil {
- t.Fatalf("failed to parse %q: %v", out, err)
- }
- if want := 1; got != want {
- t.Errorf("MemTotal got: %d, want: %d", got, want)
- }
-}
-
-// TestJobControl tests that job control characters are handled properly.
-func TestJobControl(t *testing.T) {
- if err := testutil.Pull("alpine"); err != nil {
- t.Fatalf("docker pull failed: %v", err)
- }
- d := testutil.MakeDocker("job-control-test")
-
- // Start the container with an attached PTY.
- _, ptmx, err := d.RunWithPty("alpine", "sh")
- if err != nil {
- t.Fatalf("docker run failed: %v", err)
- }
- defer ptmx.Close()
- defer d.CleanUp()
-
- // Call "sleep 100" in the shell.
- if _, err := ptmx.Write([]byte("sleep 100\n")); err != nil {
- t.Fatalf("error writing to pty: %v", err)
- }
-
- // Give shell a few seconds to start executing the sleep.
- time.Sleep(2 * time.Second)
-
- // Send a ^C to the pty, which should kill sleep, but not the shell.
- // \x03 is ASCII "end of text", which is the same as ^C.
- if _, err := ptmx.Write([]byte{'\x03'}); err != nil {
- t.Fatalf("error writing to pty: %v", err)
- }
-
- // The shell should still be alive at this point. Sleep should have
- // exited with code 2+128=130. We'll exit with 10 plus that number, so
- // that we can be sure that the shell did not get signalled.
- if _, err := ptmx.Write([]byte("exit $(expr $? + 10)\n")); err != nil {
- t.Fatalf("error writing to pty: %v", err)
- }
-
- // Wait for the container to exit.
- got, err := d.Wait(5 * time.Second)
- if err != nil {
- t.Fatalf("error getting exit code: %v", err)
- }
- // Container should exit with code 10+130=140.
- if want := syscall.WaitStatus(140); got != want {
- t.Errorf("container exited with code %d want %d", got, want)
- }
-}
-
-// TestTmpFile checks that files inside '/tmp' are not overridden. In addition,
-// it checks that working dir is created if it doesn't exit.
-func TestTmpFile(t *testing.T) {
- if err := testutil.Pull("alpine"); err != nil {
- t.Fatal("docker pull failed:", err)
- }
- d := testutil.MakeDocker("tmp-file-test")
- if err := d.Run("-w=/tmp/foo/bar", "--read-only", "alpine", "touch", "/tmp/foo/bar/file"); err != nil {
- t.Fatal("docker run failed:", err)
- }
- defer d.CleanUp()
-}
-
-func TestMain(m *testing.M) {
- testutil.EnsureSupportedDockerVersion()
- os.Exit(m.Run())
-}
diff --git a/runsc/test/integration/regression_test.go b/runsc/test/integration/regression_test.go
deleted file mode 100644
index fb68dda99..000000000
--- a/runsc/test/integration/regression_test.go
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2019 The gVisor Authors.
-//
-// 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 integration
-
-import (
- "strings"
- "testing"
-
- "gvisor.dev/gvisor/runsc/test/testutil"
-)
-
-// Test that UDS can be created using overlay when parent directory is in lower
-// layer only (b/134090485).
-//
-// Prerequisite: the directory where the socket file is created must not have
-// been open for write before bind(2) is called.
-func TestBindOverlay(t *testing.T) {
- if err := testutil.Pull("ubuntu:trusty"); err != nil {
- t.Fatal("docker pull failed:", err)
- }
- d := testutil.MakeDocker("bind-overlay-test")
-
- cmd := "nc -l -U /var/run/sock & p=$! && sleep 1 && echo foobar-asdf | nc -U /var/run/sock && wait $p"
- got, err := d.RunFg("ubuntu:trusty", "bash", "-c", cmd)
- if err != nil {
- t.Fatal("docker run failed:", err)
- }
-
- if want := "foobar-asdf"; !strings.Contains(got, want) {
- t.Fatalf("docker run output is missing %q: %s", want, got)
- }
- defer d.CleanUp()
-}
diff --git a/runsc/test/root/BUILD b/runsc/test/root/BUILD
deleted file mode 100644
index 500ef7b8e..000000000
--- a/runsc/test/root/BUILD
+++ /dev/null
@@ -1,33 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "root",
- srcs = ["root.go"],
- importpath = "gvisor.dev/gvisor/runsc/test/root",
-)
-
-go_test(
- name = "root_test",
- size = "small",
- srcs = [
- "cgroup_test.go",
- "chroot_test.go",
- "crictl_test.go",
- ],
- embed = [":root"],
- tags = [
- # Requires docker and runsc to be configured before the test runs.
- # Also test only runs as root.
- "manual",
- "local",
- ],
- deps = [
- "//runsc/cgroup",
- "//runsc/specutils",
- "//runsc/test/root/testdata",
- "//runsc/test/testutil",
- "@com_github_syndtr_gocapability//capability:go_default_library",
- ],
-)
diff --git a/runsc/test/root/cgroup_test.go b/runsc/test/root/cgroup_test.go
deleted file mode 100644
index 5392dc6e0..000000000
--- a/runsc/test/root/cgroup_test.go
+++ /dev/null
@@ -1,237 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// 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 root
-
-import (
- "bufio"
- "fmt"
- "io/ioutil"
- "os"
- "os/exec"
- "path/filepath"
- "strconv"
- "strings"
- "testing"
-
- "gvisor.dev/gvisor/runsc/cgroup"
- "gvisor.dev/gvisor/runsc/test/testutil"
-)
-
-func verifyPid(pid int, path string) error {
- f, err := os.Open(path)
- if err != nil {
- return err
- }
- defer f.Close()
-
- var gots []int
- scanner := bufio.NewScanner(f)
- for scanner.Scan() {
- got, err := strconv.Atoi(scanner.Text())
- if err != nil {
- return err
- }
- if got == pid {
- return nil
- }
- gots = append(gots, got)
- }
- if scanner.Err() != nil {
- return scanner.Err()
- }
- return fmt.Errorf("got: %s, want: %d", gots, pid)
-}
-
-// TestCgroup sets cgroup options and checks that cgroup was properly configured.
-func TestCgroup(t *testing.T) {
- if err := testutil.Pull("alpine"); err != nil {
- t.Fatal("docker pull failed:", err)
- }
- d := testutil.MakeDocker("cgroup-test")
-
- attrs := []struct {
- arg string
- ctrl string
- file string
- want string
- skipIfNotFound bool
- }{
- {
- arg: "--cpu-shares=1000",
- ctrl: "cpu",
- file: "cpu.shares",
- want: "1000",
- },
- {
- arg: "--cpu-period=2000",
- ctrl: "cpu",
- file: "cpu.cfs_period_us",
- want: "2000",
- },
- {
- arg: "--cpu-quota=3000",
- ctrl: "cpu",
- file: "cpu.cfs_quota_us",
- want: "3000",
- },
- {
- arg: "--cpuset-cpus=0",
- ctrl: "cpuset",
- file: "cpuset.cpus",
- want: "0",
- },
- {
- arg: "--cpuset-mems=0",
- ctrl: "cpuset",
- file: "cpuset.mems",
- want: "0",
- },
- {
- arg: "--kernel-memory=100MB",
- ctrl: "memory",
- file: "memory.kmem.limit_in_bytes",
- want: "104857600",
- },
- {
- arg: "--memory=1GB",
- ctrl: "memory",
- file: "memory.limit_in_bytes",
- want: "1073741824",
- },
- {
- arg: "--memory-reservation=500MB",
- ctrl: "memory",
- file: "memory.soft_limit_in_bytes",
- want: "524288000",
- },
- {
- arg: "--memory-swap=2GB",
- ctrl: "memory",
- file: "memory.memsw.limit_in_bytes",
- want: "2147483648",
- skipIfNotFound: true, // swap may be disabled on the machine.
- },
- {
- arg: "--memory-swappiness=5",
- ctrl: "memory",
- file: "memory.swappiness",
- want: "5",
- },
- {
- arg: "--blkio-weight=750",
- ctrl: "blkio",
- file: "blkio.weight",
- want: "750",
- },
- }
-
- args := make([]string, 0, len(attrs))
- for _, attr := range attrs {
- args = append(args, attr.arg)
- }
-
- args = append(args, "alpine", "sleep", "10000")
- if err := d.Run(args...); err != nil {
- t.Fatal("docker create failed:", err)
- }
- defer d.CleanUp()
-
- gid, err := d.ID()
- if err != nil {
- t.Fatalf("Docker.ID() failed: %v", err)
- }
- t.Logf("cgroup ID: %s", gid)
-
- // Check list of attributes defined above.
- for _, attr := range attrs {
- path := filepath.Join("/sys/fs/cgroup", attr.ctrl, "docker", gid, attr.file)
- out, err := ioutil.ReadFile(path)
- if err != nil {
- if os.IsNotExist(err) && attr.skipIfNotFound {
- t.Logf("skipped %s/%s", attr.ctrl, attr.file)
- continue
- }
- t.Fatalf("failed to read %q: %v", path, err)
- }
- if got := strings.TrimSpace(string(out)); got != attr.want {
- t.Errorf("arg: %q, cgroup attribute %s/%s, got: %q, want: %q", attr.arg, attr.ctrl, attr.file, got, attr.want)
- }
- }
-
- // Check that sandbox is inside cgroup.
- controllers := []string{
- "blkio",
- "cpu",
- "cpuset",
- "memory",
- "net_cls",
- "net_prio",
- "devices",
- "freezer",
- "perf_event",
- "pids",
- "systemd",
- }
- pid, err := d.SandboxPid()
- if err != nil {
- t.Fatalf("SandboxPid: %v", err)
- }
- for _, ctrl := range controllers {
- path := filepath.Join("/sys/fs/cgroup", ctrl, "docker", gid, "cgroup.procs")
- if err := verifyPid(pid, path); err != nil {
- t.Errorf("cgroup control %q processes: %v", ctrl, err)
- }
- }
-}
-
-func TestCgroupParent(t *testing.T) {
- if err := testutil.Pull("alpine"); err != nil {
- t.Fatal("docker pull failed:", err)
- }
- d := testutil.MakeDocker("cgroup-test")
-
- parent := testutil.RandomName("runsc")
- if err := d.Run("--cgroup-parent", parent, "alpine", "sleep", "10000"); err != nil {
- t.Fatal("docker create failed:", err)
- }
- defer d.CleanUp()
- gid, err := d.ID()
- if err != nil {
- t.Fatalf("Docker.ID() failed: %v", err)
- }
- t.Logf("cgroup ID: %s", gid)
-
- // Check that sandbox is inside cgroup.
- pid, err := d.SandboxPid()
- if err != nil {
- t.Fatalf("SandboxPid: %v", err)
- }
-
- // Finds cgroup for the sandbox's parent process to check that cgroup is
- // created in the right location relative to the parent.
- cmd := fmt.Sprintf("grep PPid: /proc/%d/status | sed 's/PPid:\\s//'", pid)
- ppid, err := exec.Command("bash", "-c", cmd).CombinedOutput()
- if err != nil {
- t.Fatalf("Executing %q: %v", cmd, err)
- }
- cgroups, err := cgroup.LoadPaths(strings.TrimSpace(string(ppid)))
- if err != nil {
- t.Fatalf("cgroup.LoadPath(%s): %v", ppid, err)
- }
- path := filepath.Join("/sys/fs/cgroup/memory", cgroups["memory"], parent, gid, "cgroup.procs")
- if err := verifyPid(pid, path); err != nil {
- t.Errorf("cgroup control %q processes: %v", "memory", err)
- }
-}
diff --git a/runsc/test/root/chroot_test.go b/runsc/test/root/chroot_test.go
deleted file mode 100644
index d0f236580..000000000
--- a/runsc/test/root/chroot_test.go
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// 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 root is used for tests that requires sysadmin privileges run. First,
-// follow the setup instruction in runsc/test/README.md. To run these tests:
-//
-// bazel build //runsc/test/root:root_test
-// root_test=$(find -L ./bazel-bin/ -executable -type f -name root_test | grep __main__)
-// sudo RUNSC_RUNTIME=runsc-test ${root_test}
-package root
-
-import (
- "fmt"
- "io/ioutil"
- "os"
- "os/exec"
- "path/filepath"
- "strconv"
- "strings"
- "testing"
-
- "github.com/syndtr/gocapability/capability"
- "gvisor.dev/gvisor/runsc/specutils"
- "gvisor.dev/gvisor/runsc/test/testutil"
-)
-
-// TestChroot verifies that the sandbox is chroot'd and that mounts are cleaned
-// up after the sandbox is destroyed.
-func TestChroot(t *testing.T) {
- d := testutil.MakeDocker("chroot-test")
- if err := d.Run("alpine", "sleep", "10000"); err != nil {
- t.Fatalf("docker run failed: %v", err)
- }
- defer d.CleanUp()
-
- pid, err := d.SandboxPid()
- if err != nil {
- t.Fatalf("Docker.SandboxPid(): %v", err)
- }
-
- // Check that sandbox is chroot'ed.
- procRoot := filepath.Join("/proc", strconv.Itoa(pid), "root")
- chroot, err := filepath.EvalSymlinks(procRoot)
- if err != nil {
- t.Fatalf("error resolving /proc/<pid>/root symlink: %v", err)
- }
- if chroot != "/" {
- t.Errorf("sandbox is not chroot'd, it should be inside: /, got: %q", chroot)
- }
-
- path, err := filepath.EvalSymlinks(filepath.Join("/proc", strconv.Itoa(pid), "cwd"))
- if err != nil {
- t.Fatalf("error resolving /proc/<pid>/cwd symlink: %v", err)
- }
- if chroot != path {
- t.Errorf("sandbox current dir is wrong, want: %q, got: %q", chroot, path)
- }
-
- fi, err := ioutil.ReadDir(procRoot)
- if err != nil {
- t.Fatalf("error listing %q: %v", chroot, err)
- }
- if want, got := 1, len(fi); want != got {
- t.Fatalf("chroot dir got %d entries, want %d", got, want)
- }
-
- // chroot dir is prepared by runsc and should contains only /proc.
- if fi[0].Name() != "proc" {
- t.Errorf("chroot got children %v, want %v", fi[0].Name(), "proc")
- }
-
- d.CleanUp()
-}
-
-func TestChrootGofer(t *testing.T) {
- d := testutil.MakeDocker("chroot-test")
- if err := d.Run("alpine", "sleep", "10000"); err != nil {
- t.Fatalf("docker run failed: %v", err)
- }
- defer d.CleanUp()
-
- // It's tricky to find gofers. Get sandbox PID first, then find parent. From
- // parent get all immediate children, remove the sandbox, and everything else
- // are gofers.
- sandPID, err := d.SandboxPid()
- if err != nil {
- t.Fatalf("Docker.SandboxPid(): %v", err)
- }
-
- // Find sandbox's parent PID.
- cmd := fmt.Sprintf("grep PPid /proc/%d/status | awk '{print $2}'", sandPID)
- parent, err := exec.Command("sh", "-c", cmd).CombinedOutput()
- if err != nil {
- t.Fatalf("failed to fetch runsc (%d) parent PID: %v, out:\n%s", sandPID, err, string(parent))
- }
- parentPID, err := strconv.Atoi(strings.TrimSpace(string(parent)))
- if err != nil {
- t.Fatalf("failed to parse PPID %q: %v", string(parent), err)
- }
-
- // Get all children from parent.
- childrenOut, err := exec.Command("/usr/bin/pgrep", "-P", strconv.Itoa(parentPID)).CombinedOutput()
- if err != nil {
- t.Fatalf("failed to fetch containerd-shim children: %v", err)
- }
- children := strings.Split(strings.TrimSpace(string(childrenOut)), "\n")
-
- // This where the root directory is mapped on the host and that's where the
- // gofer must have chroot'd to.
- root := "/root"
-
- for _, child := range children {
- childPID, err := strconv.Atoi(child)
- if err != nil {
- t.Fatalf("failed to parse child PID %q: %v", child, err)
- }
- if childPID == sandPID {
- // Skip the sandbox, all other immediate children are gofers.
- continue
- }
-
- // Check that gofer is chroot'ed.
- chroot, err := filepath.EvalSymlinks(filepath.Join("/proc", child, "root"))
- if err != nil {
- t.Fatalf("error resolving /proc/<pid>/root symlink: %v", err)
- }
- if root != chroot {
- t.Errorf("gofer chroot is wrong, want: %q, got: %q", root, chroot)
- }
-
- path, err := filepath.EvalSymlinks(filepath.Join("/proc", child, "cwd"))
- if err != nil {
- t.Fatalf("error resolving /proc/<pid>/cwd symlink: %v", err)
- }
- if root != path {
- t.Errorf("gofer current dir is wrong, want: %q, got: %q", root, path)
- }
- }
-}
-
-func TestMain(m *testing.M) {
- testutil.EnsureSupportedDockerVersion()
-
- if !specutils.HasCapabilities(capability.CAP_SYS_ADMIN, capability.CAP_DAC_OVERRIDE) {
- fmt.Println("Test requires sysadmin privileges to run. Try again with sudo.")
- os.Exit(1)
- }
-
- os.Exit(m.Run())
-}
diff --git a/runsc/test/root/crictl_test.go b/runsc/test/root/crictl_test.go
deleted file mode 100644
index 515ae2df1..000000000
--- a/runsc/test/root/crictl_test.go
+++ /dev/null
@@ -1,242 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// 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 root
-
-import (
- "bytes"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "net/http"
- "os"
- "os/exec"
- "path"
- "path/filepath"
- "strings"
- "testing"
- "time"
-
- "gvisor.dev/gvisor/runsc/specutils"
- "gvisor.dev/gvisor/runsc/test/root/testdata"
- "gvisor.dev/gvisor/runsc/test/testutil"
-)
-
-// Tests for crictl have to be run as root (rather than in a user namespace)
-// because crictl creates named network namespaces in /var/run/netns/.
-
-func TestCrictlSanity(t *testing.T) {
- // Setup containerd and crictl.
- crictl, cleanup, err := setup(t)
- if err != nil {
- t.Fatalf("failed to setup crictl: %v", err)
- }
- defer cleanup()
- podID, contID, err := crictl.StartPodAndContainer("httpd", testdata.Sandbox, testdata.Httpd)
- if err != nil {
- t.Fatal(err)
- }
-
- // Look for the httpd page.
- if err = httpGet(crictl, podID, "index.html"); err != nil {
- t.Fatalf("failed to get page: %v", err)
- }
-
- // Stop everything.
- if err := crictl.StopPodAndContainer(podID, contID); err != nil {
- t.Fatal(err)
- }
-}
-
-func TestMountPaths(t *testing.T) {
- // Setup containerd and crictl.
- crictl, cleanup, err := setup(t)
- if err != nil {
- t.Fatalf("failed to setup crictl: %v", err)
- }
- defer cleanup()
- podID, contID, err := crictl.StartPodAndContainer("httpd", testdata.Sandbox, testdata.HttpdMountPaths)
- if err != nil {
- t.Fatal(err)
- }
-
- // Look for the directory available at /test.
- if err = httpGet(crictl, podID, "test"); err != nil {
- t.Fatalf("failed to get page: %v", err)
- }
-
- // Stop everything.
- if err := crictl.StopPodAndContainer(podID, contID); err != nil {
- t.Fatal(err)
- }
-}
-
-func TestMountOverSymlinks(t *testing.T) {
- // Setup containerd and crictl.
- crictl, cleanup, err := setup(t)
- if err != nil {
- t.Fatalf("failed to setup crictl: %v", err)
- }
- defer cleanup()
- podID, contID, err := crictl.StartPodAndContainer("k8s.gcr.io/busybox", testdata.Sandbox, testdata.MountOverSymlink)
- if err != nil {
- t.Fatal(err)
- }
-
- out, err := crictl.Exec(contID, "readlink", "/etc/resolv.conf")
- if err != nil {
- t.Fatal(err)
- }
- if want := "/tmp/resolv.conf"; !strings.Contains(string(out), want) {
- t.Fatalf("/etc/resolv.conf is not pointing to %q: %q", want, string(out))
- }
-
- etc, err := crictl.Exec(contID, "cat", "/etc/resolv.conf")
- if err != nil {
- t.Fatal(err)
- }
- tmp, err := crictl.Exec(contID, "cat", "/tmp/resolv.conf")
- if err != nil {
- t.Fatal(err)
- }
- if tmp != etc {
- t.Fatalf("file content doesn't match:\n\t/etc/resolv.conf: %s\n\t/tmp/resolv.conf: %s", string(etc), string(tmp))
- }
-
- // Stop everything.
- if err := crictl.StopPodAndContainer(podID, contID); err != nil {
- t.Fatal(err)
- }
-}
-
-// setup sets up before a test. Specifically it:
-// * Creates directories and a socket for containerd to utilize.
-// * Runs containerd and waits for it to reach a "ready" state for testing.
-// * Returns a cleanup function that should be called at the end of the test.
-func setup(t *testing.T) (*testutil.Crictl, func(), error) {
- var cleanups []func()
- cleanupFunc := func() {
- for i := len(cleanups) - 1; i >= 0; i-- {
- cleanups[i]()
- }
- }
- cleanup := specutils.MakeCleanup(cleanupFunc)
- defer cleanup.Clean()
-
- // Create temporary containerd root and state directories, and a socket
- // via which crictl and containerd communicate.
- containerdRoot, err := ioutil.TempDir(testutil.TmpDir(), "containerd-root")
- if err != nil {
- t.Fatalf("failed to create containerd root: %v", err)
- }
- cleanups = append(cleanups, func() { os.RemoveAll(containerdRoot) })
- containerdState, err := ioutil.TempDir(testutil.TmpDir(), "containerd-state")
- if err != nil {
- t.Fatalf("failed to create containerd state: %v", err)
- }
- cleanups = append(cleanups, func() { os.RemoveAll(containerdState) })
- sockAddr := filepath.Join(testutil.TmpDir(), "containerd-test.sock")
-
- // Start containerd.
- config, err := testutil.WriteTmpFile("containerd-config", testdata.ContainerdConfig(getRunsc()))
- if err != nil {
- t.Fatalf("failed to write containerd config")
- }
- cleanups = append(cleanups, func() { os.RemoveAll(config) })
- containerd := exec.Command(getContainerd(),
- "--config", config,
- "--log-level", "debug",
- "--root", containerdRoot,
- "--state", containerdState,
- "--address", sockAddr)
- cleanups = append(cleanups, func() {
- if err := testutil.KillCommand(containerd); err != nil {
- log.Printf("error killing containerd: %v", err)
- }
- })
- containerdStderr, err := containerd.StderrPipe()
- if err != nil {
- t.Fatalf("failed to get containerd stderr: %v", err)
- }
- containerdStdout, err := containerd.StdoutPipe()
- if err != nil {
- t.Fatalf("failed to get containerd stdout: %v", err)
- }
- if err := containerd.Start(); err != nil {
- t.Fatalf("failed running containerd: %v", err)
- }
-
- // Wait for containerd to boot. Then put all containerd output into a
- // buffer to be logged at the end of the test.
- testutil.WaitUntilRead(containerdStderr, "Start streaming server", nil, 10*time.Second)
- stdoutBuf := &bytes.Buffer{}
- stderrBuf := &bytes.Buffer{}
- go func() { io.Copy(stdoutBuf, containerdStdout) }()
- go func() { io.Copy(stderrBuf, containerdStderr) }()
- cleanups = append(cleanups, func() {
- t.Logf("containerd stdout: %s", string(stdoutBuf.Bytes()))
- t.Logf("containerd stderr: %s", string(stderrBuf.Bytes()))
- })
-
- cleanup.Release()
- return testutil.NewCrictl(20*time.Second, sockAddr), cleanupFunc, nil
-}
-
-// httpGet GETs the contents of a file served from a pod on port 80.
-func httpGet(crictl *testutil.Crictl, podID, filePath string) error {
- // Get the IP of the httpd server.
- ip, err := crictl.PodIP(podID)
- if err != nil {
- return fmt.Errorf("failed to get IP from pod %q: %v", podID, err)
- }
-
- // GET the page. We may be waiting for the server to start, so retry
- // with a timeout.
- var resp *http.Response
- cb := func() error {
- r, err := http.Get(fmt.Sprintf("http://%s", path.Join(ip, filePath)))
- resp = r
- return err
- }
- if err := testutil.Poll(cb, 20*time.Second); err != nil {
- return err
- }
- defer resp.Body.Close()
-
- if resp.StatusCode != 200 {
- return fmt.Errorf("bad status returned: %d", resp.StatusCode)
- }
- return nil
-}
-
-func getContainerd() string {
- // Bazel doesn't pass PATH through, assume the location of containerd
- // unless specified by environment variable.
- c := os.Getenv("CONTAINERD_PATH")
- if c == "" {
- return "/usr/local/bin/containerd"
- }
- return c
-}
-
-func getRunsc() string {
- // Bazel doesn't pass PATH through, assume the location of runsc unless
- // specified by environment variable.
- c := os.Getenv("RUNSC_EXEC")
- if c == "" {
- return "/tmp/runsc-test/runsc"
- }
- return c
-}
diff --git a/runsc/test/root/root.go b/runsc/test/root/root.go
deleted file mode 100644
index 349c752cc..000000000
--- a/runsc/test/root/root.go
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// 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 root is empty. See chroot_test.go for description.
-package root
diff --git a/runsc/test/root/testdata/BUILD b/runsc/test/root/testdata/BUILD
deleted file mode 100644
index 80dc5f214..000000000
--- a/runsc/test/root/testdata/BUILD
+++ /dev/null
@@ -1,18 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "testdata",
- srcs = [
- "busybox.go",
- "containerd_config.go",
- "httpd.go",
- "httpd_mount_paths.go",
- "sandbox.go",
- ],
- importpath = "gvisor.dev/gvisor/runsc/test/root/testdata",
- visibility = [
- "//visibility:public",
- ],
-)
diff --git a/runsc/test/root/testdata/busybox.go b/runsc/test/root/testdata/busybox.go
deleted file mode 100644
index e4dbd2843..000000000
--- a/runsc/test/root/testdata/busybox.go
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// 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 testdata
-
-// MountOverSymlink is a JSON config for a container that /etc/resolv.conf is a
-// symlink to /tmp/resolv.conf.
-var MountOverSymlink = `
-{
- "metadata": {
- "name": "busybox"
- },
- "image": {
- "image": "k8s.gcr.io/busybox"
- },
- "command": [
- "sleep",
- "1000"
- ]
-}
-`
diff --git a/runsc/test/root/testdata/containerd_config.go b/runsc/test/root/testdata/containerd_config.go
deleted file mode 100644
index e12f1ec88..000000000
--- a/runsc/test/root/testdata/containerd_config.go
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// 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 testdata contains data required for root tests.
-package testdata
-
-import "fmt"
-
-// containerdConfigTemplate is a .toml config for containerd. It contains a
-// formatting verb so the runtime field can be set via fmt.Sprintf.
-const containerdConfigTemplate = `
-disabled_plugins = ["restart"]
-[plugins.linux]
- runtime = "%s"
- runtime_root = "/tmp/test-containerd/runsc"
- shim = "/usr/local/bin/gvisor-containerd-shim"
- shim_debug = true
-
-[plugins.cri.containerd.runtimes.runsc]
- runtime_type = "io.containerd.runtime.v1.linux"
- runtime_engine = "%s"
-`
-
-// ContainerdConfig returns a containerd config file with the specified
-// runtime.
-func ContainerdConfig(runtime string) string {
- return fmt.Sprintf(containerdConfigTemplate, runtime, runtime)
-}
diff --git a/runsc/test/root/testdata/httpd.go b/runsc/test/root/testdata/httpd.go
deleted file mode 100644
index 45d5e33d4..000000000
--- a/runsc/test/root/testdata/httpd.go
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// 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 testdata
-
-// Httpd is a JSON config for an httpd container.
-const Httpd = `
-{
- "metadata": {
- "name": "httpd"
- },
- "image":{
- "image": "httpd"
- },
- "mounts": [
- ],
- "linux": {
- },
- "log_path": "httpd.log"
-}
-`
diff --git a/runsc/test/root/testdata/httpd_mount_paths.go b/runsc/test/root/testdata/httpd_mount_paths.go
deleted file mode 100644
index ac3f4446a..000000000
--- a/runsc/test/root/testdata/httpd_mount_paths.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// 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 testdata
-
-// HttpdMountPaths is a JSON config for an httpd container with additional
-// mounts.
-const HttpdMountPaths = `
-{
- "metadata": {
- "name": "httpd"
- },
- "image":{
- "image": "httpd"
- },
- "mounts": [
- {
- "container_path": "/var/run/secrets/kubernetes.io/serviceaccount",
- "host_path": "/var/lib/kubelet/pods/82bae206-cdf5-11e8-b245-8cdcd43ac064/volumes/kubernetes.io~secret/default-token-2rpfx",
- "readonly": true
- },
- {
- "container_path": "/etc/hosts",
- "host_path": "/var/lib/kubelet/pods/82bae206-cdf5-11e8-b245-8cdcd43ac064/etc-hosts",
- "readonly": false
- },
- {
- "container_path": "/dev/termination-log",
- "host_path": "/var/lib/kubelet/pods/82bae206-cdf5-11e8-b245-8cdcd43ac064/containers/httpd/d1709580",
- "readonly": false
- },
- {
- "container_path": "/usr/local/apache2/htdocs/test",
- "host_path": "/var/lib/kubelet/pods/82bae206-cdf5-11e8-b245-8cdcd43ac064",
- "readonly": true
- }
- ],
- "linux": {
- },
- "log_path": "httpd.log"
-}
-`
diff --git a/runsc/test/root/testdata/sandbox.go b/runsc/test/root/testdata/sandbox.go
deleted file mode 100644
index 0db210370..000000000
--- a/runsc/test/root/testdata/sandbox.go
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// 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 testdata
-
-// Sandbox is a default JSON config for a sandbox.
-const Sandbox = `
-{
- "metadata": {
- "name": "default-sandbox",
- "namespace": "default",
- "attempt": 1,
- "uid": "hdishd83djaidwnduwk28bcsb"
- },
- "linux": {
- },
- "log_directory": "/tmp"
-}
-`
diff --git a/runsc/test/testutil/testutil_race.go b/runsc/test/testutil/testutil_race.go
deleted file mode 100644
index 86db6ffa1..000000000
--- a/runsc/test/testutil/testutil_race.go
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// 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.
-
-// +build race
-
-package testutil
-
-func init() {
- RaceEnabled = true
-}
diff --git a/runsc/test/testutil/BUILD b/runsc/testutil/BUILD
index 327e7ca4d..d44ebc906 100644
--- a/runsc/test/testutil/BUILD
+++ b/runsc/testutil/BUILD
@@ -4,19 +4,14 @@ package(licenses = ["notice"])
go_library(
name = "testutil",
- srcs = [
- "crictl.go",
- "docker.go",
- "testutil.go",
- "testutil_race.go",
- ],
- importpath = "gvisor.dev/gvisor/runsc/test/testutil",
+ testonly = 1,
+ srcs = ["testutil.go"],
+ importpath = "gvisor.dev/gvisor/runsc/testutil",
visibility = ["//:sandbox"],
deps = [
"//runsc/boot",
"//runsc/specutils",
"@com_github_cenkalti_backoff//:go_default_library",
- "@com_github_kr_pty//:go_default_library",
"@com_github_opencontainers_runtime-spec//specs-go:go_default_library",
],
)
diff --git a/runsc/test/testutil/testutil.go b/runsc/testutil/testutil.go
index 4a3dfa0e3..57ab73d97 100644
--- a/runsc/test/testutil/testutil.go
+++ b/runsc/testutil/testutil.go
@@ -18,8 +18,10 @@ package testutil
import (
"bufio"
"context"
+ "debug/elf"
"encoding/base32"
"encoding/json"
+ "flag"
"fmt"
"io"
"io/ioutil"
@@ -42,12 +44,18 @@ import (
"gvisor.dev/gvisor/runsc/specutils"
)
+var (
+ checkpoint = flag.Bool("checkpoint", true, "control checkpoint/restore support")
+)
+
func init() {
rand.Seed(time.Now().UnixNano())
}
-// RaceEnabled is set to true if it was built with '--race' option.
-var RaceEnabled = false
+// IsCheckpointSupported returns the relevant command line flag.
+func IsCheckpointSupported() bool {
+ return *checkpoint
+}
// TmpDir returns the absolute path to a writable directory that can be used as
// scratch by the test.
@@ -191,14 +199,11 @@ func SetupRootDir() (string, error) {
// SetupContainer creates a bundle and root dir for the container, generates a
// test config, and writes the spec to config.json in the bundle dir.
func SetupContainer(spec *specs.Spec, conf *boot.Config) (rootDir, bundleDir string, err error) {
- // Setup root dir if one hasn't been provided.
- if len(conf.RootDir) == 0 {
- rootDir, err = SetupRootDir()
- if err != nil {
- return "", "", err
- }
- conf.RootDir = rootDir
+ rootDir, err = SetupRootDir()
+ if err != nil {
+ return "", "", err
}
+ conf.RootDir = rootDir
bundleDir, err = SetupBundleDir(spec)
return rootDir, bundleDir, err
}
@@ -419,3 +424,17 @@ func WriteTmpFile(pattern, text string) (string, error) {
func RandomName(prefix string) string {
return fmt.Sprintf("%s-%06d", prefix, rand.Int31n(1000000))
}
+
+// IsStatic returns true iff the given file is a static binary.
+func IsStatic(filename string) (bool, error) {
+ f, err := elf.Open(filename)
+ if err != nil {
+ return false, err
+ }
+ for _, prog := range f.Progs {
+ if prog.Type == elf.PT_INTERP {
+ return false, nil // Has interpreter.
+ }
+ }
+ return true, nil
+}
diff --git a/runsc/tools/dockercfg/BUILD b/runsc/tools/dockercfg/BUILD
deleted file mode 100644
index 5cff917ed..000000000
--- a/runsc/tools/dockercfg/BUILD
+++ /dev/null
@@ -1,10 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_binary")
-
-package(licenses = ["notice"])
-
-go_binary(
- name = "dockercfg",
- srcs = ["dockercfg.go"],
- visibility = ["//visibility:public"],
- deps = ["@com_github_google_subcommands//:go_default_library"],
-)
diff --git a/runsc/tools/dockercfg/dockercfg.go b/runsc/tools/dockercfg/dockercfg.go
deleted file mode 100644
index eb9dbd421..000000000
--- a/runsc/tools/dockercfg/dockercfg.go
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// 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.
-
-// Helper tool to configure Docker daemon.
-package main
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "io/ioutil"
- "log"
- "os"
-
- "flag"
- "github.com/google/subcommands"
-)
-
-var (
- configFile = flag.String("config_file", "/etc/docker/daemon.json", "path to Docker daemon config file")
- experimental = flag.Bool("experimental", false, "enable experimental features")
-)
-
-func main() {
- subcommands.Register(subcommands.HelpCommand(), "")
- subcommands.Register(subcommands.FlagsCommand(), "")
- subcommands.Register(&runtimeAdd{}, "")
- subcommands.Register(&runtimeRemove{}, "")
-
- // All subcommands must be registered before flag parsing.
- flag.Parse()
-
- exitCode := subcommands.Execute(context.Background())
- os.Exit(int(exitCode))
-}
-
-type runtime struct {
- Path string `json:"path,omitempty"`
- RuntimeArgs []string `json:"runtimeArgs,omitempty"`
-}
-
-// runtimeAdd implements subcommands.Command.
-type runtimeAdd struct {
-}
-
-// Name implements subcommands.Command.Name.
-func (*runtimeAdd) Name() string {
- return "runtime-add"
-}
-
-// Synopsis implements subcommands.Command.Synopsis.
-func (*runtimeAdd) Synopsis() string {
- return "adds a runtime to docker daemon configuration"
-}
-
-// Usage implements subcommands.Command.Usage.
-func (*runtimeAdd) Usage() string {
- return `runtime-add [flags] <name> <path> [args...] -- if provided, args are passed as arguments to the runtime
-`
-}
-
-// SetFlags implements subcommands.Command.SetFlags.
-func (*runtimeAdd) SetFlags(*flag.FlagSet) {
-}
-
-// Execute implements subcommands.Command.Execute.
-func (r *runtimeAdd) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
- if f.NArg() < 2 {
- f.Usage()
- return subcommands.ExitUsageError
- }
- name := f.Arg(0)
- path := f.Arg(1)
- runtimeArgs := f.Args()[2:]
-
- fmt.Printf("Adding runtime %q to file %q\n", name, *configFile)
- c, err := readConfig(*configFile)
- if err != nil {
- log.Fatalf("Error reading config file %q: %v", *configFile, err)
- }
-
- var rts map[string]interface{}
- if i, ok := c["runtimes"]; ok {
- rts = i.(map[string]interface{})
- } else {
- rts = make(map[string]interface{})
- c["runtimes"] = rts
- }
- if *experimental {
- c["experimental"] = true
- }
- rts[name] = runtime{Path: path, RuntimeArgs: runtimeArgs}
-
- if err := writeConfig(c, *configFile); err != nil {
- log.Fatalf("Error writing config file %q: %v", *configFile, err)
- }
- return subcommands.ExitSuccess
-}
-
-// runtimeRemove implements subcommands.Command.
-type runtimeRemove struct {
-}
-
-// Name implements subcommands.Command.Name.
-func (*runtimeRemove) Name() string {
- return "runtime-rm"
-}
-
-// Synopsis implements subcommands.Command.Synopsis.
-func (*runtimeRemove) Synopsis() string {
- return "removes a runtime from docker daemon configuration"
-}
-
-// Usage implements subcommands.Command.Usage.
-func (*runtimeRemove) Usage() string {
- return `runtime-rm [flags] <name>
-`
-}
-
-// SetFlags implements subcommands.Command.SetFlags.
-func (*runtimeRemove) SetFlags(*flag.FlagSet) {
-}
-
-// Execute implements subcommands.Command.Execute.
-func (r *runtimeRemove) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
- if f.NArg() != 1 {
- f.Usage()
- return subcommands.ExitUsageError
- }
- name := f.Arg(0)
-
- fmt.Printf("Removing runtime %q from file %q\n", name, *configFile)
- c, err := readConfig(*configFile)
- if err != nil {
- log.Fatalf("Error reading config file %q: %v", *configFile, err)
- }
-
- var rts map[string]interface{}
- if i, ok := c["runtimes"]; ok {
- rts = i.(map[string]interface{})
- } else {
- log.Fatalf("runtime %q not found", name)
- }
- if _, ok := rts[name]; !ok {
- log.Fatalf("runtime %q not found", name)
- }
- delete(rts, name)
-
- if err := writeConfig(c, *configFile); err != nil {
- log.Fatalf("Error writing config file %q: %v", *configFile, err)
- }
- return subcommands.ExitSuccess
-}
-
-func readConfig(path string) (map[string]interface{}, error) {
- configBytes, err := ioutil.ReadFile(path)
- if err != nil && !os.IsNotExist(err) {
- return nil, err
- }
- c := make(map[string]interface{})
- if len(configBytes) > 0 {
- if err := json.Unmarshal(configBytes, &c); err != nil {
- return nil, err
- }
- }
- return c, nil
-}
-
-func writeConfig(c map[string]interface{}, path string) error {
- b, err := json.MarshalIndent(c, "", " ")
- if err != nil {
- return err
- }
-
- if err := os.Rename(path, path+"~"); err != nil && !os.IsNotExist(err) {
- return fmt.Errorf("error renaming config file %q: %v", path, err)
- }
- if err := ioutil.WriteFile(path, b, 0644); err != nil {
- return fmt.Errorf("error writing config file %q: %v", path, err)
- }
- return nil
-}