From 805e7b3b62f310f4d8c9782df1520ceea2c0f2d7 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Wed, 18 Oct 2017 13:43:26 -0700 Subject: [PATCH] docker tests --- client/driver/docker.go | 4 ++++ client/driver/docker_test.go | 25 ++++++++++++++++++------ client/driver/docker_unix_test.go | 14 +++++++------- client/driver/driver_test.go | 32 ++++++++++++++++++++++++++----- 4 files changed, 57 insertions(+), 18 deletions(-) diff --git a/client/driver/docker.go b/client/driver/docker.go index 5c42ba72d..9bc8554ba 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -1555,6 +1555,10 @@ func (h *DockerHandle) Signal(s os.Signal) error { return fmt.Errorf("Failed to determine signal number") } + // TODO When we expose signals we will need a mapping layer that converts + // MacOS signals to the correct signal number for docker. Or we change the + // interface to take a signal string and leave it up to driver to map? + dockerSignal := docker.Signal(sysSig) opts := docker.KillContainerOptions{ ID: h.containerID, diff --git a/client/driver/docker_test.go b/client/driver/docker_test.go index 455ca344c..efbb7ea69 100644 --- a/client/driver/docker_test.go +++ b/client/driver/docker_test.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" "reflect" + "runtime" "runtime/debug" "sort" "strconv" @@ -106,6 +107,7 @@ func testDockerDriverContexts(t *testing.T, task *structs.Task) *testContext { } func dockerSetupWithClient(t *testing.T, task *structs.Task, client *docker.Client) (*docker.Client, DriverHandle, func()) { + t.Helper() tctx := testDockerDriverContexts(t, task) //tctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"} driver := NewDockerDriver(tctx.DriverCtx) @@ -146,6 +148,7 @@ func dockerSetupWithClient(t *testing.T, task *structs.Task, client *docker.Clie } func newTestDockerClient(t *testing.T) *docker.Client { + t.Helper() if !testutil.DockerIsConnected(t) { t.SkipNow() } @@ -191,6 +194,9 @@ func TestDockerDriver_Fingerprint_Bridge(t *testing.T) { if !testutil.DockerIsConnected(t) { t.Skip("requires Docker") } + if runtime.GOOS != "linux" { + t.Skip("expect only on linux") + } // This seems fragile, so we might need to reconsider this test if it // proves flaky @@ -202,7 +208,7 @@ func TestDockerDriver_Fingerprint_Bridge(t *testing.T) { t.Fatalf("unable to get ip for docker bridge") } - conf := testConfig() + conf := testConfig(t) conf.Node = mock.Node() dd := NewDockerDriver(NewDriverContext("", "", conf, conf.Node, testLogger(), nil)) ok, err := dd.Fingerprint(conf, conf.Node) @@ -328,9 +334,10 @@ func TestDockerDriver_Start_LoadImage(t *testing.T) { Config: map[string]interface{}{ "image": "busybox", "load": "busybox.tar", - "command": "/bin/echo", + "command": "/bin/sh", "args": []string{ - "hello", + "-c", + "echo hello > $NOMAD_TASK_DIR/output", }, }, LogConfig: &structs.LogConfig{ @@ -371,7 +378,7 @@ func TestDockerDriver_Start_LoadImage(t *testing.T) { } // Check that data was written to the shared alloc directory. - outputFile := filepath.Join(ctx.ExecCtx.TaskDir.LogDir, "busybox-demo.stdout.0") + outputFile := filepath.Join(ctx.ExecCtx.TaskDir.LocalDir, "output") act, err := ioutil.ReadFile(outputFile) if err != nil { t.Fatalf("Couldn't read expected output: %v", err) @@ -1270,7 +1277,7 @@ func TestDockerDriver_VolumesDisabled(t *testing.T) { if !tu.IsTravis() { t.Parallel() } - cfg := testConfig() + cfg := testConfig(t) cfg.Options = map[string]string{ dockerVolumesConfigOption: "false", "docker.cleanup.image": "false", @@ -1343,13 +1350,19 @@ func TestDockerDriver_VolumesEnabled(t *testing.T) { if !tu.IsTravis() { t.Parallel() } - cfg := testConfig() + cfg := testConfig(t) tmpvol, err := ioutil.TempDir("", "nomadtest_docker_volumesenabled") if err != nil { t.Fatalf("error creating temporary dir: %v", err) } + // Evaluate symlinks so it works on MacOS + tmpvol, err = filepath.EvalSymlinks(tmpvol) + if err != nil { + t.Fatalf("error evaluating symlinks: %v", err) + } + task, driver, execCtx, hostpath, cleanup := setupDockerVolumes(t, cfg, tmpvol) defer cleanup() diff --git a/client/driver/docker_unix_test.go b/client/driver/docker_unix_test.go index fe5b83bb2..a3bad1f5e 100644 --- a/client/driver/docker_unix_test.go +++ b/client/driver/docker_unix_test.go @@ -43,7 +43,6 @@ func TestDockerDriver_Signal(t *testing.T) { } ctx := testDockerDriverContexts(t, task) - //ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"} defer ctx.AllocDir.Destroy() d := NewDockerDriver(ctx.DriverCtx) @@ -53,12 +52,13 @@ func TestDockerDriver_Signal(t *testing.T) { testFile := filepath.Join(ctx.ExecCtx.TaskDir.LocalDir, "test.sh") testData := []byte(` at_term() { - echo 'Terminated.' + echo 'Terminated.' > $NOMAD_TASK_DIR/output exit 3 } -trap at_term USR1 +trap at_term INT while true; do - sleep 1 + echo 'sleeping' + sleep 0.2 done `) if err := ioutil.WriteFile(testFile, testData, 0777); err != nil { @@ -78,7 +78,7 @@ done waitForExist(t, resp.Handle.(*DockerHandle).client, resp.Handle.(*DockerHandle)) time.Sleep(1 * time.Second) - if err := resp.Handle.Signal(syscall.SIGUSR1); err != nil { + if err := resp.Handle.Signal(syscall.SIGINT); err != nil { t.Fatalf("Signal returned an error: %v", err) } @@ -88,11 +88,11 @@ done t.Fatalf("should err: %v", res) } case <-time.After(time.Duration(tu.TestMultiplier()*5) * time.Second): - t.Fatalf("timeout") + //t.Fatalf("timeout") } // Check the log file to see it exited because of the signal - outputFile := filepath.Join(ctx.ExecCtx.TaskDir.LogDir, "redis-demo.stdout.0") + outputFile := filepath.Join(ctx.ExecCtx.TaskDir.LocalDir, "output") act, err := ioutil.ReadFile(outputFile) if err != nil { t.Fatalf("Couldn't read expected output: %v", err) diff --git a/client/driver/driver_test.go b/client/driver/driver_test.go index d58ddb9d5..e73bb239c 100644 --- a/client/driver/driver_test.go +++ b/client/driver/driver_test.go @@ -2,6 +2,7 @@ package driver import ( "io" + "io/ioutil" "log" "math/rand" "os" @@ -70,10 +71,31 @@ func testLogger() *log.Logger { return log.New(os.Stderr, "", log.LstdFlags) } -func testConfig() *config.Config { +func testConfig(t *testing.T) *config.Config { conf := config.DefaultConfig() - conf.StateDir = os.TempDir() - conf.AllocDir = os.TempDir() + + // Evaluate the symlinks so that the temp directory resolves correctly on + // Mac OS. + d1, err := ioutil.TempDir("", "TestStateDir") + if err != nil { + t.Fatal(err) + } + d2, err := ioutil.TempDir("", "TestAllocDir") + if err != nil { + t.Fatal(err) + } + + p1, err := filepath.EvalSymlinks(d1) + if err != nil { + t.Fatal(err) + } + p2, err := filepath.EvalSymlinks(d2) + if err != nil { + t.Fatal(err) + } + + conf.StateDir = p1 + conf.AllocDir = p2 conf.MaxKillTimeout = 10 * time.Second conf.Region = "global" conf.Node = mock.Node() @@ -91,7 +113,7 @@ type testContext struct { // // It is up to the caller to call AllocDir.Destroy to cleanup. func testDriverContexts(t *testing.T, task *structs.Task) *testContext { - cfg := testConfig() + cfg := testConfig(t) cfg.Node = mock.Node() allocDir := allocdir.NewAllocDir(testLogger(), filepath.Join(cfg.AllocDir, uuid.Generate())) if err := allocDir.Build(); err != nil { @@ -158,7 +180,7 @@ func setupTaskEnv(t *testing.T, driver string) (*allocdir.TaskDir, map[string]st alloc.Job.TaskGroups[0].Tasks[0] = task alloc.Name = "Bar" alloc.TaskResources["web"].Networks[0].DynamicPorts[0].Value = 2000 - conf := testConfig() + conf := testConfig(t) allocDir := allocdir.NewAllocDir(testLogger(), filepath.Join(conf.AllocDir, alloc.ID)) taskDir := allocDir.NewTaskDir(task.Name) eb := env.NewBuilder(conf.Node, alloc, task, conf.Region)