github.com/ncodes/nomad@v0.5.7-0.20170403112158-97adf4a74fb3/client/driver/docker_test.go (about)

     1  package driver
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"math/rand"
     7  	"os"
     8  	"path/filepath"
     9  	"reflect"
    10  	"runtime/debug"
    11  	"strconv"
    12  	"strings"
    13  	"syscall"
    14  	"testing"
    15  	"time"
    16  
    17  	docker "github.com/fsouza/go-dockerclient"
    18  	"github.com/ncodes/nomad/client/allocdir"
    19  	"github.com/ncodes/nomad/client/config"
    20  	"github.com/ncodes/nomad/client/driver/env"
    21  	cstructs "github.com/ncodes/nomad/client/structs"
    22  	"github.com/ncodes/nomad/client/testutil"
    23  	"github.com/ncodes/nomad/nomad/mock"
    24  	"github.com/ncodes/nomad/nomad/structs"
    25  	tu "github.com/ncodes/nomad/testutil"
    26  )
    27  
    28  func dockerIsRemote(t *testing.T) bool {
    29  	client, err := docker.NewClientFromEnv()
    30  	if err != nil {
    31  		return false
    32  	}
    33  
    34  	// Technically this could be a local tcp socket but for testing purposes
    35  	// we'll just assume that tcp is only used for remote connections.
    36  	if client.Endpoint()[0:3] == "tcp" {
    37  		return true
    38  	}
    39  	return false
    40  }
    41  
    42  // Ports used by tests
    43  var (
    44  	docker_reserved = 32768 + int(rand.Int31n(25000))
    45  	docker_dynamic  = 32768 + int(rand.Int31n(25000))
    46  )
    47  
    48  // Returns a task with a reserved and dynamic port. The ports are returned
    49  // respectively.
    50  func dockerTask() (*structs.Task, int, int) {
    51  	docker_reserved += 1
    52  	docker_dynamic += 1
    53  	return &structs.Task{
    54  		Name:   "redis-demo",
    55  		Driver: "docker",
    56  		Config: map[string]interface{}{
    57  			"image":   "busybox",
    58  			"load":    "busybox.tar",
    59  			"command": "/bin/nc",
    60  			"args":    []string{"-l", "127.0.0.1", "-p", "0"},
    61  		},
    62  		LogConfig: &structs.LogConfig{
    63  			MaxFiles:      10,
    64  			MaxFileSizeMB: 10,
    65  		},
    66  		Resources: &structs.Resources{
    67  			MemoryMB: 256,
    68  			CPU:      512,
    69  			Networks: []*structs.NetworkResource{
    70  				&structs.NetworkResource{
    71  					IP:            "127.0.0.1",
    72  					ReservedPorts: []structs.Port{{Label: "main", Value: docker_reserved}},
    73  					DynamicPorts:  []structs.Port{{Label: "REDIS", Value: docker_dynamic}},
    74  				},
    75  			},
    76  		},
    77  	}, docker_reserved, docker_dynamic
    78  }
    79  
    80  // dockerSetup does all of the basic setup you need to get a running docker
    81  // process up and running for testing. Use like:
    82  //
    83  //	task := taskTemplate()
    84  //	// do custom task configuration
    85  //	client, handle, cleanup := dockerSetup(t, task)
    86  //	defer cleanup()
    87  //	// do test stuff
    88  //
    89  // If there is a problem during setup this function will abort or skip the test
    90  // and indicate the reason.
    91  func dockerSetup(t *testing.T, task *structs.Task) (*docker.Client, DriverHandle, func()) {
    92  	client := newTestDockerClient(t)
    93  	return dockerSetupWithClient(t, task, client)
    94  }
    95  
    96  func testDockerDriverContexts(t *testing.T, task *structs.Task) *testContext {
    97  	tctx := testDriverContexts(t, task)
    98  
    99  	// Drop the delay
   100  	tctx.DriverCtx.config.Options = make(map[string]string)
   101  	tctx.DriverCtx.config.Options[dockerImageRemoveDelayConfigOption] = "1s"
   102  
   103  	return tctx
   104  }
   105  
   106  func dockerSetupWithClient(t *testing.T, task *structs.Task, client *docker.Client) (*docker.Client, DriverHandle, func()) {
   107  	tctx := testDockerDriverContexts(t, task)
   108  	//tctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"}
   109  	driver := NewDockerDriver(tctx.DriverCtx)
   110  	copyImage(t, tctx.ExecCtx.TaskDir, "busybox.tar")
   111  
   112  	res, err := driver.Prestart(tctx.ExecCtx, task)
   113  	if err != nil {
   114  		tctx.AllocDir.Destroy()
   115  		t.Fatalf("error in prestart: %v", err)
   116  	}
   117  
   118  	handle, err := driver.Start(tctx.ExecCtx, task)
   119  	if err != nil {
   120  		tctx.AllocDir.Destroy()
   121  		t.Fatalf("Failed to start driver: %s\nStack\n%s", err, debug.Stack())
   122  	}
   123  
   124  	if handle == nil {
   125  		tctx.AllocDir.Destroy()
   126  		t.Fatalf("handle is nil\nStack\n%s", debug.Stack())
   127  	}
   128  
   129  	cleanup := func() {
   130  		driver.Cleanup(tctx.ExecCtx, res)
   131  		handle.Kill()
   132  		tctx.AllocDir.Destroy()
   133  	}
   134  
   135  	return client, handle, cleanup
   136  }
   137  
   138  func newTestDockerClient(t *testing.T) *docker.Client {
   139  	if !testutil.DockerIsConnected(t) {
   140  		t.SkipNow()
   141  	}
   142  
   143  	client, err := docker.NewClientFromEnv()
   144  	if err != nil {
   145  		t.Fatalf("Failed to initialize client: %s\nStack\n%s", err, debug.Stack())
   146  	}
   147  	return client
   148  }
   149  
   150  // This test should always pass, even if docker daemon is not available
   151  func TestDockerDriver_Fingerprint(t *testing.T) {
   152  	ctx := testDockerDriverContexts(t, &structs.Task{Name: "foo", Driver: "docker", Resources: basicResources})
   153  	//ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"}
   154  	defer ctx.AllocDir.Destroy()
   155  	d := NewDockerDriver(ctx.DriverCtx)
   156  	node := &structs.Node{
   157  		Attributes: make(map[string]string),
   158  	}
   159  	apply, err := d.Fingerprint(&config.Config{}, node)
   160  	if err != nil {
   161  		t.Fatalf("err: %v", err)
   162  	}
   163  	if apply != testutil.DockerIsConnected(t) {
   164  		t.Fatalf("Fingerprinter should detect when docker is available")
   165  	}
   166  	if node.Attributes["driver.docker"] != "1" {
   167  		t.Log("Docker daemon not available. The remainder of the docker tests will be skipped.")
   168  	}
   169  	t.Logf("Found docker version %s", node.Attributes["driver.docker.version"])
   170  }
   171  
   172  func TestDockerDriver_StartOpen_Wait(t *testing.T) {
   173  	if !testutil.DockerIsConnected(t) {
   174  		t.SkipNow()
   175  	}
   176  
   177  	task := &structs.Task{
   178  		Name:   "nc-demo",
   179  		Driver: "docker",
   180  		Config: map[string]interface{}{
   181  			"load":    "busybox.tar",
   182  			"image":   "busybox",
   183  			"command": "/bin/nc",
   184  			"args":    []string{"-l", "127.0.0.1", "-p", "0"},
   185  		},
   186  		LogConfig: &structs.LogConfig{
   187  			MaxFiles:      10,
   188  			MaxFileSizeMB: 10,
   189  		},
   190  		Resources: basicResources,
   191  	}
   192  
   193  	ctx := testDockerDriverContexts(t, task)
   194  	//ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"}
   195  	defer ctx.AllocDir.Destroy()
   196  	d := NewDockerDriver(ctx.DriverCtx)
   197  	copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar")
   198  
   199  	_, err := d.Prestart(ctx.ExecCtx, task)
   200  	if err != nil {
   201  		t.Fatalf("error in prestart: %v", err)
   202  	}
   203  
   204  	handle, err := d.Start(ctx.ExecCtx, task)
   205  	if err != nil {
   206  		t.Fatalf("err: %v", err)
   207  	}
   208  	if handle == nil {
   209  		t.Fatalf("missing handle")
   210  	}
   211  	defer handle.Kill()
   212  
   213  	// Attempt to open
   214  	handle2, err := d.Open(ctx.ExecCtx, handle.ID())
   215  	if err != nil {
   216  		t.Fatalf("err: %v", err)
   217  	}
   218  	if handle2 == nil {
   219  		t.Fatalf("missing handle")
   220  	}
   221  }
   222  
   223  func TestDockerDriver_Start_Wait(t *testing.T) {
   224  	task := &structs.Task{
   225  		Name:   "nc-demo",
   226  		Driver: "docker",
   227  		Config: map[string]interface{}{
   228  			"load":    "busybox.tar",
   229  			"image":   "busybox",
   230  			"command": "/bin/echo",
   231  			"args":    []string{"hello"},
   232  		},
   233  		Resources: &structs.Resources{
   234  			MemoryMB: 256,
   235  			CPU:      512,
   236  		},
   237  		LogConfig: &structs.LogConfig{
   238  			MaxFiles:      10,
   239  			MaxFileSizeMB: 10,
   240  		},
   241  	}
   242  
   243  	_, handle, cleanup := dockerSetup(t, task)
   244  	defer cleanup()
   245  
   246  	// Update should be a no-op
   247  	err := handle.Update(task)
   248  	if err != nil {
   249  		t.Fatalf("err: %v", err)
   250  	}
   251  
   252  	select {
   253  	case res := <-handle.WaitCh():
   254  		if !res.Successful() {
   255  			t.Fatalf("err: %v", res)
   256  		}
   257  	case <-time.After(time.Duration(tu.TestMultiplier()*5) * time.Second):
   258  		t.Fatalf("timeout")
   259  	}
   260  }
   261  
   262  func TestDockerDriver_Start_LoadImage(t *testing.T) {
   263  	if !testutil.DockerIsConnected(t) {
   264  		t.SkipNow()
   265  	}
   266  	task := &structs.Task{
   267  		Name:   "busybox-demo",
   268  		Driver: "docker",
   269  		Config: map[string]interface{}{
   270  			"image":   "busybox",
   271  			"load":    "busybox.tar",
   272  			"command": "/bin/echo",
   273  			"args": []string{
   274  				"hello",
   275  			},
   276  		},
   277  		LogConfig: &structs.LogConfig{
   278  			MaxFiles:      10,
   279  			MaxFileSizeMB: 10,
   280  		},
   281  		Resources: &structs.Resources{
   282  			MemoryMB: 256,
   283  			CPU:      512,
   284  		},
   285  	}
   286  
   287  	ctx := testDockerDriverContexts(t, task)
   288  	//ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"}
   289  	defer ctx.AllocDir.Destroy()
   290  	d := NewDockerDriver(ctx.DriverCtx)
   291  
   292  	// Copy the image into the task's directory
   293  	copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar")
   294  
   295  	_, err := d.Prestart(ctx.ExecCtx, task)
   296  	if err != nil {
   297  		t.Fatalf("error in prestart: %v", err)
   298  	}
   299  	handle, err := d.Start(ctx.ExecCtx, task)
   300  	if err != nil {
   301  		t.Fatalf("err: %v", err)
   302  	}
   303  	if handle == nil {
   304  		t.Fatalf("missing handle")
   305  	}
   306  	defer handle.Kill()
   307  
   308  	select {
   309  	case res := <-handle.WaitCh():
   310  		if !res.Successful() {
   311  			t.Fatalf("err: %v", res)
   312  		}
   313  	case <-time.After(time.Duration(tu.TestMultiplier()*5) * time.Second):
   314  		t.Fatalf("timeout")
   315  	}
   316  
   317  	// Check that data was written to the shared alloc directory.
   318  	outputFile := filepath.Join(ctx.ExecCtx.TaskDir.LogDir, "busybox-demo.stdout.0")
   319  	act, err := ioutil.ReadFile(outputFile)
   320  	if err != nil {
   321  		t.Fatalf("Couldn't read expected output: %v", err)
   322  	}
   323  
   324  	exp := "hello"
   325  	if strings.TrimSpace(string(act)) != exp {
   326  		t.Fatalf("Command outputted %v; want %v", act, exp)
   327  	}
   328  
   329  }
   330  
   331  func TestDockerDriver_Start_BadPull_Recoverable(t *testing.T) {
   332  	if !testutil.DockerIsConnected(t) {
   333  		t.SkipNow()
   334  	}
   335  	task := &structs.Task{
   336  		Name:   "busybox-demo",
   337  		Driver: "docker",
   338  		Config: map[string]interface{}{
   339  			"image":   "127.0.1.1:32121/foo", // bad path
   340  			"command": "/bin/echo",
   341  			"args": []string{
   342  				"hello",
   343  			},
   344  		},
   345  		LogConfig: &structs.LogConfig{
   346  			MaxFiles:      10,
   347  			MaxFileSizeMB: 10,
   348  		},
   349  		Resources: &structs.Resources{
   350  			MemoryMB: 256,
   351  			CPU:      512,
   352  		},
   353  	}
   354  
   355  	ctx := testDockerDriverContexts(t, task)
   356  	//ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"}
   357  	defer ctx.AllocDir.Destroy()
   358  	d := NewDockerDriver(ctx.DriverCtx)
   359  
   360  	_, err := d.Prestart(ctx.ExecCtx, task)
   361  	if err == nil {
   362  		t.Fatalf("want error in prestart: %v", err)
   363  	}
   364  
   365  	if rerr, ok := err.(*structs.RecoverableError); !ok {
   366  		t.Fatalf("want recoverable error: %+v", err)
   367  	} else if !rerr.IsRecoverable() {
   368  		t.Fatalf("error not recoverable: %+v", err)
   369  	}
   370  }
   371  
   372  func TestDockerDriver_Start_Wait_AllocDir(t *testing.T) {
   373  	// This test requires that the alloc dir be mounted into docker as a volume.
   374  	// Because this cannot happen when docker is run remotely, e.g. when running
   375  	// docker in a VM, we skip this when we detect Docker is being run remotely.
   376  	if !testutil.DockerIsConnected(t) || dockerIsRemote(t) {
   377  		t.SkipNow()
   378  	}
   379  
   380  	exp := []byte{'w', 'i', 'n'}
   381  	file := "output.txt"
   382  	task := &structs.Task{
   383  		Name:   "nc-demo",
   384  		Driver: "docker",
   385  		Config: map[string]interface{}{
   386  			"image":   "busybox",
   387  			"load":    "busybox.tar",
   388  			"command": "/bin/sh",
   389  			"args": []string{
   390  				"-c",
   391  				fmt.Sprintf(`sleep 1; echo -n %s > $%s/%s`,
   392  					string(exp), env.AllocDir, file),
   393  			},
   394  		},
   395  		LogConfig: &structs.LogConfig{
   396  			MaxFiles:      10,
   397  			MaxFileSizeMB: 10,
   398  		},
   399  		Resources: &structs.Resources{
   400  			MemoryMB: 256,
   401  			CPU:      512,
   402  		},
   403  	}
   404  
   405  	ctx := testDockerDriverContexts(t, task)
   406  	//ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"}
   407  	defer ctx.AllocDir.Destroy()
   408  	d := NewDockerDriver(ctx.DriverCtx)
   409  	copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar")
   410  
   411  	_, err := d.Prestart(ctx.ExecCtx, task)
   412  	if err != nil {
   413  		t.Fatalf("error in prestart: %v", err)
   414  	}
   415  	handle, err := d.Start(ctx.ExecCtx, task)
   416  	if err != nil {
   417  		t.Fatalf("err: %v", err)
   418  	}
   419  	if handle == nil {
   420  		t.Fatalf("missing handle")
   421  	}
   422  	defer handle.Kill()
   423  
   424  	select {
   425  	case res := <-handle.WaitCh():
   426  		if !res.Successful() {
   427  			t.Fatalf("err: %v", res)
   428  		}
   429  	case <-time.After(time.Duration(tu.TestMultiplier()*5) * time.Second):
   430  		t.Fatalf("timeout")
   431  	}
   432  
   433  	// Check that data was written to the shared alloc directory.
   434  	outputFile := filepath.Join(ctx.AllocDir.SharedDir, file)
   435  	act, err := ioutil.ReadFile(outputFile)
   436  	if err != nil {
   437  		t.Fatalf("Couldn't read expected output: %v", err)
   438  	}
   439  
   440  	if !reflect.DeepEqual(act, exp) {
   441  		t.Fatalf("Command outputted %v; want %v", act, exp)
   442  	}
   443  }
   444  
   445  func TestDockerDriver_Start_Kill_Wait(t *testing.T) {
   446  	task := &structs.Task{
   447  		Name:   "nc-demo",
   448  		Driver: "docker",
   449  		Config: map[string]interface{}{
   450  			"image":   "busybox",
   451  			"load":    "busybox.tar",
   452  			"command": "/bin/sleep",
   453  			"args":    []string{"10"},
   454  		},
   455  		LogConfig: &structs.LogConfig{
   456  			MaxFiles:      10,
   457  			MaxFileSizeMB: 10,
   458  		},
   459  		Resources: basicResources,
   460  	}
   461  
   462  	_, handle, cleanup := dockerSetup(t, task)
   463  	defer cleanup()
   464  
   465  	go func() {
   466  		time.Sleep(100 * time.Millisecond)
   467  		err := handle.Kill()
   468  		if err != nil {
   469  			t.Fatalf("err: %v", err)
   470  		}
   471  	}()
   472  
   473  	select {
   474  	case res := <-handle.WaitCh():
   475  		if res.Successful() {
   476  			t.Fatalf("should err: %v", res)
   477  		}
   478  	case <-time.After(time.Duration(tu.TestMultiplier()*10) * time.Second):
   479  		t.Fatalf("timeout")
   480  	}
   481  }
   482  
   483  func TestDockerDriver_StartN(t *testing.T) {
   484  	if !testutil.DockerIsConnected(t) {
   485  		t.SkipNow()
   486  	}
   487  
   488  	task1, _, _ := dockerTask()
   489  	task2, _, _ := dockerTask()
   490  	task3, _, _ := dockerTask()
   491  	taskList := []*structs.Task{task1, task2, task3}
   492  
   493  	handles := make([]DriverHandle, len(taskList))
   494  
   495  	t.Logf("Starting %d tasks", len(taskList))
   496  
   497  	// Let's spin up a bunch of things
   498  	for idx, task := range taskList {
   499  		ctx := testDockerDriverContexts(t, task)
   500  		//ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"}
   501  		defer ctx.AllocDir.Destroy()
   502  		d := NewDockerDriver(ctx.DriverCtx)
   503  		copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar")
   504  
   505  		_, err := d.Prestart(ctx.ExecCtx, task)
   506  		if err != nil {
   507  			t.Fatalf("error in prestart #%d: %v", idx+1, err)
   508  		}
   509  		handles[idx], err = d.Start(ctx.ExecCtx, task)
   510  		if err != nil {
   511  			t.Errorf("Failed starting task #%d: %s", idx+1, err)
   512  		}
   513  	}
   514  
   515  	t.Log("All tasks are started. Terminating...")
   516  
   517  	for idx, handle := range handles {
   518  		if handle == nil {
   519  			t.Errorf("Bad handle for task #%d", idx+1)
   520  			continue
   521  		}
   522  
   523  		err := handle.Kill()
   524  		if err != nil {
   525  			t.Errorf("Failed stopping task #%d: %s", idx+1, err)
   526  		}
   527  	}
   528  
   529  	t.Log("Test complete!")
   530  }
   531  
   532  func TestDockerDriver_StartNVersions(t *testing.T) {
   533  	if !testutil.DockerIsConnected(t) {
   534  		t.SkipNow()
   535  	}
   536  
   537  	task1, _, _ := dockerTask()
   538  	task1.Config["image"] = "busybox"
   539  	task1.Config["load"] = "busybox.tar"
   540  
   541  	task2, _, _ := dockerTask()
   542  	task2.Config["image"] = "busybox:musl"
   543  	task2.Config["load"] = "busybox_musl.tar"
   544  
   545  	task3, _, _ := dockerTask()
   546  	task3.Config["image"] = "busybox:glibc"
   547  	task3.Config["load"] = "busybox_glibc.tar"
   548  
   549  	taskList := []*structs.Task{task1, task2, task3}
   550  
   551  	handles := make([]DriverHandle, len(taskList))
   552  
   553  	t.Logf("Starting %d tasks", len(taskList))
   554  
   555  	// Let's spin up a bunch of things
   556  	for idx, task := range taskList {
   557  		ctx := testDockerDriverContexts(t, task)
   558  		//ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"}
   559  		defer ctx.AllocDir.Destroy()
   560  		d := NewDockerDriver(ctx.DriverCtx)
   561  		copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar")
   562  		copyImage(t, ctx.ExecCtx.TaskDir, "busybox_musl.tar")
   563  		copyImage(t, ctx.ExecCtx.TaskDir, "busybox_glibc.tar")
   564  
   565  		_, err := d.Prestart(ctx.ExecCtx, task)
   566  		if err != nil {
   567  			t.Fatalf("error in prestart #%d: %v", idx+1, err)
   568  		}
   569  		handles[idx], err = d.Start(ctx.ExecCtx, task)
   570  		if err != nil {
   571  			t.Errorf("Failed starting task #%d: %s", idx+1, err)
   572  		}
   573  	}
   574  
   575  	t.Log("All tasks are started. Terminating...")
   576  
   577  	for idx, handle := range handles {
   578  		if handle == nil {
   579  			t.Errorf("Bad handle for task #%d", idx+1)
   580  			continue
   581  		}
   582  
   583  		err := handle.Kill()
   584  		if err != nil {
   585  			t.Errorf("Failed stopping task #%d: %s", idx+1, err)
   586  		}
   587  	}
   588  
   589  	t.Log("Test complete!")
   590  }
   591  
   592  func waitForExist(t *testing.T, client *docker.Client, handle *DockerHandle) {
   593  	tu.WaitForResult(func() (bool, error) {
   594  		container, err := client.InspectContainer(handle.ContainerID())
   595  		if err != nil {
   596  			if _, ok := err.(*docker.NoSuchContainer); !ok {
   597  				return false, err
   598  			}
   599  		}
   600  
   601  		return container != nil, nil
   602  	}, func(err error) {
   603  		t.Fatalf("err: %v", err)
   604  	})
   605  }
   606  
   607  func TestDockerDriver_NetworkMode_Host(t *testing.T) {
   608  	expected := "host"
   609  
   610  	task := &structs.Task{
   611  		Name:   "nc-demo",
   612  		Driver: "docker",
   613  		Config: map[string]interface{}{
   614  			"image":        "busybox",
   615  			"load":         "busybox.tar",
   616  			"command":      "/bin/nc",
   617  			"args":         []string{"-l", "127.0.0.1", "-p", "0"},
   618  			"network_mode": expected,
   619  		},
   620  		Resources: &structs.Resources{
   621  			MemoryMB: 256,
   622  			CPU:      512,
   623  		},
   624  		LogConfig: &structs.LogConfig{
   625  			MaxFiles:      10,
   626  			MaxFileSizeMB: 10,
   627  		},
   628  	}
   629  
   630  	client, handle, cleanup := dockerSetup(t, task)
   631  	defer cleanup()
   632  
   633  	waitForExist(t, client, handle.(*DockerHandle))
   634  
   635  	container, err := client.InspectContainer(handle.(*DockerHandle).ContainerID())
   636  	if err != nil {
   637  		t.Fatalf("err: %v", err)
   638  	}
   639  
   640  	actual := container.HostConfig.NetworkMode
   641  	if actual != expected {
   642  		t.Fatalf("Got network mode %q; want %q", expected, actual)
   643  	}
   644  }
   645  
   646  func TestDockerDriver_NetworkAliases_Bridge(t *testing.T) {
   647  	// Because go-dockerclient doesn't provide api for query network aliases, just check that
   648  	// a container can be created with a 'network_aliases' property
   649  
   650  	// Create network, network-scoped alias is supported only for containers in user defined networks
   651  	client := newTestDockerClient(t)
   652  	networkOpts := docker.CreateNetworkOptions{Name: "foobar", Driver: "bridge"}
   653  	network, err := client.CreateNetwork(networkOpts)
   654  	if err != nil {
   655  		t.Fatalf("err: %v", err)
   656  	}
   657  	defer client.RemoveNetwork(network.ID)
   658  
   659  	expected := []string{"foobar"}
   660  	task := &structs.Task{
   661  		Name:   "nc-demo",
   662  		Driver: "docker",
   663  		Config: map[string]interface{}{
   664  			"image":           "busybox",
   665  			"load":            "busybox.tar",
   666  			"command":         "/bin/nc",
   667  			"args":            []string{"-l", "127.0.0.1", "-p", "0"},
   668  			"network_mode":    network.Name,
   669  			"network_aliases": expected,
   670  		},
   671  		Resources: &structs.Resources{
   672  			MemoryMB: 256,
   673  			CPU:      512,
   674  		},
   675  		LogConfig: &structs.LogConfig{
   676  			MaxFiles:      10,
   677  			MaxFileSizeMB: 10,
   678  		},
   679  	}
   680  
   681  	client, handle, cleanup := dockerSetupWithClient(t, task, client)
   682  	defer cleanup()
   683  
   684  	waitForExist(t, client, handle.(*DockerHandle))
   685  
   686  	_, err = client.InspectContainer(handle.(*DockerHandle).ContainerID())
   687  	if err != nil {
   688  		t.Fatalf("err: %v", err)
   689  	}
   690  }
   691  
   692  func TestDockerDriver_Labels(t *testing.T) {
   693  	task, _, _ := dockerTask()
   694  	task.Config["labels"] = []map[string]string{
   695  		map[string]string{
   696  			"label1": "value1",
   697  			"label2": "value2",
   698  		},
   699  	}
   700  
   701  	client, handle, cleanup := dockerSetup(t, task)
   702  	defer cleanup()
   703  
   704  	waitForExist(t, client, handle.(*DockerHandle))
   705  
   706  	container, err := client.InspectContainer(handle.(*DockerHandle).ContainerID())
   707  	if err != nil {
   708  		t.Fatalf("err: %v", err)
   709  	}
   710  
   711  	if want, got := 2, len(container.Config.Labels); want != got {
   712  		t.Errorf("Wrong labels count for docker job. Expect: %d, got: %d", want, got)
   713  	}
   714  
   715  	if want, got := "value1", container.Config.Labels["label1"]; want != got {
   716  		t.Errorf("Wrong label value docker job. Expect: %s, got: %s", want, got)
   717  	}
   718  }
   719  
   720  func TestDockerDriver_ForcePull_IsInvalidConfig(t *testing.T) {
   721  	task, _, _ := dockerTask()
   722  	task.Config["force_pull"] = "nothing"
   723  
   724  	ctx := testDockerDriverContexts(t, task)
   725  	defer ctx.AllocDir.Destroy()
   726  	//ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"}
   727  	driver := NewDockerDriver(ctx.DriverCtx)
   728  
   729  	if _, err := driver.Prestart(ctx.ExecCtx, task); err == nil {
   730  		t.Fatalf("error expected in prestart")
   731  	}
   732  }
   733  
   734  func TestDockerDriver_ForcePull(t *testing.T) {
   735  	task, _, _ := dockerTask()
   736  	task.Config["force_pull"] = "true"
   737  
   738  	client, handle, cleanup := dockerSetup(t, task)
   739  	defer cleanup()
   740  
   741  	waitForExist(t, client, handle.(*DockerHandle))
   742  
   743  	_, err := client.InspectContainer(handle.(*DockerHandle).ContainerID())
   744  	if err != nil {
   745  		t.Fatalf("err: %v", err)
   746  	}
   747  }
   748  
   749  func TestDockerDriver_DNS(t *testing.T) {
   750  	task, _, _ := dockerTask()
   751  	task.Config["dns_servers"] = []string{"8.8.8.8", "8.8.4.4"}
   752  	task.Config["dns_search_domains"] = []string{"example.com", "example.org", "example.net"}
   753  
   754  	client, handle, cleanup := dockerSetup(t, task)
   755  	defer cleanup()
   756  
   757  	waitForExist(t, client, handle.(*DockerHandle))
   758  
   759  	container, err := client.InspectContainer(handle.(*DockerHandle).ContainerID())
   760  	if err != nil {
   761  		t.Fatalf("err: %v", err)
   762  	}
   763  
   764  	if !reflect.DeepEqual(task.Config["dns_servers"], container.HostConfig.DNS) {
   765  		t.Errorf("DNS Servers don't match.\nExpected:\n%s\nGot:\n%s\n", task.Config["dns_servers"], container.HostConfig.DNS)
   766  	}
   767  
   768  	if !reflect.DeepEqual(task.Config["dns_search_domains"], container.HostConfig.DNSSearch) {
   769  		t.Errorf("DNS Servers don't match.\nExpected:\n%s\nGot:\n%s\n", task.Config["dns_search_domains"], container.HostConfig.DNSSearch)
   770  	}
   771  }
   772  
   773  func TestDockerWorkDir(t *testing.T) {
   774  	task, _, _ := dockerTask()
   775  	task.Config["work_dir"] = "/some/path"
   776  
   777  	client, handle, cleanup := dockerSetup(t, task)
   778  	defer cleanup()
   779  
   780  	container, err := client.InspectContainer(handle.(*DockerHandle).ContainerID())
   781  	if err != nil {
   782  		t.Fatalf("err: %v", err)
   783  	}
   784  
   785  	if want, got := "/some/path", container.Config.WorkingDir; want != got {
   786  		t.Errorf("Wrong working directory for docker job. Expect: %s, got: %s", want, got)
   787  	}
   788  }
   789  
   790  func inSlice(needle string, haystack []string) bool {
   791  	for _, h := range haystack {
   792  		if h == needle {
   793  			return true
   794  		}
   795  	}
   796  	return false
   797  }
   798  
   799  func TestDockerDriver_PortsNoMap(t *testing.T) {
   800  	task, res, dyn := dockerTask()
   801  
   802  	client, handle, cleanup := dockerSetup(t, task)
   803  	defer cleanup()
   804  
   805  	waitForExist(t, client, handle.(*DockerHandle))
   806  
   807  	container, err := client.InspectContainer(handle.(*DockerHandle).ContainerID())
   808  	if err != nil {
   809  		t.Fatalf("err: %v", err)
   810  	}
   811  
   812  	// Verify that the correct ports are EXPOSED
   813  	expectedExposedPorts := map[docker.Port]struct{}{
   814  		docker.Port(fmt.Sprintf("%d/tcp", res)): struct{}{},
   815  		docker.Port(fmt.Sprintf("%d/udp", res)): struct{}{},
   816  		docker.Port(fmt.Sprintf("%d/tcp", dyn)): struct{}{},
   817  		docker.Port(fmt.Sprintf("%d/udp", dyn)): struct{}{},
   818  	}
   819  
   820  	if !reflect.DeepEqual(container.Config.ExposedPorts, expectedExposedPorts) {
   821  		t.Errorf("Exposed ports don't match.\nExpected:\n%s\nGot:\n%s\n", expectedExposedPorts, container.Config.ExposedPorts)
   822  	}
   823  
   824  	// Verify that the correct ports are FORWARDED
   825  	expectedPortBindings := map[docker.Port][]docker.PortBinding{
   826  		docker.Port(fmt.Sprintf("%d/tcp", res)): []docker.PortBinding{docker.PortBinding{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", res)}},
   827  		docker.Port(fmt.Sprintf("%d/udp", res)): []docker.PortBinding{docker.PortBinding{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", res)}},
   828  		docker.Port(fmt.Sprintf("%d/tcp", dyn)): []docker.PortBinding{docker.PortBinding{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", dyn)}},
   829  		docker.Port(fmt.Sprintf("%d/udp", dyn)): []docker.PortBinding{docker.PortBinding{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", dyn)}},
   830  	}
   831  
   832  	if !reflect.DeepEqual(container.HostConfig.PortBindings, expectedPortBindings) {
   833  		t.Errorf("Forwarded ports don't match.\nExpected:\n%s\nGot:\n%s\n", expectedPortBindings, container.HostConfig.PortBindings)
   834  	}
   835  
   836  	expectedEnvironment := map[string]string{
   837  		"NOMAD_ADDR_main":  fmt.Sprintf("127.0.0.1:%d", res),
   838  		"NOMAD_ADDR_REDIS": fmt.Sprintf("127.0.0.1:%d", dyn),
   839  	}
   840  
   841  	for key, val := range expectedEnvironment {
   842  		search := fmt.Sprintf("%s=%s", key, val)
   843  		if !inSlice(search, container.Config.Env) {
   844  			t.Errorf("Expected to find %s in container environment: %+v", search, container.Config.Env)
   845  		}
   846  	}
   847  }
   848  
   849  func TestDockerDriver_PortsMapping(t *testing.T) {
   850  	task, res, dyn := dockerTask()
   851  	task.Config["port_map"] = []map[string]string{
   852  		map[string]string{
   853  			"main":  "8080",
   854  			"REDIS": "6379",
   855  		},
   856  	}
   857  
   858  	client, handle, cleanup := dockerSetup(t, task)
   859  	defer cleanup()
   860  
   861  	waitForExist(t, client, handle.(*DockerHandle))
   862  
   863  	container, err := client.InspectContainer(handle.(*DockerHandle).ContainerID())
   864  	if err != nil {
   865  		t.Fatalf("err: %v", err)
   866  	}
   867  
   868  	// Verify that the correct ports are EXPOSED
   869  	expectedExposedPorts := map[docker.Port]struct{}{
   870  		docker.Port("8080/tcp"): struct{}{},
   871  		docker.Port("8080/udp"): struct{}{},
   872  		docker.Port("6379/tcp"): struct{}{},
   873  		docker.Port("6379/udp"): struct{}{},
   874  	}
   875  
   876  	if !reflect.DeepEqual(container.Config.ExposedPorts, expectedExposedPorts) {
   877  		t.Errorf("Exposed ports don't match.\nExpected:\n%s\nGot:\n%s\n", expectedExposedPorts, container.Config.ExposedPorts)
   878  	}
   879  
   880  	// Verify that the correct ports are FORWARDED
   881  	expectedPortBindings := map[docker.Port][]docker.PortBinding{
   882  		docker.Port("8080/tcp"): []docker.PortBinding{docker.PortBinding{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", res)}},
   883  		docker.Port("8080/udp"): []docker.PortBinding{docker.PortBinding{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", res)}},
   884  		docker.Port("6379/tcp"): []docker.PortBinding{docker.PortBinding{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", dyn)}},
   885  		docker.Port("6379/udp"): []docker.PortBinding{docker.PortBinding{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", dyn)}},
   886  	}
   887  
   888  	if !reflect.DeepEqual(container.HostConfig.PortBindings, expectedPortBindings) {
   889  		t.Errorf("Forwarded ports don't match.\nExpected:\n%s\nGot:\n%s\n", expectedPortBindings, container.HostConfig.PortBindings)
   890  	}
   891  
   892  	expectedEnvironment := map[string]string{
   893  		"NOMAD_ADDR_main":      "127.0.0.1:8080",
   894  		"NOMAD_ADDR_REDIS":     "127.0.0.1:6379",
   895  		"NOMAD_HOST_PORT_main": strconv.Itoa(docker_reserved),
   896  	}
   897  
   898  	for key, val := range expectedEnvironment {
   899  		search := fmt.Sprintf("%s=%s", key, val)
   900  		if !inSlice(search, container.Config.Env) {
   901  			t.Errorf("Expected to find %s in container environment: %+v", search, container.Config.Env)
   902  		}
   903  	}
   904  }
   905  
   906  func TestDockerDriver_User(t *testing.T) {
   907  	task := &structs.Task{
   908  		Name:   "redis-demo",
   909  		User:   "alice",
   910  		Driver: "docker",
   911  		Config: map[string]interface{}{
   912  			"image":   "busybox",
   913  			"load":    "busybox.tar",
   914  			"command": "/bin/sleep",
   915  			"args":    []string{"10000"},
   916  		},
   917  		Resources: &structs.Resources{
   918  			MemoryMB: 256,
   919  			CPU:      512,
   920  		},
   921  		LogConfig: &structs.LogConfig{
   922  			MaxFiles:      10,
   923  			MaxFileSizeMB: 10,
   924  		},
   925  	}
   926  
   927  	if !testutil.DockerIsConnected(t) {
   928  		t.SkipNow()
   929  	}
   930  
   931  	ctx := testDockerDriverContexts(t, task)
   932  	//ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"}
   933  	driver := NewDockerDriver(ctx.DriverCtx)
   934  	defer ctx.AllocDir.Destroy()
   935  	copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar")
   936  
   937  	_, err := driver.Prestart(ctx.ExecCtx, task)
   938  	if err != nil {
   939  		t.Fatalf("error in prestart: %v", err)
   940  	}
   941  
   942  	// It should fail because the user "alice" does not exist on the given
   943  	// image.
   944  	handle, err := driver.Start(ctx.ExecCtx, task)
   945  	if err == nil {
   946  		handle.Kill()
   947  		t.Fatalf("Should've failed")
   948  	}
   949  
   950  	if !strings.Contains(err.Error(), "alice") {
   951  		t.Fatalf("Expected failure string not found, found %q instead", err.Error())
   952  	}
   953  }
   954  
   955  func TestDockerDriver_CleanupContainer(t *testing.T) {
   956  	task := &structs.Task{
   957  		Name:   "redis-demo",
   958  		Driver: "docker",
   959  		Config: map[string]interface{}{
   960  			"image":   "busybox",
   961  			"load":    "busybox.tar",
   962  			"command": "/bin/echo",
   963  			"args":    []string{"hello"},
   964  		},
   965  		Resources: &structs.Resources{
   966  			MemoryMB: 256,
   967  			CPU:      512,
   968  		},
   969  		LogConfig: &structs.LogConfig{
   970  			MaxFiles:      10,
   971  			MaxFileSizeMB: 10,
   972  		},
   973  	}
   974  
   975  	_, handle, cleanup := dockerSetup(t, task)
   976  	defer cleanup()
   977  
   978  	// Update should be a no-op
   979  	err := handle.Update(task)
   980  	if err != nil {
   981  		t.Fatalf("err: %v", err)
   982  	}
   983  
   984  	select {
   985  	case res := <-handle.WaitCh():
   986  		if !res.Successful() {
   987  			t.Fatalf("err: %v", res)
   988  		}
   989  
   990  		time.Sleep(3 * time.Second)
   991  
   992  		// Ensure that the container isn't present
   993  		_, err := client.InspectContainer(handle.(*DockerHandle).containerID)
   994  		if err == nil {
   995  			t.Fatalf("expected to not get container")
   996  		}
   997  
   998  	case <-time.After(time.Duration(tu.TestMultiplier()*5) * time.Second):
   999  		t.Fatalf("timeout")
  1000  	}
  1001  }
  1002  
  1003  func TestDockerDriver_Stats(t *testing.T) {
  1004  	task := &structs.Task{
  1005  		Name:   "sleep",
  1006  		Driver: "docker",
  1007  		Config: map[string]interface{}{
  1008  			"image":   "busybox",
  1009  			"load":    "busybox.tar",
  1010  			"command": "/bin/sleep",
  1011  			"args":    []string{"100"},
  1012  		},
  1013  		LogConfig: &structs.LogConfig{
  1014  			MaxFiles:      10,
  1015  			MaxFileSizeMB: 10,
  1016  		},
  1017  		Resources: basicResources,
  1018  	}
  1019  
  1020  	_, handle, cleanup := dockerSetup(t, task)
  1021  	defer cleanup()
  1022  
  1023  	waitForExist(t, client, handle.(*DockerHandle))
  1024  
  1025  	go func() {
  1026  		time.Sleep(3 * time.Second)
  1027  		ru, err := handle.Stats()
  1028  		if err != nil {
  1029  			t.Fatalf("err: %v", err)
  1030  		}
  1031  		if ru.ResourceUsage == nil {
  1032  			handle.Kill()
  1033  			t.Fatalf("expected resource usage")
  1034  		}
  1035  		err = handle.Kill()
  1036  		if err != nil {
  1037  			t.Fatalf("err: %v", err)
  1038  		}
  1039  	}()
  1040  
  1041  	select {
  1042  	case res := <-handle.WaitCh():
  1043  		if res.Successful() {
  1044  			t.Fatalf("should err: %v", res)
  1045  		}
  1046  	case <-time.After(time.Duration(tu.TestMultiplier()*10) * time.Second):
  1047  		t.Fatalf("timeout")
  1048  	}
  1049  }
  1050  
  1051  func TestDockerDriver_Signal(t *testing.T) {
  1052  	if !testutil.DockerIsConnected(t) {
  1053  		t.SkipNow()
  1054  	}
  1055  
  1056  	task := &structs.Task{
  1057  		Name:   "redis-demo",
  1058  		Driver: "docker",
  1059  		Config: map[string]interface{}{
  1060  			"image":   "busybox",
  1061  			"load":    "busybox.tar",
  1062  			"command": "/bin/sh",
  1063  			"args":    []string{"local/test.sh"},
  1064  		},
  1065  		Resources: &structs.Resources{
  1066  			MemoryMB: 256,
  1067  			CPU:      512,
  1068  		},
  1069  		LogConfig: &structs.LogConfig{
  1070  			MaxFiles:      10,
  1071  			MaxFileSizeMB: 10,
  1072  		},
  1073  	}
  1074  
  1075  	ctx := testDockerDriverContexts(t, task)
  1076  	//ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"}
  1077  	defer ctx.AllocDir.Destroy()
  1078  	d := NewDockerDriver(ctx.DriverCtx)
  1079  
  1080  	// Copy the image into the task's directory
  1081  	copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar")
  1082  
  1083  	testFile := filepath.Join(ctx.ExecCtx.TaskDir.LocalDir, "test.sh")
  1084  	testData := []byte(`
  1085  at_term() {
  1086      echo 'Terminated.'
  1087      exit 3
  1088  }
  1089  trap at_term USR1
  1090  while true; do
  1091      sleep 1
  1092  done
  1093  	`)
  1094  	if err := ioutil.WriteFile(testFile, testData, 0777); err != nil {
  1095  		t.Fatalf("Failed to write data: %v", err)
  1096  	}
  1097  
  1098  	_, err := d.Prestart(ctx.ExecCtx, task)
  1099  	if err != nil {
  1100  		t.Fatalf("error in prestart: %v", err)
  1101  	}
  1102  	handle, err := d.Start(ctx.ExecCtx, task)
  1103  	if err != nil {
  1104  		t.Fatalf("err: %v", err)
  1105  	}
  1106  	if handle == nil {
  1107  		t.Fatalf("missing handle")
  1108  	}
  1109  	defer handle.Kill()
  1110  
  1111  	waitForExist(t, handle.(*DockerHandle).client, handle.(*DockerHandle))
  1112  
  1113  	time.Sleep(1 * time.Second)
  1114  	if err := handle.Signal(syscall.SIGUSR1); err != nil {
  1115  		t.Fatalf("Signal returned an error: %v", err)
  1116  	}
  1117  
  1118  	select {
  1119  	case res := <-handle.WaitCh():
  1120  		if res.Successful() {
  1121  			t.Fatalf("should err: %v", res)
  1122  		}
  1123  	case <-time.After(time.Duration(tu.TestMultiplier()*5) * time.Second):
  1124  		t.Fatalf("timeout")
  1125  	}
  1126  
  1127  	// Check the log file to see it exited because of the signal
  1128  	outputFile := filepath.Join(ctx.ExecCtx.TaskDir.LogDir, "redis-demo.stdout.0")
  1129  	act, err := ioutil.ReadFile(outputFile)
  1130  	if err != nil {
  1131  		t.Fatalf("Couldn't read expected output: %v", err)
  1132  	}
  1133  
  1134  	exp := "Terminated."
  1135  	if strings.TrimSpace(string(act)) != exp {
  1136  		t.Fatalf("Command outputted %v; want %v", act, exp)
  1137  	}
  1138  }
  1139  
  1140  func setupDockerVolumes(t *testing.T, cfg *config.Config, hostpath string) (*structs.Task, Driver, *ExecContext, string, func()) {
  1141  	if !testutil.DockerIsConnected(t) {
  1142  		t.SkipNow()
  1143  	}
  1144  
  1145  	randfn := fmt.Sprintf("test-%d", rand.Int())
  1146  	hostfile := filepath.Join(hostpath, randfn)
  1147  	containerPath := "/mnt/vol"
  1148  	containerFile := filepath.Join(containerPath, randfn)
  1149  
  1150  	task := &structs.Task{
  1151  		Name:   "ls",
  1152  		Env:    map[string]string{"VOL_PATH": containerPath},
  1153  		Driver: "docker",
  1154  		Config: map[string]interface{}{
  1155  			"image":   "busybox",
  1156  			"load":    "busybox.tar",
  1157  			"command": "touch",
  1158  			"args":    []string{containerFile},
  1159  			"volumes": []string{fmt.Sprintf("%s:${VOL_PATH}", hostpath)},
  1160  		},
  1161  		LogConfig: &structs.LogConfig{
  1162  			MaxFiles:      10,
  1163  			MaxFileSizeMB: 10,
  1164  		},
  1165  		Resources: basicResources,
  1166  	}
  1167  
  1168  	// Build alloc and task directory structure
  1169  	allocDir := allocdir.NewAllocDir(testLogger(), filepath.Join(cfg.AllocDir, structs.GenerateUUID()))
  1170  	if err := allocDir.Build(); err != nil {
  1171  		t.Fatalf("failed to build alloc dir: %v", err)
  1172  	}
  1173  	taskDir := allocDir.NewTaskDir(task.Name)
  1174  	if err := taskDir.Build(false, nil, cstructs.FSIsolationImage); err != nil {
  1175  		allocDir.Destroy()
  1176  		t.Fatalf("failed to build task dir: %v", err)
  1177  	}
  1178  
  1179  	alloc := mock.Alloc()
  1180  	execCtx := NewExecContext(taskDir)
  1181  	cleanup := func() {
  1182  		allocDir.Destroy()
  1183  		if filepath.IsAbs(hostpath) {
  1184  			os.RemoveAll(hostpath)
  1185  		}
  1186  	}
  1187  
  1188  	taskEnv, err := GetTaskEnv(taskDir, cfg.Node, task, alloc, cfg, "")
  1189  	if err != nil {
  1190  		cleanup()
  1191  		t.Fatalf("Failed to get task env: %v", err)
  1192  	}
  1193  
  1194  	logger := testLogger()
  1195  	emitter := func(m string, args ...interface{}) {
  1196  		logger.Printf("[EVENT] "+m, args...)
  1197  	}
  1198  	driverCtx := NewDriverContext(task.Name, alloc.ID, cfg, cfg.Node, testLogger(), taskEnv, emitter)
  1199  	driver := NewDockerDriver(driverCtx)
  1200  	copyImage(t, taskDir, "busybox.tar")
  1201  
  1202  	return task, driver, execCtx, hostfile, cleanup
  1203  }
  1204  
  1205  func TestDockerDriver_VolumesDisabled(t *testing.T) {
  1206  	cfg := testConfig()
  1207  	cfg.Options = map[string]string{
  1208  		dockerVolumesConfigOption: "false",
  1209  		"docker.cleanup.image":    "false",
  1210  	}
  1211  
  1212  	{
  1213  		tmpvol, err := ioutil.TempDir("", "nomadtest_docker_volumesdisabled")
  1214  		if err != nil {
  1215  			t.Fatalf("error creating temporary dir: %v", err)
  1216  		}
  1217  
  1218  		task, driver, execCtx, _, cleanup := setupDockerVolumes(t, cfg, tmpvol)
  1219  		defer cleanup()
  1220  
  1221  		_, err = driver.Prestart(execCtx, task)
  1222  		if err != nil {
  1223  			t.Fatalf("error in prestart: %v", err)
  1224  		}
  1225  		if _, err := driver.Start(execCtx, task); err == nil {
  1226  			t.Fatalf("Started driver successfully when volumes should have been disabled.")
  1227  		}
  1228  	}
  1229  
  1230  	// Relative paths should still be allowed
  1231  	{
  1232  		task, driver, execCtx, fn, cleanup := setupDockerVolumes(t, cfg, ".")
  1233  		defer cleanup()
  1234  
  1235  		_, err := driver.Prestart(execCtx, task)
  1236  		if err != nil {
  1237  			t.Fatalf("error in prestart: %v", err)
  1238  		}
  1239  		handle, err := driver.Start(execCtx, task)
  1240  		if err != nil {
  1241  			t.Fatalf("err: %v", err)
  1242  		}
  1243  		defer handle.Kill()
  1244  
  1245  		select {
  1246  		case res := <-handle.WaitCh():
  1247  			if !res.Successful() {
  1248  				t.Fatalf("unexpected err: %v", res)
  1249  			}
  1250  		case <-time.After(time.Duration(tu.TestMultiplier()*10) * time.Second):
  1251  			t.Fatalf("timeout")
  1252  		}
  1253  
  1254  		if _, err := ioutil.ReadFile(filepath.Join(execCtx.TaskDir.Dir, fn)); err != nil {
  1255  			t.Fatalf("unexpected error reading %s: %v", fn, err)
  1256  		}
  1257  	}
  1258  
  1259  	// Volume Drivers should be rejected (error)
  1260  	{
  1261  		task, driver, execCtx, _, cleanup := setupDockerVolumes(t, cfg, "fake_flocker_vol")
  1262  		defer cleanup()
  1263  		task.Config["volume_driver"] = "flocker"
  1264  
  1265  		if _, err := driver.Prestart(execCtx, task); err != nil {
  1266  			t.Fatalf("error in prestart: %v", err)
  1267  		}
  1268  		if _, err := driver.Start(execCtx, task); err == nil {
  1269  			t.Fatalf("Started driver successfully when volume drivers should have been disabled.")
  1270  		}
  1271  	}
  1272  
  1273  }
  1274  
  1275  func TestDockerDriver_VolumesEnabled(t *testing.T) {
  1276  	cfg := testConfig()
  1277  
  1278  	tmpvol, err := ioutil.TempDir("", "nomadtest_docker_volumesenabled")
  1279  	if err != nil {
  1280  		t.Fatalf("error creating temporary dir: %v", err)
  1281  	}
  1282  
  1283  	task, driver, execCtx, hostpath, cleanup := setupDockerVolumes(t, cfg, tmpvol)
  1284  	defer cleanup()
  1285  
  1286  	_, err = driver.Prestart(execCtx, task)
  1287  	if err != nil {
  1288  		t.Fatalf("error in prestart: %v", err)
  1289  	}
  1290  	handle, err := driver.Start(execCtx, task)
  1291  	if err != nil {
  1292  		t.Fatalf("Failed to start docker driver: %v", err)
  1293  	}
  1294  	defer handle.Kill()
  1295  
  1296  	select {
  1297  	case res := <-handle.WaitCh():
  1298  		if !res.Successful() {
  1299  			t.Fatalf("unexpected err: %v", res)
  1300  		}
  1301  	case <-time.After(time.Duration(tu.TestMultiplier()*10) * time.Second):
  1302  		t.Fatalf("timeout")
  1303  	}
  1304  
  1305  	if _, err := ioutil.ReadFile(hostpath); err != nil {
  1306  		t.Fatalf("unexpected error reading %s: %v", hostpath, err)
  1307  	}
  1308  }
  1309  
  1310  // TestDockerDriver_Cleanup ensures Cleanup removes only downloaded images.
  1311  func TestDockerDriver_Cleanup(t *testing.T) {
  1312  	if !testutil.DockerIsConnected(t) {
  1313  		t.SkipNow()
  1314  	}
  1315  
  1316  	imageName := "hello-world:latest"
  1317  	task := &structs.Task{
  1318  		Name:   "cleanup_test",
  1319  		Driver: "docker",
  1320  		Config: map[string]interface{}{
  1321  			"image": imageName,
  1322  		},
  1323  	}
  1324  	tctx := testDockerDriverContexts(t, task)
  1325  	defer tctx.AllocDir.Destroy()
  1326  
  1327  	// Run Prestart
  1328  	driver := NewDockerDriver(tctx.DriverCtx).(*DockerDriver)
  1329  	res, err := driver.Prestart(tctx.ExecCtx, task)
  1330  	if err != nil {
  1331  		t.Fatalf("error in prestart: %v", err)
  1332  	}
  1333  	if len(res.Resources) == 0 || len(res.Resources[dockerImageResKey]) == 0 {
  1334  		t.Fatalf("no created resources: %#v", res)
  1335  	}
  1336  
  1337  	// Cleanup
  1338  	rescopy := res.Copy()
  1339  	if err := driver.Cleanup(tctx.ExecCtx, rescopy); err != nil {
  1340  		t.Fatalf("Cleanup failed: %v", err)
  1341  	}
  1342  
  1343  	// Make sure rescopy is updated
  1344  	if len(rescopy.Resources) > 0 {
  1345  		t.Errorf("Cleanup should have cleared resource map: %#v", rescopy.Resources)
  1346  	}
  1347  
  1348  	// Ensure image was removed
  1349  	tu.WaitForResult(func() (bool, error) {
  1350  		if _, err := client.InspectImage(driver.driverConfig.ImageName); err == nil {
  1351  			return false, fmt.Errorf("image exists but should have been removed. Does another %v container exist?", imageName)
  1352  		}
  1353  
  1354  		return true, nil
  1355  	}, func(err error) {
  1356  		t.Fatalf("err: %v", err)
  1357  	})
  1358  
  1359  	// The image doesn't exist which shouldn't be an error when calling
  1360  	// Cleanup, so call it again to make sure.
  1361  	if err := driver.Cleanup(tctx.ExecCtx, res.Copy()); err != nil {
  1362  		t.Fatalf("Cleanup failed: %v", err)
  1363  	}
  1364  }
  1365  
  1366  func copyImage(t *testing.T, taskDir *allocdir.TaskDir, image string) {
  1367  	dst := filepath.Join(taskDir.LocalDir, image)
  1368  	copyFile(filepath.Join("./test-resources/docker", image), dst, t)
  1369  }
  1370  
  1371  func TestDockerDriver_AuthConfiguration(t *testing.T) {
  1372  	path := "./test-resources/docker/auth.json"
  1373  	cases := []struct {
  1374  		Repo       string
  1375  		AuthConfig *docker.AuthConfiguration
  1376  	}{
  1377  		{
  1378  			Repo:       "lolwhat.com/what:1337",
  1379  			AuthConfig: &docker.AuthConfiguration{},
  1380  		},
  1381  		{
  1382  			Repo: "redis:3.2",
  1383  			AuthConfig: &docker.AuthConfiguration{
  1384  				Username:      "test",
  1385  				Password:      "1234",
  1386  				Email:         "",
  1387  				ServerAddress: "https://index.docker.io/v1/",
  1388  			},
  1389  		},
  1390  		{
  1391  			Repo: "quay.io/redis:3.2",
  1392  			AuthConfig: &docker.AuthConfiguration{
  1393  				Username:      "test",
  1394  				Password:      "5678",
  1395  				Email:         "",
  1396  				ServerAddress: "quay.io",
  1397  			},
  1398  		},
  1399  		{
  1400  			Repo: "other.io/redis:3.2",
  1401  			AuthConfig: &docker.AuthConfiguration{
  1402  				Username:      "test",
  1403  				Password:      "abcd",
  1404  				Email:         "",
  1405  				ServerAddress: "https://other.io/v1/",
  1406  			},
  1407  		},
  1408  	}
  1409  
  1410  	for i, c := range cases {
  1411  		act, err := authOptionFrom(path, c.Repo)
  1412  		if err != nil {
  1413  			t.Fatalf("Test %d failed: %v", i+1, err)
  1414  		}
  1415  
  1416  		if !reflect.DeepEqual(act, c.AuthConfig) {
  1417  			t.Fatalf("Test %d failed: Unexpected auth config: got %+v; want %+v", i+1, act, c.AuthConfig)
  1418  		}
  1419  	}
  1420  }