summaryrefslogtreecommitdiffhomepage
path: root/test/root/crictl_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'test/root/crictl_test.go')
-rw-r--r--test/root/crictl_test.go536
1 files changed, 0 insertions, 536 deletions
diff --git a/test/root/crictl_test.go b/test/root/crictl_test.go
deleted file mode 100644
index df52dd381..000000000
--- a/test/root/crictl_test.go
+++ /dev/null
@@ -1,536 +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"
- "encoding/json"
- "fmt"
- "io"
- "io/ioutil"
- "net/http"
- "os"
- "os/exec"
- "path"
- "regexp"
- "strconv"
- "strings"
- "sync"
- "testing"
- "time"
-
- "gvisor.dev/gvisor/pkg/cleanup"
- "gvisor.dev/gvisor/pkg/test/criutil"
- "gvisor.dev/gvisor/pkg/test/dockerutil"
- "gvisor.dev/gvisor/pkg/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/.
-
-// Sandbox returns a JSON config for a simple sandbox. Sandbox names must be
-// unique so different names should be used when running tests on the same
-// containerd instance.
-func Sandbox(name string) string {
- // Sandbox is a default JSON config for a sandbox.
- s := map[string]interface{}{
- "metadata": map[string]string{
- "name": name,
- "namespace": "default",
- "uid": testutil.RandomID(""),
- },
- "linux": map[string]string{},
- "log_directory": "/tmp",
- }
-
- v, err := json.Marshal(s)
- if err != nil {
- // This shouldn't happen.
- panic(err)
- }
- return string(v)
-}
-
-// SimpleSpec returns a JSON config for a simple container that runs the
-// specified command in the specified image.
-func SimpleSpec(name, image string, cmd []string, extra map[string]interface{}) string {
- s := map[string]interface{}{
- "metadata": map[string]string{
- "name": name,
- },
- "image": map[string]string{
- "image": testutil.ImageByName(image),
- },
- // Log files are not deleted after root tests are run. Log to random
- // paths to ensure logs are fresh.
- "log_path": fmt.Sprintf("%s.log", testutil.RandomID(name)),
- "stdin": false,
- "tty": false,
- }
- if len(cmd) > 0 { // Omit if empty.
- s["command"] = cmd
- }
- for k, v := range extra {
- s[k] = v // Extra settings.
- }
- v, err := json.Marshal(s)
- if err != nil {
- // This shouldn't happen.
- panic(err)
- }
- return string(v)
-}
-
-// Httpd is a JSON config for an httpd container.
-var Httpd = SimpleSpec("httpd", "basic/httpd", nil, nil)
-
-// TestCrictlSanity refers to b/112433158.
-func TestCrictlSanity(t *testing.T) {
- for _, version := range allVersions {
- t.Run(version, func(t *testing.T) {
- // Setup containerd and crictl.
- crictl, cleanup, err := setup(t, version)
- if err != nil {
- t.Fatalf("failed to setup crictl: %v", err)
- }
- defer cleanup()
- podID, contID, err := crictl.StartPodAndContainer(containerdRuntime, "basic/httpd", Sandbox("default"), Httpd)
- if err != nil {
- t.Fatalf("start failed: %v", 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.Fatalf("stop failed: %v", err)
- }
- })
- }
-}
-
-// HttpdMountPaths is a JSON config for an httpd container with additional
-// mounts.
-var HttpdMountPaths = SimpleSpec("httpd", "basic/httpd", nil, map[string]interface{}{
- "mounts": []map[string]interface{}{
- map[string]interface{}{
- "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,
- },
- map[string]interface{}{
- "container_path": "/etc/hosts",
- "host_path": "/var/lib/kubelet/pods/82bae206-cdf5-11e8-b245-8cdcd43ac064/etc-hosts",
- "readonly": false,
- },
- map[string]interface{}{
- "container_path": "/dev/termination-log",
- "host_path": "/var/lib/kubelet/pods/82bae206-cdf5-11e8-b245-8cdcd43ac064/containers/httpd/d1709580",
- "readonly": false,
- },
- map[string]interface{}{
- "container_path": "/usr/local/apache2/htdocs/test",
- "host_path": "/var/lib/kubelet/pods/82bae206-cdf5-11e8-b245-8cdcd43ac064",
- "readonly": true,
- },
- },
- "linux": map[string]interface{}{},
-})
-
-// TestMountPaths refers to b/117635704.
-func TestMountPaths(t *testing.T) {
- for _, version := range allVersions {
- t.Run(version, func(t *testing.T) {
- // Setup containerd and crictl.
- crictl, cleanup, err := setup(t, version)
- if err != nil {
- t.Fatalf("failed to setup crictl: %v", err)
- }
- defer cleanup()
- podID, contID, err := crictl.StartPodAndContainer(containerdRuntime, "basic/httpd", Sandbox("default"), HttpdMountPaths)
- if err != nil {
- t.Fatalf("start failed: %v", 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.Fatalf("stop failed: %v", err)
- }
- })
- }
-}
-
-// TestMountPaths refers to b/118728671.
-func TestMountOverSymlinks(t *testing.T) {
- for _, version := range allVersions {
- t.Run(version, func(t *testing.T) {
- // Setup containerd and crictl.
- crictl, cleanup, err := setup(t, version)
- if err != nil {
- t.Fatalf("failed to setup crictl: %v", err)
- }
- defer cleanup()
-
- spec := SimpleSpec("busybox", "basic/resolv", []string{"sleep", "1000"}, nil)
- podID, contID, err := crictl.StartPodAndContainer(containerdRuntime, "basic/resolv", Sandbox("default"), spec)
- if err != nil {
- t.Fatalf("start failed: %v", err)
- }
-
- out, err := crictl.Exec(contID, "readlink", "/etc/resolv.conf")
- if err != nil {
- t.Fatalf("readlink failed: %v, out: %s", err, out)
- }
- 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.Fatalf("cat failed: %v, out: %s", err, etc)
- }
- tmp, err := crictl.Exec(contID, "cat", "/tmp/resolv.conf")
- if err != nil {
- t.Fatalf("cat failed: %v, out: %s", err, out)
- }
- 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.Fatalf("stop failed: %v", err)
- }
- })
- }
-}
-
-// TestHomeDir tests that the HOME environment variable is set for
-// Pod containers.
-func TestHomeDir(t *testing.T) {
- for _, version := range allVersions {
- t.Run(version, func(t *testing.T) {
- // Setup containerd and crictl.
- crictl, cleanup, err := setup(t, version)
- if err != nil {
- t.Fatalf("failed to setup crictl: %v", err)
- }
- defer cleanup()
-
- // Note that container ID returned here is a sub-container. All Pod
- // containers are sub-containers. The root container of the sandbox is the
- // pause container.
- t.Run("sub-container", func(t *testing.T) {
- contSpec := SimpleSpec("subcontainer", "basic/busybox", []string{"sh", "-c", "echo $HOME"}, nil)
- podID, contID, err := crictl.StartPodAndContainer(containerdRuntime, "basic/busybox", Sandbox("subcont-sandbox"), contSpec)
- if err != nil {
- t.Fatalf("start failed: %v", err)
- }
-
- out, err := crictl.Logs(contID)
- if err != nil {
- t.Fatalf("failed retrieving container logs: %v, out: %s", err, out)
- }
- if got, want := strings.TrimSpace(string(out)), "/root"; got != want {
- t.Fatalf("Home directory invalid. Got %q, Want : %q", got, want)
- }
-
- // Stop everything; note that the pod may have already stopped.
- crictl.StopPodAndContainer(podID, contID)
- })
-
- // Tests that HOME is set for the exec process.
- t.Run("exec", func(t *testing.T) {
- contSpec := SimpleSpec("exec", "basic/busybox", []string{"sleep", "1000"}, nil)
- podID, contID, err := crictl.StartPodAndContainer(containerdRuntime, "basic/busybox", Sandbox("exec-sandbox"), contSpec)
- if err != nil {
- t.Fatalf("start failed: %v", err)
- }
-
- out, err := crictl.Exec(contID, "sh", "-c", "echo $HOME")
- if err != nil {
- t.Fatalf("failed retrieving container logs: %v, out: %s", err, out)
- }
- if got, want := strings.TrimSpace(string(out)), "/root"; got != want {
- t.Fatalf("Home directory invalid. Got %q, Want : %q", got, want)
- }
-
- // Stop everything.
- if err := crictl.StopPodAndContainer(podID, contID); err != nil {
- t.Fatalf("stop failed: %v", err)
- }
- })
- })
- }
-}
-
-const containerdRuntime = "runsc"
-
-const v1Template = `
-disabled_plugins = ["restart"]
-[plugins.cri]
- disable_tcp_service = true
-[plugins.linux]
- shim = "%s"
- shim_debug = true
-[plugins.cri.containerd.runtimes.` + containerdRuntime + `]
- runtime_type = "io.containerd.runtime.v1.linux"
- runtime_engine = "%s"
- runtime_root = "%s/root/runsc"
-`
-
-const v2Template = `
-disabled_plugins = ["restart"]
-[plugins.cri]
- disable_tcp_service = true
-[plugins.linux]
- shim_debug = true
-[plugins.cri.containerd.runtimes.` + containerdRuntime + `]
- runtime_type = "io.containerd.` + containerdRuntime + `.v1"
-[plugins.cri.containerd.runtimes.` + containerdRuntime + `.options]
- TypeUrl = "io.containerd.` + containerdRuntime + `.v1.options"
-`
-
-const (
- // v1 is the containerd API v1.
- v1 string = "v1"
-
- // v2 is the containerd API v2.
- v2 string = "v2"
-)
-
-// allVersions is the set of known versions.
-var allVersions = []string{v1, v2}
-
-// 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, version string) (*criutil.Crictl, func(), error) {
- // 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)
- }
- cu := cleanup.Make(func() { os.RemoveAll(containerdRoot) })
- defer cu.Clean()
- t.Logf("Using containerd root: %s", containerdRoot)
-
- containerdState, err := ioutil.TempDir(testutil.TmpDir(), "containerd-state")
- if err != nil {
- t.Fatalf("failed to create containerd state: %v", err)
- }
- cu.Add(func() { os.RemoveAll(containerdState) })
- t.Logf("Using containerd state: %s", containerdState)
-
- sockDir, err := ioutil.TempDir(testutil.TmpDir(), "containerd-sock")
- if err != nil {
- t.Fatalf("failed to create containerd socket directory: %v", err)
- }
- cu.Add(func() { os.RemoveAll(sockDir) })
- sockAddr := path.Join(sockDir, "test.sock")
- t.Logf("Using containerd socket: %s", sockAddr)
-
- // Extract the containerd version.
- versionCmd := exec.Command(getContainerd(), "-v")
- out, err := versionCmd.CombinedOutput()
- if err != nil {
- t.Fatalf("error extracting containerd version: %v (%s)", err, string(out))
- }
- r := regexp.MustCompile(" v([0-9]+)\\.([0-9]+)\\.([0-9+])")
- vs := r.FindStringSubmatch(string(out))
- if len(vs) != 4 {
- t.Fatalf("error unexpected version string: %s", string(out))
- }
- major, err := strconv.ParseUint(vs[1], 10, 64)
- if err != nil {
- t.Fatalf("error parsing containerd major version: %v (%s)", err, string(out))
- }
- minor, err := strconv.ParseUint(vs[2], 10, 64)
- if err != nil {
- t.Fatalf("error parsing containerd minor version: %v (%s)", err, string(out))
- }
- t.Logf("Using containerd version: %d.%d", major, minor)
-
- // We rewrite a configuration. This is based on the current docker
- // configuration for the runtime under test.
- runtime, err := dockerutil.RuntimePath()
- if err != nil {
- t.Fatalf("error discovering runtime path: %v", err)
- }
- t.Logf("Using runtime: %v", runtime)
-
- // Construct a PATH that includes the runtime directory. This is
- // because the shims will be installed there, and containerd may infer
- // the binary name and search the PATH.
- runtimeDir := path.Dir(runtime)
- modifiedPath := os.Getenv("PATH")
- if modifiedPath != "" {
- modifiedPath = ":" + modifiedPath // We prepend below.
- }
- modifiedPath = path.Dir(getContainerd()) + modifiedPath
- modifiedPath = runtimeDir + ":" + modifiedPath
- t.Logf("Using PATH: %v", modifiedPath)
-
- var (
- config string
- runpArgs []string
- )
- switch version {
- case v1:
- // This is only supported less than 1.3.
- if major > 1 || (major == 1 && minor >= 3) {
- t.Skipf("skipping unsupported containerd (want less than 1.3, got %d.%d)", major, minor)
- }
-
- // We provide the shim, followed by the runtime, and then a
- // temporary root directory.
- config = fmt.Sprintf(v1Template, criutil.ResolvePath("gvisor-containerd-shim"), runtime, containerdRoot)
- case v2:
- // This is only supported past 1.2.
- if major < 1 || (major == 1 && minor <= 1) {
- t.Skipf("skipping incompatible containerd (want at least 1.2, got %d.%d)", major, minor)
- }
-
- // The runtime is provided via parameter. Note that the v2 shim
- // binary name is always containerd-shim-* so we don't actually
- // care about the docker runtime name.
- config = v2Template
- default:
- t.Fatalf("unknown version: %s", version)
- }
- t.Logf("Using config: %s", config)
-
- // Generate the configuration for the test.
- configFile, configCleanup, err := testutil.WriteTmpFile("containerd-config", config)
- if err != nil {
- t.Fatalf("failed to write containerd config")
- }
- cu.Add(configCleanup)
-
- // Start containerd.
- args := []string{
- getContainerd(),
- "--config", configFile,
- "--log-level", "debug",
- "--root", containerdRoot,
- "--state", containerdState,
- "--address", sockAddr,
- }
- t.Logf("Using args: %s", strings.Join(args, " "))
- cmd := exec.Command(args[0], args[1:]...)
- cmd.Env = append(os.Environ(), "PATH="+modifiedPath)
-
- // Include output in logs.
- stderrPipe, err := cmd.StderrPipe()
- if err != nil {
- t.Fatalf("failed to create stderr pipe: %v", err)
- }
- cu.Add(func() { stderrPipe.Close() })
- stdoutPipe, err := cmd.StdoutPipe()
- if err != nil {
- t.Fatalf("failed to create stdout pipe: %v", err)
- }
- cu.Add(func() { stdoutPipe.Close() })
- var (
- wg sync.WaitGroup
- stderr bytes.Buffer
- stdout bytes.Buffer
- )
- startupR, startupW := io.Pipe()
- wg.Add(2)
- go func() {
- defer wg.Done()
- io.Copy(io.MultiWriter(startupW, &stderr), stderrPipe)
- }()
- go func() {
- defer wg.Done()
- io.Copy(io.MultiWriter(startupW, &stdout), stdoutPipe)
- }()
- cu.Add(func() {
- wg.Wait()
- t.Logf("containerd stdout: %s", stdout.String())
- t.Logf("containerd stderr: %s", stderr.String())
- })
-
- // Start the process.
- if err := cmd.Start(); err != nil {
- t.Fatalf("failed running containerd: %v", err)
- }
-
- // Wait for containerd to boot.
- if err := testutil.WaitUntilRead(startupR, "Start streaming server", 10*time.Second); err != nil {
- t.Fatalf("failed to start containerd: %v", err)
- }
-
- // Discard all subsequent data.
- go io.Copy(ioutil.Discard, startupR)
-
- // Create the crictl interface.
- cc := criutil.NewCrictl(t, sockAddr, runpArgs)
- cu.Add(cc.CleanUp)
-
- // Kill must be the last cleanup (as it will be executed first).
- cu.Add(func() {
- // Best effort: ignore errors.
- testutil.KillCommand(cmd)
- })
-
- return cc, cu.Release(), nil
-}
-
-// httpGet GETs the contents of a file served from a pod on port 80.
-func httpGet(crictl *criutil.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 {
- // Use the local path if it exists, otherwise, use the system one.
- if _, err := os.Stat("/usr/local/bin/containerd"); err == nil {
- return "/usr/local/bin/containerd"
- }
- return "/usr/bin/containerd"
-}