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