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