github.com/emate/nomad@v0.8.2-wo-binpacking/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"
    11  	"runtime/debug"
    12  	"sort"
    13  	"strconv"
    14  	"strings"
    15  	"testing"
    16  	"time"
    17  
    18  	docker "github.com/fsouza/go-dockerclient"
    19  	"github.com/hashicorp/consul/lib/freeport"
    20  	sockaddr "github.com/hashicorp/go-sockaddr"
    21  	"github.com/hashicorp/nomad/client/allocdir"
    22  	"github.com/hashicorp/nomad/client/config"
    23  	"github.com/hashicorp/nomad/client/driver/env"
    24  	"github.com/hashicorp/nomad/client/fingerprint"
    25  	cstructs "github.com/hashicorp/nomad/client/structs"
    26  	"github.com/hashicorp/nomad/client/testutil"
    27  	"github.com/hashicorp/nomad/helper/uuid"
    28  	"github.com/hashicorp/nomad/nomad/mock"
    29  	"github.com/hashicorp/nomad/nomad/structs"
    30  	tu "github.com/hashicorp/nomad/testutil"
    31  	"github.com/stretchr/testify/assert"
    32  	"github.com/stretchr/testify/require"
    33  )
    34  
    35  func dockerIsRemote(t *testing.T) bool {
    36  	client, err := docker.NewClientFromEnv()
    37  	if err != nil {
    38  		return false
    39  	}
    40  
    41  	// Technically this could be a local tcp socket but for testing purposes
    42  	// we'll just assume that tcp is only used for remote connections.
    43  	if client.Endpoint()[0:3] == "tcp" {
    44  		return true
    45  	}
    46  	return false
    47  }
    48  
    49  // Returns a task with a reserved and dynamic port. The ports are returned
    50  // respectively.
    51  func dockerTask(t *testing.T) (*structs.Task, int, int) {
    52  	ports := freeport.GetT(t, 2)
    53  	dockerReserved := ports[0]
    54  	dockerDynamic := ports[1]
    55  	return &structs.Task{
    56  		Name:   "redis-demo",
    57  		Driver: "docker",
    58  		Config: map[string]interface{}{
    59  			"image":   "busybox",
    60  			"load":    "busybox.tar",
    61  			"command": "/bin/nc",
    62  			"args":    []string{"-l", "127.0.0.1", "-p", "0"},
    63  		},
    64  		LogConfig: &structs.LogConfig{
    65  			MaxFiles:      10,
    66  			MaxFileSizeMB: 10,
    67  		},
    68  		Resources: &structs.Resources{
    69  			MemoryMB: 256,
    70  			CPU:      512,
    71  			Networks: []*structs.NetworkResource{
    72  				{
    73  					IP:            "127.0.0.1",
    74  					ReservedPorts: []structs.Port{{Label: "main", Value: dockerReserved}},
    75  					DynamicPorts:  []structs.Port{{Label: "REDIS", Value: dockerDynamic}},
    76  				},
    77  			},
    78  		},
    79  	}, dockerReserved, dockerDynamic
    80  }
    81  
    82  // dockerSetup does all of the basic setup you need to get a running docker
    83  // process up and running for testing. Use like:
    84  //
    85  //	task := taskTemplate()
    86  //	// do custom task configuration
    87  //	client, handle, cleanup := dockerSetup(t, task)
    88  //	defer cleanup()
    89  //	// do test stuff
    90  //
    91  // If there is a problem during setup this function will abort or skip the test
    92  // and indicate the reason.
    93  func dockerSetup(t *testing.T, task *structs.Task) (*docker.Client, *DockerHandle, func()) {
    94  	client := newTestDockerClient(t)
    95  	return dockerSetupWithClient(t, task, client)
    96  }
    97  
    98  func testDockerDriverContexts(t *testing.T, task *structs.Task) *testContext {
    99  	tctx := testDriverContexts(t, task)
   100  
   101  	// Drop the delay
   102  	tctx.DriverCtx.config.Options = make(map[string]string)
   103  	tctx.DriverCtx.config.Options[dockerImageRemoveDelayConfigOption] = "1s"
   104  
   105  	return tctx
   106  }
   107  
   108  func dockerSetupWithClient(t *testing.T, task *structs.Task, client *docker.Client) (*docker.Client, *DockerHandle, func()) {
   109  	t.Helper()
   110  	tctx := testDockerDriverContexts(t, task)
   111  	driver := NewDockerDriver(tctx.DriverCtx)
   112  	copyImage(t, tctx.ExecCtx.TaskDir, "busybox.tar")
   113  
   114  	presp, err := driver.Prestart(tctx.ExecCtx, task)
   115  	if err != nil {
   116  		if presp != nil && presp.CreatedResources != nil {
   117  			driver.Cleanup(tctx.ExecCtx, presp.CreatedResources)
   118  		}
   119  		tctx.AllocDir.Destroy()
   120  		t.Fatalf("error in prestart: %v", err)
   121  	}
   122  	// Update the exec ctx with the driver network env vars
   123  	tctx.ExecCtx.TaskEnv = tctx.EnvBuilder.SetDriverNetwork(presp.Network).Build()
   124  
   125  	sresp, err := driver.Start(tctx.ExecCtx, task)
   126  	if err != nil {
   127  		driver.Cleanup(tctx.ExecCtx, presp.CreatedResources)
   128  		tctx.AllocDir.Destroy()
   129  		t.Fatalf("Failed to start driver: %s\nStack\n%s", err, debug.Stack())
   130  	}
   131  
   132  	if sresp.Handle == nil {
   133  		driver.Cleanup(tctx.ExecCtx, presp.CreatedResources)
   134  		tctx.AllocDir.Destroy()
   135  		t.Fatalf("handle is nil\nStack\n%s", debug.Stack())
   136  	}
   137  
   138  	// At runtime this is handled by TaskRunner
   139  	tctx.ExecCtx.TaskEnv = tctx.EnvBuilder.SetDriverNetwork(sresp.Network).Build()
   140  
   141  	cleanup := func() {
   142  		driver.Cleanup(tctx.ExecCtx, presp.CreatedResources)
   143  		sresp.Handle.Kill()
   144  		tctx.AllocDir.Destroy()
   145  	}
   146  
   147  	return client, sresp.Handle.(*DockerHandle), cleanup
   148  }
   149  
   150  func newTestDockerClient(t *testing.T) *docker.Client {
   151  	t.Helper()
   152  	if !testutil.DockerIsConnected(t) {
   153  		t.Skip("Docker not connected")
   154  	}
   155  
   156  	client, err := docker.NewClientFromEnv()
   157  	if err != nil {
   158  		t.Fatalf("Failed to initialize client: %s\nStack\n%s", err, debug.Stack())
   159  	}
   160  	return client
   161  }
   162  
   163  // This test should always pass, even if docker daemon is not available
   164  func TestDockerDriver_Fingerprint(t *testing.T) {
   165  	if !tu.IsTravis() {
   166  		t.Parallel()
   167  	}
   168  
   169  	ctx := testDockerDriverContexts(t, &structs.Task{Name: "foo", Driver: "docker", Resources: basicResources})
   170  	//ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"}
   171  	defer ctx.AllocDir.Destroy()
   172  	d := NewDockerDriver(ctx.DriverCtx)
   173  	node := &structs.Node{
   174  		Attributes: make(map[string]string),
   175  	}
   176  
   177  	request := &cstructs.FingerprintRequest{Config: &config.Config{}, Node: node}
   178  	var response cstructs.FingerprintResponse
   179  	err := d.Fingerprint(request, &response)
   180  	if err != nil {
   181  		t.Fatalf("err: %v", err)
   182  	}
   183  
   184  	attributes := response.Attributes
   185  	if testutil.DockerIsConnected(t) && attributes["driver.docker"] == "" {
   186  		t.Fatalf("Fingerprinter should detect when docker is available")
   187  	}
   188  
   189  	if attributes["driver.docker"] != "1" {
   190  		t.Log("Docker daemon not available. The remainder of the docker tests will be skipped.")
   191  	} else {
   192  
   193  		// if docker is available, make sure that the response is tagged as
   194  		// applicable
   195  		if !response.Detected {
   196  			t.Fatalf("expected response to be applicable")
   197  		}
   198  	}
   199  
   200  	t.Logf("Found docker version %s", attributes["driver.docker.version"])
   201  }
   202  
   203  // TestDockerDriver_Fingerprint_Bridge asserts that if Docker is running we set
   204  // the bridge network's IP as a node attribute. See #2785
   205  func TestDockerDriver_Fingerprint_Bridge(t *testing.T) {
   206  	if !tu.IsTravis() {
   207  		t.Parallel()
   208  	}
   209  	if !testutil.DockerIsConnected(t) {
   210  		t.Skip("requires Docker")
   211  	}
   212  	if runtime.GOOS != "linux" {
   213  		t.Skip("expect only on linux")
   214  	}
   215  
   216  	// This seems fragile, so we might need to reconsider this test if it
   217  	// proves flaky
   218  	expectedAddr, err := sockaddr.GetInterfaceIP("docker0")
   219  	if err != nil {
   220  		t.Fatalf("unable to get ip for docker0: %v", err)
   221  	}
   222  	if expectedAddr == "" {
   223  		t.Fatalf("unable to get ip for docker bridge")
   224  	}
   225  
   226  	conf := testConfig(t)
   227  	conf.Node = mock.Node()
   228  	dd := NewDockerDriver(NewDriverContext("", "", "", "", conf, conf.Node, testLogger(), nil))
   229  
   230  	request := &cstructs.FingerprintRequest{Config: conf, Node: conf.Node}
   231  	var response cstructs.FingerprintResponse
   232  
   233  	err = dd.Fingerprint(request, &response)
   234  	if err != nil {
   235  		t.Fatalf("error fingerprinting docker: %v", err)
   236  	}
   237  
   238  	if !response.Detected {
   239  		t.Fatalf("expected response to be applicable")
   240  	}
   241  
   242  	attributes := response.Attributes
   243  	if attributes == nil {
   244  		t.Fatalf("expected attributes to be set")
   245  	}
   246  
   247  	if attributes["driver.docker"] == "" {
   248  		t.Fatalf("expected Docker to be enabled but false was returned")
   249  	}
   250  
   251  	if found := attributes["driver.docker.bridge_ip"]; found != expectedAddr {
   252  		t.Fatalf("expected bridge ip %q but found: %q", expectedAddr, found)
   253  	}
   254  	t.Logf("docker bridge ip: %q", attributes["driver.docker.bridge_ip"])
   255  }
   256  
   257  func TestDockerDriver_Check_DockerHealthStatus(t *testing.T) {
   258  	if !tu.IsTravis() {
   259  		t.Parallel()
   260  	}
   261  	if !testutil.DockerIsConnected(t) {
   262  		t.Skip("requires Docker")
   263  	}
   264  	if runtime.GOOS != "linux" {
   265  		t.Skip("expect only on linux")
   266  	}
   267  
   268  	require := require.New(t)
   269  
   270  	expectedAddr, err := sockaddr.GetInterfaceIP("docker0")
   271  	if err != nil {
   272  		t.Fatalf("unable to get ip for docker0: %v", err)
   273  	}
   274  	if expectedAddr == "" {
   275  		t.Fatalf("unable to get ip for docker bridge")
   276  	}
   277  
   278  	conf := testConfig(t)
   279  	conf.Node = mock.Node()
   280  	dd := NewDockerDriver(NewDriverContext("", "", "", "", conf, conf.Node, testLogger(), nil))
   281  
   282  	request := &cstructs.HealthCheckRequest{}
   283  	var response cstructs.HealthCheckResponse
   284  
   285  	dc, ok := dd.(fingerprint.HealthCheck)
   286  	require.True(ok)
   287  	err = dc.HealthCheck(request, &response)
   288  	require.Nil(err)
   289  
   290  	driverInfo := response.Drivers["docker"]
   291  	require.NotNil(driverInfo)
   292  	require.True(driverInfo.Healthy)
   293  }
   294  
   295  func TestDockerDriver_StartOpen_Wait(t *testing.T) {
   296  	if !tu.IsTravis() {
   297  		t.Parallel()
   298  	}
   299  	if !testutil.DockerIsConnected(t) {
   300  		t.Skip("Docker not connected")
   301  	}
   302  
   303  	task := &structs.Task{
   304  		Name:   "nc-demo",
   305  		Driver: "docker",
   306  		Config: map[string]interface{}{
   307  			"load":    "busybox.tar",
   308  			"image":   "busybox",
   309  			"command": "/bin/nc",
   310  			"args":    []string{"-l", "127.0.0.1", "-p", "0"},
   311  		},
   312  		LogConfig: &structs.LogConfig{
   313  			MaxFiles:      10,
   314  			MaxFileSizeMB: 10,
   315  		},
   316  		Resources: basicResources,
   317  	}
   318  
   319  	ctx := testDockerDriverContexts(t, task)
   320  	//ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"}
   321  	defer ctx.AllocDir.Destroy()
   322  	d := NewDockerDriver(ctx.DriverCtx)
   323  	copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar")
   324  
   325  	_, err := d.Prestart(ctx.ExecCtx, task)
   326  	if err != nil {
   327  		t.Fatalf("error in prestart: %v", err)
   328  	}
   329  
   330  	resp, err := d.Start(ctx.ExecCtx, task)
   331  	if err != nil {
   332  		t.Fatalf("err: %v", err)
   333  	}
   334  	if resp.Handle == nil {
   335  		t.Fatalf("missing handle")
   336  	}
   337  	defer resp.Handle.Kill()
   338  
   339  	// Attempt to open
   340  	resp2, err := d.Open(ctx.ExecCtx, resp.Handle.ID())
   341  	if err != nil {
   342  		t.Fatalf("err: %v", err)
   343  	}
   344  	if resp2 == nil {
   345  		t.Fatalf("missing handle")
   346  	}
   347  }
   348  
   349  func TestDockerDriver_Start_Wait(t *testing.T) {
   350  	if !tu.IsTravis() {
   351  		t.Parallel()
   352  	}
   353  	if !testutil.DockerIsConnected(t) {
   354  		t.Skip("Docker not connected")
   355  	}
   356  	task := &structs.Task{
   357  		Name:   "nc-demo",
   358  		Driver: "docker",
   359  		Config: map[string]interface{}{
   360  			"load":    "busybox.tar",
   361  			"image":   "busybox",
   362  			"command": "/bin/echo",
   363  			"args":    []string{"hello"},
   364  		},
   365  		Resources: &structs.Resources{
   366  			MemoryMB: 256,
   367  			CPU:      512,
   368  		},
   369  		LogConfig: &structs.LogConfig{
   370  			MaxFiles:      10,
   371  			MaxFileSizeMB: 10,
   372  		},
   373  	}
   374  
   375  	_, handle, cleanup := dockerSetup(t, task)
   376  	defer cleanup()
   377  
   378  	// Update should be a no-op
   379  	err := handle.Update(task)
   380  	if err != nil {
   381  		t.Fatalf("err: %v", err)
   382  	}
   383  
   384  	select {
   385  	case res := <-handle.WaitCh():
   386  		if !res.Successful() {
   387  			t.Fatalf("err: %v", res)
   388  		}
   389  	case <-time.After(time.Duration(tu.TestMultiplier()*5) * time.Second):
   390  		t.Fatalf("timeout")
   391  	}
   392  }
   393  
   394  // TestDockerDriver_Start_StoppedContainer asserts that Nomad will detect a
   395  // stopped task container, remove it, and start a new container.
   396  //
   397  // See https://github.com/hashicorp/nomad/issues/3419
   398  func TestDockerDriver_Start_StoppedContainer(t *testing.T) {
   399  	if !tu.IsTravis() {
   400  		t.Parallel()
   401  	}
   402  	if !testutil.DockerIsConnected(t) {
   403  		t.Skip("Docker not connected")
   404  	}
   405  	task := &structs.Task{
   406  		Name:   "nc-demo",
   407  		Driver: "docker",
   408  		Config: map[string]interface{}{
   409  			"load":    "busybox.tar",
   410  			"image":   "busybox",
   411  			"command": "sleep",
   412  			"args":    []string{"9000"},
   413  		},
   414  		Resources: &structs.Resources{
   415  			MemoryMB: 100,
   416  			CPU:      100,
   417  		},
   418  		LogConfig: &structs.LogConfig{
   419  			MaxFiles:      1,
   420  			MaxFileSizeMB: 10,
   421  		},
   422  	}
   423  
   424  	tctx := testDockerDriverContexts(t, task)
   425  	defer tctx.AllocDir.Destroy()
   426  
   427  	copyImage(t, tctx.ExecCtx.TaskDir, "busybox.tar")
   428  	client := newTestDockerClient(t)
   429  	driver := NewDockerDriver(tctx.DriverCtx).(*DockerDriver)
   430  	driverConfig := &DockerDriverConfig{ImageName: "busybox", LoadImage: "busybox.tar"}
   431  	if _, err := driver.loadImage(driverConfig, client, tctx.ExecCtx.TaskDir); err != nil {
   432  		t.Fatalf("error loading image: %v", err)
   433  	}
   434  
   435  	// Create a container of the same name but don't start it. This mimics
   436  	// the case of dockerd getting restarted and stopping containers while
   437  	// Nomad is watching them.
   438  	opts := docker.CreateContainerOptions{
   439  		Name: fmt.Sprintf("%s-%s", task.Name, tctx.DriverCtx.allocID),
   440  		Config: &docker.Config{
   441  			Image: "busybox",
   442  			Cmd:   []string{"sleep", "9000"},
   443  		},
   444  	}
   445  	if _, err := client.CreateContainer(opts); err != nil {
   446  		t.Fatalf("error creating initial container: %v", err)
   447  	}
   448  
   449  	// Now assert that the driver can still start normally
   450  	presp, err := driver.Prestart(tctx.ExecCtx, task)
   451  	if err != nil {
   452  		driver.Cleanup(tctx.ExecCtx, presp.CreatedResources)
   453  		t.Fatalf("error in prestart: %v", err)
   454  	}
   455  	defer driver.Cleanup(tctx.ExecCtx, presp.CreatedResources)
   456  
   457  	sresp, err := driver.Start(tctx.ExecCtx, task)
   458  	if err != nil {
   459  		t.Fatalf("failed to start driver: %s", err)
   460  	}
   461  	handle := sresp.Handle.(*DockerHandle)
   462  	waitForExist(t, client, handle)
   463  	handle.Kill()
   464  }
   465  
   466  func TestDockerDriver_Start_LoadImage(t *testing.T) {
   467  	if !tu.IsTravis() {
   468  		t.Parallel()
   469  	}
   470  	if !testutil.DockerIsConnected(t) {
   471  		t.Skip("Docker not connected")
   472  	}
   473  	task := &structs.Task{
   474  		Name:   "busybox-demo",
   475  		Driver: "docker",
   476  		Config: map[string]interface{}{
   477  			"image":   "busybox",
   478  			"load":    "busybox.tar",
   479  			"command": "/bin/sh",
   480  			"args": []string{
   481  				"-c",
   482  				"echo hello > $NOMAD_TASK_DIR/output",
   483  			},
   484  		},
   485  		LogConfig: &structs.LogConfig{
   486  			MaxFiles:      10,
   487  			MaxFileSizeMB: 10,
   488  		},
   489  		Resources: &structs.Resources{
   490  			MemoryMB: 256,
   491  			CPU:      512,
   492  		},
   493  	}
   494  
   495  	ctx := testDockerDriverContexts(t, task)
   496  	//ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"}
   497  	defer ctx.AllocDir.Destroy()
   498  	d := NewDockerDriver(ctx.DriverCtx)
   499  
   500  	// Copy the image into the task's directory
   501  	copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar")
   502  
   503  	_, err := d.Prestart(ctx.ExecCtx, task)
   504  	if err != nil {
   505  		t.Fatalf("error in prestart: %v", err)
   506  	}
   507  	resp, err := d.Start(ctx.ExecCtx, task)
   508  	if err != nil {
   509  		t.Fatalf("err: %v", err)
   510  	}
   511  	defer resp.Handle.Kill()
   512  
   513  	select {
   514  	case res := <-resp.Handle.WaitCh():
   515  		if !res.Successful() {
   516  			t.Fatalf("err: %v", res)
   517  		}
   518  	case <-time.After(time.Duration(tu.TestMultiplier()*5) * time.Second):
   519  		t.Fatalf("timeout")
   520  	}
   521  
   522  	// Check that data was written to the shared alloc directory.
   523  	outputFile := filepath.Join(ctx.ExecCtx.TaskDir.LocalDir, "output")
   524  	act, err := ioutil.ReadFile(outputFile)
   525  	if err != nil {
   526  		t.Fatalf("Couldn't read expected output: %v", err)
   527  	}
   528  
   529  	exp := "hello"
   530  	if strings.TrimSpace(string(act)) != exp {
   531  		t.Fatalf("Command outputted %v; want %v", act, exp)
   532  	}
   533  
   534  }
   535  
   536  func TestDockerDriver_Start_BadPull_Recoverable(t *testing.T) {
   537  	if !tu.IsTravis() {
   538  		t.Parallel()
   539  	}
   540  	if !testutil.DockerIsConnected(t) {
   541  		t.Skip("Docker not connected")
   542  	}
   543  	task := &structs.Task{
   544  		Name:   "busybox-demo",
   545  		Driver: "docker",
   546  		Config: map[string]interface{}{
   547  			"image":   "127.0.1.1:32121/foo", // bad path
   548  			"command": "/bin/echo",
   549  			"args": []string{
   550  				"hello",
   551  			},
   552  		},
   553  		LogConfig: &structs.LogConfig{
   554  			MaxFiles:      10,
   555  			MaxFileSizeMB: 10,
   556  		},
   557  		Resources: &structs.Resources{
   558  			MemoryMB: 256,
   559  			CPU:      512,
   560  		},
   561  	}
   562  
   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  
   568  	_, err := d.Prestart(ctx.ExecCtx, task)
   569  	if err == nil {
   570  		t.Fatalf("want error in prestart: %v", err)
   571  	}
   572  
   573  	if rerr, ok := err.(*structs.RecoverableError); !ok {
   574  		t.Fatalf("want recoverable error: %+v", err)
   575  	} else if !rerr.IsRecoverable() {
   576  		t.Fatalf("error not recoverable: %+v", err)
   577  	}
   578  }
   579  
   580  func TestDockerDriver_Start_Wait_AllocDir(t *testing.T) {
   581  	if !tu.IsTravis() {
   582  		t.Parallel()
   583  	}
   584  	// This test requires that the alloc dir be mounted into docker as a volume.
   585  	// Because this cannot happen when docker is run remotely, e.g. when running
   586  	// docker in a VM, we skip this when we detect Docker is being run remotely.
   587  	if !testutil.DockerIsConnected(t) || dockerIsRemote(t) {
   588  		t.Skip("Docker not connected")
   589  	}
   590  
   591  	exp := []byte{'w', 'i', 'n'}
   592  	file := "output.txt"
   593  	task := &structs.Task{
   594  		Name:   "nc-demo",
   595  		Driver: "docker",
   596  		Config: map[string]interface{}{
   597  			"image":   "busybox",
   598  			"load":    "busybox.tar",
   599  			"command": "/bin/sh",
   600  			"args": []string{
   601  				"-c",
   602  				fmt.Sprintf(`sleep 1; echo -n %s > $%s/%s`,
   603  					string(exp), env.AllocDir, file),
   604  			},
   605  		},
   606  		LogConfig: &structs.LogConfig{
   607  			MaxFiles:      10,
   608  			MaxFileSizeMB: 10,
   609  		},
   610  		Resources: &structs.Resources{
   611  			MemoryMB: 256,
   612  			CPU:      512,
   613  		},
   614  	}
   615  
   616  	ctx := testDockerDriverContexts(t, task)
   617  	//ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"}
   618  	defer ctx.AllocDir.Destroy()
   619  	d := NewDockerDriver(ctx.DriverCtx)
   620  	copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar")
   621  
   622  	_, err := d.Prestart(ctx.ExecCtx, task)
   623  	if err != nil {
   624  		t.Fatalf("error in prestart: %v", err)
   625  	}
   626  	resp, err := d.Start(ctx.ExecCtx, task)
   627  	if err != nil {
   628  		t.Fatalf("err: %v", err)
   629  	}
   630  	defer resp.Handle.Kill()
   631  
   632  	select {
   633  	case res := <-resp.Handle.WaitCh():
   634  		if !res.Successful() {
   635  			t.Fatalf("err: %v", res)
   636  		}
   637  	case <-time.After(time.Duration(tu.TestMultiplier()*5) * time.Second):
   638  		t.Fatalf("timeout")
   639  	}
   640  
   641  	// Check that data was written to the shared alloc directory.
   642  	outputFile := filepath.Join(ctx.AllocDir.SharedDir, file)
   643  	act, err := ioutil.ReadFile(outputFile)
   644  	if err != nil {
   645  		t.Fatalf("Couldn't read expected output: %v", err)
   646  	}
   647  
   648  	if !reflect.DeepEqual(act, exp) {
   649  		t.Fatalf("Command outputted %v; want %v", act, exp)
   650  	}
   651  }
   652  
   653  func TestDockerDriver_Start_Kill_Wait(t *testing.T) {
   654  	if !tu.IsTravis() {
   655  		t.Parallel()
   656  	}
   657  	if !testutil.DockerIsConnected(t) {
   658  		t.Skip("Docker not connected")
   659  	}
   660  	task := &structs.Task{
   661  		Name:   "nc-demo",
   662  		Driver: "docker",
   663  		Config: map[string]interface{}{
   664  			"image":   "busybox",
   665  			"load":    "busybox.tar",
   666  			"command": "/bin/sleep",
   667  			"args":    []string{"10"},
   668  		},
   669  		LogConfig: &structs.LogConfig{
   670  			MaxFiles:      10,
   671  			MaxFileSizeMB: 10,
   672  		},
   673  		Resources: basicResources,
   674  	}
   675  
   676  	_, handle, cleanup := dockerSetup(t, task)
   677  	defer cleanup()
   678  
   679  	go func() {
   680  		time.Sleep(100 * time.Millisecond)
   681  		err := handle.Kill()
   682  		if err != nil {
   683  			t.Fatalf("err: %v", err)
   684  		}
   685  	}()
   686  
   687  	select {
   688  	case res := <-handle.WaitCh():
   689  		if res.Successful() {
   690  			t.Fatalf("should err: %v", res)
   691  		}
   692  	case <-time.After(time.Duration(tu.TestMultiplier()*10) * time.Second):
   693  		t.Fatalf("timeout")
   694  	}
   695  }
   696  
   697  func TestDockerDriver_StartN(t *testing.T) {
   698  	if !tu.IsTravis() {
   699  		t.Parallel()
   700  	}
   701  	if !testutil.DockerIsConnected(t) {
   702  		t.Skip("Docker not connected")
   703  	}
   704  
   705  	task1, _, _ := dockerTask(t)
   706  	task2, _, _ := dockerTask(t)
   707  	task3, _, _ := dockerTask(t)
   708  	taskList := []*structs.Task{task1, task2, task3}
   709  
   710  	handles := make([]DriverHandle, len(taskList))
   711  
   712  	t.Logf("Starting %d tasks", len(taskList))
   713  
   714  	// Let's spin up a bunch of things
   715  	for idx, task := range taskList {
   716  		ctx := testDockerDriverContexts(t, task)
   717  		//ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"}
   718  		defer ctx.AllocDir.Destroy()
   719  		d := NewDockerDriver(ctx.DriverCtx)
   720  		copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar")
   721  
   722  		_, err := d.Prestart(ctx.ExecCtx, task)
   723  		if err != nil {
   724  			t.Fatalf("error in prestart #%d: %v", idx+1, err)
   725  		}
   726  		resp, err := d.Start(ctx.ExecCtx, task)
   727  		if err != nil {
   728  			t.Errorf("Failed starting task #%d: %s", idx+1, err)
   729  			continue
   730  		}
   731  		handles[idx] = resp.Handle
   732  	}
   733  
   734  	t.Log("All tasks are started. Terminating...")
   735  
   736  	for idx, handle := range handles {
   737  		if handle == nil {
   738  			t.Errorf("Bad handle for task #%d", idx+1)
   739  			continue
   740  		}
   741  
   742  		err := handle.Kill()
   743  		if err != nil {
   744  			t.Errorf("Failed stopping task #%d: %s", idx+1, err)
   745  		}
   746  	}
   747  
   748  	t.Log("Test complete!")
   749  }
   750  
   751  func TestDockerDriver_StartNVersions(t *testing.T) {
   752  	if !tu.IsTravis() {
   753  		t.Parallel()
   754  	}
   755  	if !testutil.DockerIsConnected(t) {
   756  		t.Skip("Docker not connected")
   757  	}
   758  
   759  	task1, _, _ := dockerTask(t)
   760  	task1.Config["image"] = "busybox"
   761  	task1.Config["load"] = "busybox.tar"
   762  
   763  	task2, _, _ := dockerTask(t)
   764  	task2.Config["image"] = "busybox:musl"
   765  	task2.Config["load"] = "busybox_musl.tar"
   766  
   767  	task3, _, _ := dockerTask(t)
   768  	task3.Config["image"] = "busybox:glibc"
   769  	task3.Config["load"] = "busybox_glibc.tar"
   770  
   771  	taskList := []*structs.Task{task1, task2, task3}
   772  
   773  	handles := make([]DriverHandle, len(taskList))
   774  
   775  	t.Logf("Starting %d tasks", len(taskList))
   776  
   777  	// Let's spin up a bunch of things
   778  	for idx, task := range taskList {
   779  		ctx := testDockerDriverContexts(t, task)
   780  		//ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"}
   781  		defer ctx.AllocDir.Destroy()
   782  		d := NewDockerDriver(ctx.DriverCtx)
   783  		copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar")
   784  		copyImage(t, ctx.ExecCtx.TaskDir, "busybox_musl.tar")
   785  		copyImage(t, ctx.ExecCtx.TaskDir, "busybox_glibc.tar")
   786  
   787  		_, err := d.Prestart(ctx.ExecCtx, task)
   788  		if err != nil {
   789  			t.Fatalf("error in prestart #%d: %v", idx+1, err)
   790  		}
   791  		resp, err := d.Start(ctx.ExecCtx, task)
   792  		if err != nil {
   793  			t.Errorf("Failed starting task #%d: %s", idx+1, err)
   794  			continue
   795  		}
   796  		handles[idx] = resp.Handle
   797  	}
   798  
   799  	t.Log("All tasks are started. Terminating...")
   800  
   801  	for idx, handle := range handles {
   802  		if handle == nil {
   803  			t.Errorf("Bad handle for task #%d", idx+1)
   804  			continue
   805  		}
   806  
   807  		err := handle.Kill()
   808  		if err != nil {
   809  			t.Errorf("Failed stopping task #%d: %s", idx+1, err)
   810  		}
   811  	}
   812  
   813  	t.Log("Test complete!")
   814  }
   815  
   816  func waitForExist(t *testing.T, client *docker.Client, handle *DockerHandle) {
   817  	handle.logger.Printf("[DEBUG] docker.test: waiting for container %s to exist...", handle.ContainerID())
   818  	tu.WaitForResult(func() (bool, error) {
   819  		container, err := client.InspectContainer(handle.ContainerID())
   820  		if err != nil {
   821  			if _, ok := err.(*docker.NoSuchContainer); !ok {
   822  				return false, err
   823  			}
   824  		}
   825  
   826  		return container != nil, nil
   827  	}, func(err error) {
   828  		t.Fatalf("err: %v", err)
   829  	})
   830  	handle.logger.Printf("[DEBUG] docker.test: ...container %s exists!", handle.ContainerID())
   831  }
   832  
   833  func TestDockerDriver_NetworkMode_Host(t *testing.T) {
   834  	if !tu.IsTravis() {
   835  		t.Parallel()
   836  	}
   837  	if !testutil.DockerIsConnected(t) {
   838  		t.Skip("Docker not connected")
   839  	}
   840  	expected := "host"
   841  
   842  	task := &structs.Task{
   843  		Name:   "nc-demo",
   844  		Driver: "docker",
   845  		Config: map[string]interface{}{
   846  			"image":        "busybox",
   847  			"load":         "busybox.tar",
   848  			"command":      "/bin/nc",
   849  			"args":         []string{"-l", "127.0.0.1", "-p", "0"},
   850  			"network_mode": expected,
   851  		},
   852  		Resources: &structs.Resources{
   853  			MemoryMB: 256,
   854  			CPU:      512,
   855  		},
   856  		LogConfig: &structs.LogConfig{
   857  			MaxFiles:      10,
   858  			MaxFileSizeMB: 10,
   859  		},
   860  	}
   861  
   862  	client, handle, cleanup := dockerSetup(t, task)
   863  	defer cleanup()
   864  
   865  	waitForExist(t, client, handle)
   866  
   867  	container, err := client.InspectContainer(handle.ContainerID())
   868  	if err != nil {
   869  		t.Fatalf("err: %v", err)
   870  	}
   871  
   872  	actual := container.HostConfig.NetworkMode
   873  	if actual != expected {
   874  		t.Fatalf("Got network mode %q; want %q", expected, actual)
   875  	}
   876  }
   877  
   878  func TestDockerDriver_NetworkAliases_Bridge(t *testing.T) {
   879  	if !tu.IsTravis() {
   880  		t.Parallel()
   881  	}
   882  	if !testutil.DockerIsConnected(t) {
   883  		t.Skip("Docker not connected")
   884  	}
   885  
   886  	// Because go-dockerclient doesn't provide api for query network aliases, just check that
   887  	// a container can be created with a 'network_aliases' property
   888  
   889  	// Create network, network-scoped alias is supported only for containers in user defined networks
   890  	client := newTestDockerClient(t)
   891  	networkOpts := docker.CreateNetworkOptions{Name: "foobar", Driver: "bridge"}
   892  	network, err := client.CreateNetwork(networkOpts)
   893  	if err != nil {
   894  		t.Fatalf("err: %v", err)
   895  	}
   896  	defer client.RemoveNetwork(network.ID)
   897  
   898  	expected := []string{"foobar"}
   899  	task := &structs.Task{
   900  		Name:   "nc-demo",
   901  		Driver: "docker",
   902  		Config: map[string]interface{}{
   903  			"image":           "busybox",
   904  			"load":            "busybox.tar",
   905  			"command":         "/bin/nc",
   906  			"args":            []string{"-l", "127.0.0.1", "-p", "0"},
   907  			"network_mode":    network.Name,
   908  			"network_aliases": expected,
   909  		},
   910  		Resources: &structs.Resources{
   911  			MemoryMB: 256,
   912  			CPU:      512,
   913  		},
   914  		LogConfig: &structs.LogConfig{
   915  			MaxFiles:      10,
   916  			MaxFileSizeMB: 10,
   917  		},
   918  	}
   919  
   920  	client, handle, cleanup := dockerSetupWithClient(t, task, client)
   921  	defer cleanup()
   922  
   923  	waitForExist(t, client, handle)
   924  
   925  	_, err = client.InspectContainer(handle.ContainerID())
   926  	if err != nil {
   927  		t.Fatalf("err: %v", err)
   928  	}
   929  }
   930  
   931  func TestDockerDriver_Sysctl_Ulimit(t *testing.T) {
   932  	task, _, _ := dockerTask(t)
   933  	expectedUlimits := map[string]string{
   934  		"nproc":  "4242",
   935  		"nofile": "2048:4096",
   936  	}
   937  	task.Config["sysctl"] = []map[string]string{
   938  		{
   939  			"net.core.somaxconn": "16384",
   940  		},
   941  	}
   942  	task.Config["ulimit"] = []map[string]string{
   943  		expectedUlimits,
   944  	}
   945  
   946  	client, handle, cleanup := dockerSetup(t, task)
   947  	defer cleanup()
   948  
   949  	waitForExist(t, client, handle)
   950  
   951  	container, err := client.InspectContainer(handle.ContainerID())
   952  	assert.Nil(t, err, "unexpected error: %v", err)
   953  
   954  	want := "16384"
   955  	got := container.HostConfig.Sysctls["net.core.somaxconn"]
   956  	assert.Equal(t, want, got, "Wrong net.core.somaxconn config for docker job. Expect: %s, got: %s", want, got)
   957  
   958  	expectedUlimitLen := 2
   959  	actualUlimitLen := len(container.HostConfig.Ulimits)
   960  	assert.Equal(t, want, got, "Wrong number of ulimit configs for docker job. Expect: %d, got: %d", expectedUlimitLen, actualUlimitLen)
   961  
   962  	for _, got := range container.HostConfig.Ulimits {
   963  		if expectedStr, ok := expectedUlimits[got.Name]; !ok {
   964  			t.Errorf("%s config unexpected for docker job.", got.Name)
   965  		} else {
   966  			if !strings.Contains(expectedStr, ":") {
   967  				expectedStr = expectedStr + ":" + expectedStr
   968  			}
   969  
   970  			splitted := strings.SplitN(expectedStr, ":", 2)
   971  			soft, _ := strconv.Atoi(splitted[0])
   972  			hard, _ := strconv.Atoi(splitted[1])
   973  			assert.Equal(t, int64(soft), got.Soft, "Wrong soft %s ulimit for docker job. Expect: %d, got: %d", got.Name, soft, got.Soft)
   974  			assert.Equal(t, int64(hard), got.Hard, "Wrong hard %s ulimit for docker job. Expect: %d, got: %d", got.Name, hard, got.Hard)
   975  
   976  		}
   977  	}
   978  }
   979  
   980  func TestDockerDriver_Sysctl_Ulimit_Errors(t *testing.T) {
   981  	brokenConfigs := []interface{}{
   982  		map[string]interface{}{
   983  			"nofile": "",
   984  		},
   985  		map[string]interface{}{
   986  			"nofile": "abc:1234",
   987  		},
   988  		map[string]interface{}{
   989  			"nofile": "1234:abc",
   990  		},
   991  	}
   992  
   993  	test_cases := []struct {
   994  		ulimitConfig interface{}
   995  		err          error
   996  	}{
   997  		{[]interface{}{brokenConfigs[0]}, fmt.Errorf("Malformed ulimit specification nofile: \"\", cannot be empty")},
   998  		{[]interface{}{brokenConfigs[1]}, fmt.Errorf("Malformed soft ulimit nofile: abc:1234")},
   999  		{[]interface{}{brokenConfigs[2]}, fmt.Errorf("Malformed hard ulimit nofile: 1234:abc")},
  1000  	}
  1001  
  1002  	for _, tc := range test_cases {
  1003  		task, _, _ := dockerTask(t)
  1004  		task.Config["ulimit"] = tc.ulimitConfig
  1005  
  1006  		ctx := testDockerDriverContexts(t, task)
  1007  		driver := NewDockerDriver(ctx.DriverCtx)
  1008  		copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar")
  1009  		defer ctx.AllocDir.Destroy()
  1010  
  1011  		_, err := driver.Prestart(ctx.ExecCtx, task)
  1012  		assert.NotNil(t, err, "Expected non nil error")
  1013  		assert.Equal(t, err.Error(), tc.err.Error(), "unexpected error in prestart, got %v, expected %v", err, tc.err)
  1014  	}
  1015  }
  1016  
  1017  func TestDockerDriver_Labels(t *testing.T) {
  1018  	if !tu.IsTravis() {
  1019  		t.Parallel()
  1020  	}
  1021  	if !testutil.DockerIsConnected(t) {
  1022  		t.Skip("Docker not connected")
  1023  	}
  1024  
  1025  	task, _, _ := dockerTask(t)
  1026  	task.Config["labels"] = []map[string]string{
  1027  		{
  1028  			"label1": "value1",
  1029  			"label2": "value2",
  1030  		},
  1031  	}
  1032  
  1033  	client, handle, cleanup := dockerSetup(t, task)
  1034  	defer cleanup()
  1035  
  1036  	waitForExist(t, client, handle)
  1037  
  1038  	container, err := client.InspectContainer(handle.ContainerID())
  1039  	if err != nil {
  1040  		t.Fatalf("err: %v", err)
  1041  	}
  1042  
  1043  	if want, got := 2, len(container.Config.Labels); want != got {
  1044  		t.Errorf("Wrong labels count for docker job. Expect: %d, got: %d", want, got)
  1045  	}
  1046  
  1047  	if want, got := "value1", container.Config.Labels["label1"]; want != got {
  1048  		t.Errorf("Wrong label value docker job. Expect: %s, got: %s", want, got)
  1049  	}
  1050  }
  1051  
  1052  func TestDockerDriver_ForcePull_IsInvalidConfig(t *testing.T) {
  1053  	if !tu.IsTravis() {
  1054  		t.Parallel()
  1055  	}
  1056  	if !testutil.DockerIsConnected(t) {
  1057  		t.Skip("Docker not connected")
  1058  	}
  1059  
  1060  	task, _, _ := dockerTask(t)
  1061  	task.Config["force_pull"] = "nothing"
  1062  
  1063  	ctx := testDockerDriverContexts(t, task)
  1064  	defer ctx.AllocDir.Destroy()
  1065  	//ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"}
  1066  	driver := NewDockerDriver(ctx.DriverCtx)
  1067  
  1068  	if _, err := driver.Prestart(ctx.ExecCtx, task); err == nil {
  1069  		t.Fatalf("error expected in prestart")
  1070  	}
  1071  }
  1072  
  1073  func TestDockerDriver_ForcePull(t *testing.T) {
  1074  	if !tu.IsTravis() {
  1075  		t.Parallel()
  1076  	}
  1077  	if !testutil.DockerIsConnected(t) {
  1078  		t.Skip("Docker not connected")
  1079  	}
  1080  
  1081  	task, _, _ := dockerTask(t)
  1082  	task.Config["force_pull"] = "true"
  1083  
  1084  	client, handle, cleanup := dockerSetup(t, task)
  1085  	defer cleanup()
  1086  
  1087  	waitForExist(t, client, handle)
  1088  
  1089  	_, err := client.InspectContainer(handle.ContainerID())
  1090  	if err != nil {
  1091  		t.Fatalf("err: %v", err)
  1092  	}
  1093  }
  1094  
  1095  func TestDockerDriver_SecurityOpt(t *testing.T) {
  1096  	if !tu.IsTravis() {
  1097  		t.Parallel()
  1098  	}
  1099  	if !testutil.DockerIsConnected(t) {
  1100  		t.Skip("Docker not connected")
  1101  	}
  1102  
  1103  	task, _, _ := dockerTask(t)
  1104  	task.Config["security_opt"] = []string{"seccomp=unconfined"}
  1105  
  1106  	client, handle, cleanup := dockerSetup(t, task)
  1107  	defer cleanup()
  1108  
  1109  	waitForExist(t, client, handle)
  1110  
  1111  	container, err := client.InspectContainer(handle.ContainerID())
  1112  	if err != nil {
  1113  		t.Fatalf("err: %v", err)
  1114  	}
  1115  
  1116  	if !reflect.DeepEqual(task.Config["security_opt"], container.HostConfig.SecurityOpt) {
  1117  		t.Errorf("Security Opts don't match.\nExpected:\n%s\nGot:\n%s\n", task.Config["security_opt"], container.HostConfig.SecurityOpt)
  1118  	}
  1119  }
  1120  
  1121  func TestDockerDriver_Capabilities(t *testing.T) {
  1122  	if !tu.IsTravis() {
  1123  		t.Parallel()
  1124  	}
  1125  	if !testutil.DockerIsConnected(t) {
  1126  		t.Skip("Docker not connected")
  1127  	}
  1128  	if runtime.GOOS == "windows" {
  1129  		t.Skip("Capabilities not supported on windows")
  1130  	}
  1131  
  1132  	testCases := []struct {
  1133  		Name       string
  1134  		CapAdd     []string
  1135  		CapDrop    []string
  1136  		Whitelist  string
  1137  		StartError string
  1138  	}{
  1139  		{
  1140  			Name:    "default-whitelist-add-allowed",
  1141  			CapAdd:  []string{"fowner", "mknod"},
  1142  			CapDrop: []string{"all"},
  1143  		},
  1144  		{
  1145  			Name:       "default-whitelist-add-forbidden",
  1146  			CapAdd:     []string{"net_admin"},
  1147  			StartError: "net_admin",
  1148  		},
  1149  		{
  1150  			Name:    "default-whitelist-drop-existing",
  1151  			CapDrop: []string{"fowner", "mknod"},
  1152  		},
  1153  		{
  1154  			Name:      "restrictive-whitelist-drop-all",
  1155  			CapDrop:   []string{"all"},
  1156  			Whitelist: "fowner,mknod",
  1157  		},
  1158  		{
  1159  			Name:      "restrictive-whitelist-add-allowed",
  1160  			CapAdd:    []string{"fowner", "mknod"},
  1161  			CapDrop:   []string{"all"},
  1162  			Whitelist: "fowner,mknod",
  1163  		},
  1164  		{
  1165  			Name:       "restrictive-whitelist-add-forbidden",
  1166  			CapAdd:     []string{"net_admin", "mknod"},
  1167  			CapDrop:    []string{"all"},
  1168  			Whitelist:  "fowner,mknod",
  1169  			StartError: "net_admin",
  1170  		},
  1171  		{
  1172  			Name:      "permissive-whitelist",
  1173  			CapAdd:    []string{"net_admin", "mknod"},
  1174  			Whitelist: "all",
  1175  		},
  1176  		{
  1177  			Name:      "permissive-whitelist-add-all",
  1178  			CapAdd:    []string{"all"},
  1179  			Whitelist: "all",
  1180  		},
  1181  	}
  1182  
  1183  	for _, tc := range testCases {
  1184  		t.Run(tc.Name, func(t *testing.T) {
  1185  			client := newTestDockerClient(t)
  1186  			task, _, _ := dockerTask(t)
  1187  			if len(tc.CapAdd) > 0 {
  1188  				task.Config["cap_add"] = tc.CapAdd
  1189  			}
  1190  			if len(tc.CapDrop) > 0 {
  1191  				task.Config["cap_drop"] = tc.CapDrop
  1192  			}
  1193  
  1194  			tctx := testDockerDriverContexts(t, task)
  1195  			if tc.Whitelist != "" {
  1196  				tctx.DriverCtx.config.Options[dockerCapsWhitelistConfigOption] = tc.Whitelist
  1197  			}
  1198  
  1199  			driver := NewDockerDriver(tctx.DriverCtx)
  1200  			copyImage(t, tctx.ExecCtx.TaskDir, "busybox.tar")
  1201  			defer tctx.AllocDir.Destroy()
  1202  
  1203  			presp, err := driver.Prestart(tctx.ExecCtx, task)
  1204  			defer driver.Cleanup(tctx.ExecCtx, presp.CreatedResources)
  1205  			if err != nil {
  1206  				t.Fatalf("Error in prestart: %v", err)
  1207  			}
  1208  
  1209  			sresp, err := driver.Start(tctx.ExecCtx, task)
  1210  			if err == nil && tc.StartError != "" {
  1211  				t.Fatalf("Expected error in start: %v", tc.StartError)
  1212  			} else if err != nil {
  1213  				if tc.StartError == "" {
  1214  					t.Fatalf("Failed to start driver: %s\nStack\n%s", err, debug.Stack())
  1215  				} else if !strings.Contains(err.Error(), tc.StartError) {
  1216  					t.Fatalf("Expect error containing \"%s\", got %v", tc.StartError, err)
  1217  				}
  1218  				return
  1219  			}
  1220  
  1221  			if sresp.Handle == nil {
  1222  				t.Fatalf("handle is nil\nStack\n%s", debug.Stack())
  1223  			}
  1224  			defer sresp.Handle.Kill()
  1225  			handle := sresp.Handle.(*DockerHandle)
  1226  
  1227  			waitForExist(t, client, handle)
  1228  
  1229  			container, err := client.InspectContainer(handle.ContainerID())
  1230  			if err != nil {
  1231  				t.Fatalf("Error inspecting container: %v", err)
  1232  			}
  1233  
  1234  			if !reflect.DeepEqual(tc.CapAdd, container.HostConfig.CapAdd) {
  1235  				t.Errorf("CapAdd doesn't match.\nExpected:\n%s\nGot:\n%s\n", tc.CapAdd, container.HostConfig.CapAdd)
  1236  			}
  1237  
  1238  			if !reflect.DeepEqual(tc.CapDrop, container.HostConfig.CapDrop) {
  1239  				t.Errorf("CapDrop doesn't match.\nExpected:\n%s\nGot:\n%s\n", tc.CapDrop, container.HostConfig.CapDrop)
  1240  			}
  1241  		})
  1242  	}
  1243  }
  1244  
  1245  func TestDockerDriver_DNS(t *testing.T) {
  1246  	if !tu.IsTravis() {
  1247  		t.Parallel()
  1248  	}
  1249  	if !testutil.DockerIsConnected(t) {
  1250  		t.Skip("Docker not connected")
  1251  	}
  1252  
  1253  	task, _, _ := dockerTask(t)
  1254  	task.Config["dns_servers"] = []string{"8.8.8.8", "8.8.4.4"}
  1255  	task.Config["dns_search_domains"] = []string{"example.com", "example.org", "example.net"}
  1256  	task.Config["dns_options"] = []string{"ndots:1"}
  1257  
  1258  	client, handle, cleanup := dockerSetup(t, task)
  1259  	defer cleanup()
  1260  
  1261  	waitForExist(t, client, handle)
  1262  
  1263  	container, err := client.InspectContainer(handle.ContainerID())
  1264  	if err != nil {
  1265  		t.Fatalf("err: %v", err)
  1266  	}
  1267  
  1268  	if !reflect.DeepEqual(task.Config["dns_servers"], container.HostConfig.DNS) {
  1269  		t.Errorf("DNS Servers don't match.\nExpected:\n%s\nGot:\n%s\n", task.Config["dns_servers"], container.HostConfig.DNS)
  1270  	}
  1271  
  1272  	if !reflect.DeepEqual(task.Config["dns_search_domains"], container.HostConfig.DNSSearch) {
  1273  		t.Errorf("DNS Search Domains don't match.\nExpected:\n%s\nGot:\n%s\n", task.Config["dns_search_domains"], container.HostConfig.DNSSearch)
  1274  	}
  1275  
  1276  	if !reflect.DeepEqual(task.Config["dns_options"], container.HostConfig.DNSOptions) {
  1277  		t.Errorf("DNS Options don't match.\nExpected:\n%s\nGot:\n%s\n", task.Config["dns_options"], container.HostConfig.DNSOptions)
  1278  	}
  1279  }
  1280  
  1281  func TestDockerDriver_MACAddress(t *testing.T) {
  1282  	if !tu.IsTravis() {
  1283  		t.Parallel()
  1284  	}
  1285  	if !testutil.DockerIsConnected(t) {
  1286  		t.Skip("Docker not connected")
  1287  	}
  1288  
  1289  	task, _, _ := dockerTask(t)
  1290  	task.Config["mac_address"] = "00:16:3e:00:00:00"
  1291  
  1292  	client, handle, cleanup := dockerSetup(t, task)
  1293  	defer cleanup()
  1294  
  1295  	waitForExist(t, client, handle)
  1296  
  1297  	container, err := client.InspectContainer(handle.ContainerID())
  1298  	if err != nil {
  1299  		t.Fatalf("err: %v", err)
  1300  	}
  1301  
  1302  	if container.NetworkSettings.MacAddress != task.Config["mac_address"] {
  1303  		t.Errorf("expected mac_address=%q but found %q", task.Config["mac_address"], container.NetworkSettings.MacAddress)
  1304  	}
  1305  }
  1306  
  1307  func TestDockerWorkDir(t *testing.T) {
  1308  	if !tu.IsTravis() {
  1309  		t.Parallel()
  1310  	}
  1311  	if !testutil.DockerIsConnected(t) {
  1312  		t.Skip("Docker not connected")
  1313  	}
  1314  
  1315  	task, _, _ := dockerTask(t)
  1316  	task.Config["work_dir"] = "/some/path"
  1317  
  1318  	client, handle, cleanup := dockerSetup(t, task)
  1319  	defer cleanup()
  1320  
  1321  	container, err := client.InspectContainer(handle.ContainerID())
  1322  	if err != nil {
  1323  		t.Fatalf("err: %v", err)
  1324  	}
  1325  
  1326  	if want, got := "/some/path", container.Config.WorkingDir; want != got {
  1327  		t.Errorf("Wrong working directory for docker job. Expect: %s, got: %s", want, got)
  1328  	}
  1329  }
  1330  
  1331  func inSlice(needle string, haystack []string) bool {
  1332  	for _, h := range haystack {
  1333  		if h == needle {
  1334  			return true
  1335  		}
  1336  	}
  1337  	return false
  1338  }
  1339  
  1340  func TestDockerDriver_PortsNoMap(t *testing.T) {
  1341  	if !tu.IsTravis() {
  1342  		t.Parallel()
  1343  	}
  1344  	if !testutil.DockerIsConnected(t) {
  1345  		t.Skip("Docker not connected")
  1346  	}
  1347  
  1348  	task, res, dyn := dockerTask(t)
  1349  
  1350  	client, handle, cleanup := dockerSetup(t, task)
  1351  	defer cleanup()
  1352  
  1353  	waitForExist(t, client, handle)
  1354  
  1355  	container, err := client.InspectContainer(handle.ContainerID())
  1356  	if err != nil {
  1357  		t.Fatalf("err: %v", err)
  1358  	}
  1359  
  1360  	// Verify that the correct ports are EXPOSED
  1361  	expectedExposedPorts := map[docker.Port]struct{}{
  1362  		docker.Port(fmt.Sprintf("%d/tcp", res)): {},
  1363  		docker.Port(fmt.Sprintf("%d/udp", res)): {},
  1364  		docker.Port(fmt.Sprintf("%d/tcp", dyn)): {},
  1365  		docker.Port(fmt.Sprintf("%d/udp", dyn)): {},
  1366  	}
  1367  
  1368  	if !reflect.DeepEqual(container.Config.ExposedPorts, expectedExposedPorts) {
  1369  		t.Errorf("Exposed ports don't match.\nExpected:\n%s\nGot:\n%s\n", expectedExposedPorts, container.Config.ExposedPorts)
  1370  	}
  1371  
  1372  	// Verify that the correct ports are FORWARDED
  1373  	expectedPortBindings := map[docker.Port][]docker.PortBinding{
  1374  		docker.Port(fmt.Sprintf("%d/tcp", res)): {{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", res)}},
  1375  		docker.Port(fmt.Sprintf("%d/udp", res)): {{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", res)}},
  1376  		docker.Port(fmt.Sprintf("%d/tcp", dyn)): {{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", dyn)}},
  1377  		docker.Port(fmt.Sprintf("%d/udp", dyn)): {{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", dyn)}},
  1378  	}
  1379  
  1380  	if !reflect.DeepEqual(container.HostConfig.PortBindings, expectedPortBindings) {
  1381  		t.Errorf("Forwarded ports don't match.\nExpected:\n%s\nGot:\n%s\n", expectedPortBindings, container.HostConfig.PortBindings)
  1382  	}
  1383  
  1384  	expectedEnvironment := map[string]string{
  1385  		"NOMAD_ADDR_main":  fmt.Sprintf("127.0.0.1:%d", res),
  1386  		"NOMAD_ADDR_REDIS": fmt.Sprintf("127.0.0.1:%d", dyn),
  1387  	}
  1388  
  1389  	for key, val := range expectedEnvironment {
  1390  		search := fmt.Sprintf("%s=%s", key, val)
  1391  		if !inSlice(search, container.Config.Env) {
  1392  			t.Errorf("Expected to find %s in container environment: %+v", search, container.Config.Env)
  1393  		}
  1394  	}
  1395  }
  1396  
  1397  func TestDockerDriver_PortsMapping(t *testing.T) {
  1398  	if !tu.IsTravis() {
  1399  		t.Parallel()
  1400  	}
  1401  	if !testutil.DockerIsConnected(t) {
  1402  		t.Skip("Docker not connected")
  1403  	}
  1404  
  1405  	task, res, dyn := dockerTask(t)
  1406  	task.Config["port_map"] = []map[string]string{
  1407  		{
  1408  			"main":  "8080",
  1409  			"REDIS": "6379",
  1410  		},
  1411  	}
  1412  
  1413  	client, handle, cleanup := dockerSetup(t, task)
  1414  	defer cleanup()
  1415  
  1416  	waitForExist(t, client, handle)
  1417  
  1418  	container, err := client.InspectContainer(handle.ContainerID())
  1419  	if err != nil {
  1420  		t.Fatalf("err: %v", err)
  1421  	}
  1422  
  1423  	// Verify that the correct ports are EXPOSED
  1424  	expectedExposedPorts := map[docker.Port]struct{}{
  1425  		docker.Port("8080/tcp"): {},
  1426  		docker.Port("8080/udp"): {},
  1427  		docker.Port("6379/tcp"): {},
  1428  		docker.Port("6379/udp"): {},
  1429  	}
  1430  
  1431  	if !reflect.DeepEqual(container.Config.ExposedPorts, expectedExposedPorts) {
  1432  		t.Errorf("Exposed ports don't match.\nExpected:\n%s\nGot:\n%s\n", expectedExposedPorts, container.Config.ExposedPorts)
  1433  	}
  1434  
  1435  	// Verify that the correct ports are FORWARDED
  1436  	expectedPortBindings := map[docker.Port][]docker.PortBinding{
  1437  		docker.Port("8080/tcp"): {{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", res)}},
  1438  		docker.Port("8080/udp"): {{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", res)}},
  1439  		docker.Port("6379/tcp"): {{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", dyn)}},
  1440  		docker.Port("6379/udp"): {{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", dyn)}},
  1441  	}
  1442  
  1443  	if !reflect.DeepEqual(container.HostConfig.PortBindings, expectedPortBindings) {
  1444  		t.Errorf("Forwarded ports don't match.\nExpected:\n%s\nGot:\n%s\n", expectedPortBindings, container.HostConfig.PortBindings)
  1445  	}
  1446  
  1447  	expectedEnvironment := map[string]string{
  1448  		"NOMAD_PORT_main":      "8080",
  1449  		"NOMAD_PORT_REDIS":     "6379",
  1450  		"NOMAD_HOST_PORT_main": strconv.Itoa(res),
  1451  	}
  1452  
  1453  	sort.Strings(container.Config.Env)
  1454  	for key, val := range expectedEnvironment {
  1455  		search := fmt.Sprintf("%s=%s", key, val)
  1456  		if !inSlice(search, container.Config.Env) {
  1457  			t.Errorf("Expected to find %s in container environment:\n%s\n\n", search, strings.Join(container.Config.Env, "\n"))
  1458  		}
  1459  	}
  1460  }
  1461  
  1462  func TestDockerDriver_User(t *testing.T) {
  1463  	if !tu.IsTravis() {
  1464  		t.Parallel()
  1465  	}
  1466  	if !testutil.DockerIsConnected(t) {
  1467  		t.Skip("Docker not connected")
  1468  	}
  1469  
  1470  	task := &structs.Task{
  1471  		Name:   "redis-demo",
  1472  		User:   "alice",
  1473  		Driver: "docker",
  1474  		Config: map[string]interface{}{
  1475  			"image":   "busybox",
  1476  			"load":    "busybox.tar",
  1477  			"command": "/bin/sleep",
  1478  			"args":    []string{"10000"},
  1479  		},
  1480  		Resources: &structs.Resources{
  1481  			MemoryMB: 256,
  1482  			CPU:      512,
  1483  		},
  1484  		LogConfig: &structs.LogConfig{
  1485  			MaxFiles:      10,
  1486  			MaxFileSizeMB: 10,
  1487  		},
  1488  	}
  1489  
  1490  	ctx := testDockerDriverContexts(t, task)
  1491  	//ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"}
  1492  	driver := NewDockerDriver(ctx.DriverCtx)
  1493  	defer ctx.AllocDir.Destroy()
  1494  	copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar")
  1495  
  1496  	_, err := driver.Prestart(ctx.ExecCtx, task)
  1497  	if err != nil {
  1498  		t.Fatalf("error in prestart: %v", err)
  1499  	}
  1500  
  1501  	// It should fail because the user "alice" does not exist on the given
  1502  	// image.
  1503  	resp, err := driver.Start(ctx.ExecCtx, task)
  1504  	if err == nil {
  1505  		resp.Handle.Kill()
  1506  		t.Fatalf("Should've failed")
  1507  	}
  1508  
  1509  	if !strings.Contains(err.Error(), "alice") {
  1510  		t.Fatalf("Expected failure string not found, found %q instead", err.Error())
  1511  	}
  1512  }
  1513  
  1514  func TestDockerDriver_CleanupContainer(t *testing.T) {
  1515  	if !tu.IsTravis() {
  1516  		t.Parallel()
  1517  	}
  1518  	if !testutil.DockerIsConnected(t) {
  1519  		t.Skip("Docker not connected")
  1520  	}
  1521  
  1522  	task := &structs.Task{
  1523  		Name:   "redis-demo",
  1524  		Driver: "docker",
  1525  		Config: map[string]interface{}{
  1526  			"image":   "busybox",
  1527  			"load":    "busybox.tar",
  1528  			"command": "/bin/echo",
  1529  			"args":    []string{"hello"},
  1530  		},
  1531  		Resources: &structs.Resources{
  1532  			MemoryMB: 256,
  1533  			CPU:      512,
  1534  		},
  1535  		LogConfig: &structs.LogConfig{
  1536  			MaxFiles:      10,
  1537  			MaxFileSizeMB: 10,
  1538  		},
  1539  	}
  1540  
  1541  	_, handle, cleanup := dockerSetup(t, task)
  1542  	defer cleanup()
  1543  
  1544  	// Update should be a no-op
  1545  	err := handle.Update(task)
  1546  	if err != nil {
  1547  		t.Fatalf("err: %v", err)
  1548  	}
  1549  
  1550  	select {
  1551  	case res := <-handle.WaitCh():
  1552  		if !res.Successful() {
  1553  			t.Fatalf("err: %v", res)
  1554  		}
  1555  
  1556  		time.Sleep(3 * time.Second)
  1557  
  1558  		// Ensure that the container isn't present
  1559  		_, err := client.InspectContainer(handle.containerID)
  1560  		if err == nil {
  1561  			t.Fatalf("expected to not get container")
  1562  		}
  1563  
  1564  	case <-time.After(time.Duration(tu.TestMultiplier()*5) * time.Second):
  1565  		t.Fatalf("timeout")
  1566  	}
  1567  }
  1568  
  1569  func TestDockerDriver_Stats(t *testing.T) {
  1570  	if !tu.IsTravis() {
  1571  		t.Parallel()
  1572  	}
  1573  	if !testutil.DockerIsConnected(t) {
  1574  		t.Skip("Docker not connected")
  1575  	}
  1576  
  1577  	task := &structs.Task{
  1578  		Name:   "sleep",
  1579  		Driver: "docker",
  1580  		Config: map[string]interface{}{
  1581  			"image":   "busybox",
  1582  			"load":    "busybox.tar",
  1583  			"command": "/bin/sleep",
  1584  			"args":    []string{"100"},
  1585  		},
  1586  		LogConfig: &structs.LogConfig{
  1587  			MaxFiles:      10,
  1588  			MaxFileSizeMB: 10,
  1589  		},
  1590  		Resources: basicResources,
  1591  	}
  1592  
  1593  	_, handle, cleanup := dockerSetup(t, task)
  1594  	defer cleanup()
  1595  
  1596  	waitForExist(t, client, handle)
  1597  
  1598  	go func() {
  1599  		time.Sleep(3 * time.Second)
  1600  		ru, err := handle.Stats()
  1601  		if err != nil {
  1602  			t.Fatalf("err: %v", err)
  1603  		}
  1604  		if ru.ResourceUsage == nil {
  1605  			handle.Kill()
  1606  			t.Fatalf("expected resource usage")
  1607  		}
  1608  		err = handle.Kill()
  1609  		if err != nil {
  1610  			t.Fatalf("err: %v", err)
  1611  		}
  1612  	}()
  1613  
  1614  	select {
  1615  	case res := <-handle.WaitCh():
  1616  		if res.Successful() {
  1617  			t.Fatalf("should err: %v", res)
  1618  		}
  1619  	case <-time.After(time.Duration(tu.TestMultiplier()*10) * time.Second):
  1620  		t.Fatalf("timeout")
  1621  	}
  1622  }
  1623  
  1624  func setupDockerVolumes(t *testing.T, cfg *config.Config, hostpath string) (*structs.Task, Driver, *ExecContext, string, func()) {
  1625  	if !testutil.DockerIsConnected(t) {
  1626  		t.Skip("Docker not connected")
  1627  	}
  1628  
  1629  	randfn := fmt.Sprintf("test-%d", rand.Int())
  1630  	hostfile := filepath.Join(hostpath, randfn)
  1631  	containerPath := "/mnt/vol"
  1632  	containerFile := filepath.Join(containerPath, randfn)
  1633  
  1634  	task := &structs.Task{
  1635  		Name:   "ls",
  1636  		Env:    map[string]string{"VOL_PATH": containerPath},
  1637  		Driver: "docker",
  1638  		Config: map[string]interface{}{
  1639  			"image":   "busybox",
  1640  			"load":    "busybox.tar",
  1641  			"command": "touch",
  1642  			"args":    []string{containerFile},
  1643  			"volumes": []string{fmt.Sprintf("%s:${VOL_PATH}", hostpath)},
  1644  		},
  1645  		LogConfig: &structs.LogConfig{
  1646  			MaxFiles:      10,
  1647  			MaxFileSizeMB: 10,
  1648  		},
  1649  		Resources: basicResources,
  1650  	}
  1651  
  1652  	// Build alloc and task directory structure
  1653  	allocDir := allocdir.NewAllocDir(testLogger(), filepath.Join(cfg.AllocDir, uuid.Generate()))
  1654  	if err := allocDir.Build(); err != nil {
  1655  		t.Fatalf("failed to build alloc dir: %v", err)
  1656  	}
  1657  	taskDir := allocDir.NewTaskDir(task.Name)
  1658  	if err := taskDir.Build(false, nil, cstructs.FSIsolationImage); err != nil {
  1659  		allocDir.Destroy()
  1660  		t.Fatalf("failed to build task dir: %v", err)
  1661  	}
  1662  	copyImage(t, taskDir, "busybox.tar")
  1663  
  1664  	// Setup driver
  1665  	alloc := mock.Alloc()
  1666  	logger := testLogger()
  1667  	emitter := func(m string, args ...interface{}) {
  1668  		logger.Printf("[EVENT] "+m, args...)
  1669  	}
  1670  	driverCtx := NewDriverContext(alloc.Job.Name, alloc.TaskGroup, task.Name, alloc.ID, cfg, cfg.Node, testLogger(), emitter)
  1671  	driver := NewDockerDriver(driverCtx)
  1672  
  1673  	// Setup execCtx
  1674  	envBuilder := env.NewBuilder(cfg.Node, alloc, task, cfg.Region)
  1675  	SetEnvvars(envBuilder, driver.FSIsolation(), taskDir, cfg)
  1676  	execCtx := NewExecContext(taskDir, envBuilder.Build())
  1677  
  1678  	// Setup cleanup function
  1679  	cleanup := func() {
  1680  		allocDir.Destroy()
  1681  		if filepath.IsAbs(hostpath) {
  1682  			os.RemoveAll(hostpath)
  1683  		}
  1684  	}
  1685  	return task, driver, execCtx, hostfile, cleanup
  1686  }
  1687  
  1688  func TestDockerDriver_VolumesDisabled(t *testing.T) {
  1689  	if !tu.IsTravis() {
  1690  		t.Parallel()
  1691  	}
  1692  	if !testutil.DockerIsConnected(t) {
  1693  		t.Skip("Docker not connected")
  1694  	}
  1695  
  1696  	cfg := testConfig(t)
  1697  	cfg.Options = map[string]string{
  1698  		dockerVolumesConfigOption: "false",
  1699  		"docker.cleanup.image":    "false",
  1700  	}
  1701  
  1702  	{
  1703  		tmpvol, err := ioutil.TempDir("", "nomadtest_docker_volumesdisabled")
  1704  		if err != nil {
  1705  			t.Fatalf("error creating temporary dir: %v", err)
  1706  		}
  1707  
  1708  		task, driver, execCtx, _, cleanup := setupDockerVolumes(t, cfg, tmpvol)
  1709  		defer cleanup()
  1710  
  1711  		_, err = driver.Prestart(execCtx, task)
  1712  		if err != nil {
  1713  			t.Fatalf("error in prestart: %v", err)
  1714  		}
  1715  		if _, err := driver.Start(execCtx, task); err == nil {
  1716  			t.Fatalf("Started driver successfully when volumes should have been disabled.")
  1717  		}
  1718  	}
  1719  
  1720  	// Relative paths should still be allowed
  1721  	{
  1722  		task, driver, execCtx, fn, cleanup := setupDockerVolumes(t, cfg, ".")
  1723  		defer cleanup()
  1724  
  1725  		_, err := driver.Prestart(execCtx, task)
  1726  		if err != nil {
  1727  			t.Fatalf("error in prestart: %v", err)
  1728  		}
  1729  		resp, err := driver.Start(execCtx, task)
  1730  		if err != nil {
  1731  			t.Fatalf("err: %v", err)
  1732  		}
  1733  		defer resp.Handle.Kill()
  1734  
  1735  		select {
  1736  		case res := <-resp.Handle.WaitCh():
  1737  			if !res.Successful() {
  1738  				t.Fatalf("unexpected err: %v", res)
  1739  			}
  1740  		case <-time.After(time.Duration(tu.TestMultiplier()*10) * time.Second):
  1741  			t.Fatalf("timeout")
  1742  		}
  1743  
  1744  		if _, err := ioutil.ReadFile(filepath.Join(execCtx.TaskDir.Dir, fn)); err != nil {
  1745  			t.Fatalf("unexpected error reading %s: %v", fn, err)
  1746  		}
  1747  	}
  1748  
  1749  	// Volume Drivers should be rejected (error)
  1750  	{
  1751  		task, driver, execCtx, _, cleanup := setupDockerVolumes(t, cfg, "fake_flocker_vol")
  1752  		defer cleanup()
  1753  		task.Config["volume_driver"] = "flocker"
  1754  
  1755  		if _, err := driver.Prestart(execCtx, task); err != nil {
  1756  			t.Fatalf("error in prestart: %v", err)
  1757  		}
  1758  		if _, err := driver.Start(execCtx, task); err == nil {
  1759  			t.Fatalf("Started driver successfully when volume drivers should have been disabled.")
  1760  		}
  1761  	}
  1762  
  1763  }
  1764  
  1765  func TestDockerDriver_VolumesEnabled(t *testing.T) {
  1766  	if !tu.IsTravis() {
  1767  		t.Parallel()
  1768  	}
  1769  	if !testutil.DockerIsConnected(t) {
  1770  		t.Skip("Docker not connected")
  1771  	}
  1772  
  1773  	cfg := testConfig(t)
  1774  
  1775  	tmpvol, err := ioutil.TempDir("", "nomadtest_docker_volumesenabled")
  1776  	if err != nil {
  1777  		t.Fatalf("error creating temporary dir: %v", err)
  1778  	}
  1779  
  1780  	// Evaluate symlinks so it works on MacOS
  1781  	tmpvol, err = filepath.EvalSymlinks(tmpvol)
  1782  	if err != nil {
  1783  		t.Fatalf("error evaluating symlinks: %v", err)
  1784  	}
  1785  
  1786  	task, driver, execCtx, hostpath, cleanup := setupDockerVolumes(t, cfg, tmpvol)
  1787  	defer cleanup()
  1788  
  1789  	_, err = driver.Prestart(execCtx, task)
  1790  	if err != nil {
  1791  		t.Fatalf("error in prestart: %v", err)
  1792  	}
  1793  	resp, err := driver.Start(execCtx, task)
  1794  	if err != nil {
  1795  		t.Fatalf("Failed to start docker driver: %v", err)
  1796  	}
  1797  	defer resp.Handle.Kill()
  1798  
  1799  	select {
  1800  	case res := <-resp.Handle.WaitCh():
  1801  		if !res.Successful() {
  1802  			t.Fatalf("unexpected err: %v", res)
  1803  		}
  1804  	case <-time.After(time.Duration(tu.TestMultiplier()*10) * time.Second):
  1805  		t.Fatalf("timeout")
  1806  	}
  1807  
  1808  	if _, err := ioutil.ReadFile(hostpath); err != nil {
  1809  		t.Fatalf("unexpected error reading %s: %v", hostpath, err)
  1810  	}
  1811  }
  1812  
  1813  func TestDockerDriver_Mounts(t *testing.T) {
  1814  	if !tu.IsTravis() {
  1815  		t.Parallel()
  1816  	}
  1817  	if !testutil.DockerIsConnected(t) {
  1818  		t.Skip("Docker not connected")
  1819  	}
  1820  
  1821  	goodMount := map[string]interface{}{
  1822  		"target": "/nomad",
  1823  		"volume_options": []interface{}{
  1824  			map[string]interface{}{
  1825  				"labels": []interface{}{
  1826  					map[string]string{"foo": "bar"},
  1827  				},
  1828  				"driver_config": []interface{}{
  1829  					map[string]interface{}{
  1830  						"name": "local",
  1831  						"options": []interface{}{
  1832  							map[string]interface{}{
  1833  								"foo": "bar",
  1834  							},
  1835  						},
  1836  					},
  1837  				},
  1838  			},
  1839  		},
  1840  		"readonly": true,
  1841  		"source":   "test",
  1842  	}
  1843  
  1844  	cases := []struct {
  1845  		Name   string
  1846  		Mounts []interface{}
  1847  		Error  string
  1848  	}{
  1849  		{
  1850  			Name:   "good-one",
  1851  			Error:  "",
  1852  			Mounts: []interface{}{goodMount},
  1853  		},
  1854  		{
  1855  			Name:   "good-many",
  1856  			Error:  "",
  1857  			Mounts: []interface{}{goodMount, goodMount, goodMount},
  1858  		},
  1859  		{
  1860  			Name:  "multiple volume options",
  1861  			Error: "Only one volume_options stanza allowed",
  1862  			Mounts: []interface{}{
  1863  				map[string]interface{}{
  1864  					"target": "/nomad",
  1865  					"volume_options": []interface{}{
  1866  						map[string]interface{}{
  1867  							"driver_config": []interface{}{
  1868  								map[string]interface{}{
  1869  									"name": "local",
  1870  								},
  1871  							},
  1872  						},
  1873  						map[string]interface{}{
  1874  							"driver_config": []interface{}{
  1875  								map[string]interface{}{
  1876  									"name": "local",
  1877  								},
  1878  							},
  1879  						},
  1880  					},
  1881  				},
  1882  			},
  1883  		},
  1884  		{
  1885  			Name:  "multiple driver configs",
  1886  			Error: "volume driver config may only be specified once",
  1887  			Mounts: []interface{}{
  1888  				map[string]interface{}{
  1889  					"target": "/nomad",
  1890  					"volume_options": []interface{}{
  1891  						map[string]interface{}{
  1892  							"driver_config": []interface{}{
  1893  								map[string]interface{}{
  1894  									"name": "local",
  1895  								},
  1896  								map[string]interface{}{
  1897  									"name": "local",
  1898  								},
  1899  							},
  1900  						},
  1901  					},
  1902  				},
  1903  			},
  1904  		},
  1905  		{
  1906  			Name:  "multiple volume labels",
  1907  			Error: "labels may only be",
  1908  			Mounts: []interface{}{
  1909  				map[string]interface{}{
  1910  					"target": "/nomad",
  1911  					"volume_options": []interface{}{
  1912  						map[string]interface{}{
  1913  							"labels": []interface{}{
  1914  								map[string]string{"foo": "bar"},
  1915  								map[string]string{"baz": "bam"},
  1916  							},
  1917  						},
  1918  					},
  1919  				},
  1920  			},
  1921  		},
  1922  		{
  1923  			Name:  "multiple driver options",
  1924  			Error: "driver options may only",
  1925  			Mounts: []interface{}{
  1926  				map[string]interface{}{
  1927  					"target": "/nomad",
  1928  					"volume_options": []interface{}{
  1929  						map[string]interface{}{
  1930  							"driver_config": []interface{}{
  1931  								map[string]interface{}{
  1932  									"name": "local",
  1933  									"options": []interface{}{
  1934  										map[string]interface{}{
  1935  											"foo": "bar",
  1936  										},
  1937  										map[string]interface{}{
  1938  											"bam": "bar",
  1939  										},
  1940  									},
  1941  								},
  1942  							},
  1943  						},
  1944  					},
  1945  				},
  1946  			},
  1947  		},
  1948  	}
  1949  
  1950  	task := &structs.Task{
  1951  		Name:   "redis-demo",
  1952  		Driver: "docker",
  1953  		Config: map[string]interface{}{
  1954  			"image":   "busybox",
  1955  			"load":    "busybox.tar",
  1956  			"command": "/bin/sleep",
  1957  			"args":    []string{"10000"},
  1958  		},
  1959  		Resources: &structs.Resources{
  1960  			MemoryMB: 256,
  1961  			CPU:      512,
  1962  		},
  1963  		LogConfig: &structs.LogConfig{
  1964  			MaxFiles:      10,
  1965  			MaxFileSizeMB: 10,
  1966  		},
  1967  	}
  1968  
  1969  	for _, c := range cases {
  1970  		t.Run(c.Name, func(t *testing.T) {
  1971  			// Build the task
  1972  			task.Config["mounts"] = c.Mounts
  1973  
  1974  			ctx := testDockerDriverContexts(t, task)
  1975  			driver := NewDockerDriver(ctx.DriverCtx)
  1976  			copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar")
  1977  			defer ctx.AllocDir.Destroy()
  1978  
  1979  			_, err := driver.Prestart(ctx.ExecCtx, task)
  1980  			if err == nil && c.Error != "" {
  1981  				t.Fatalf("expected error: %v", c.Error)
  1982  			} else if err != nil {
  1983  				if c.Error == "" {
  1984  					t.Fatalf("unexpected error in prestart: %v", err)
  1985  				} else if !strings.Contains(err.Error(), c.Error) {
  1986  					t.Fatalf("expected error %q; got %v", c.Error, err)
  1987  				}
  1988  			}
  1989  		})
  1990  	}
  1991  }
  1992  
  1993  // TestDockerDriver_Cleanup ensures Cleanup removes only downloaded images.
  1994  func TestDockerDriver_Cleanup(t *testing.T) {
  1995  	if !tu.IsTravis() {
  1996  		t.Parallel()
  1997  	}
  1998  	if !testutil.DockerIsConnected(t) {
  1999  		t.Skip("Docker not connected")
  2000  	}
  2001  
  2002  	imageName := "hello-world:latest"
  2003  	task := &structs.Task{
  2004  		Name:   "cleanup_test",
  2005  		Driver: "docker",
  2006  		Config: map[string]interface{}{
  2007  			"image": imageName,
  2008  		},
  2009  	}
  2010  	tctx := testDockerDriverContexts(t, task)
  2011  	defer tctx.AllocDir.Destroy()
  2012  
  2013  	// Run Prestart
  2014  	driver := NewDockerDriver(tctx.DriverCtx).(*DockerDriver)
  2015  	resp, err := driver.Prestart(tctx.ExecCtx, task)
  2016  	if err != nil {
  2017  		t.Fatalf("error in prestart: %v", err)
  2018  	}
  2019  	res := resp.CreatedResources
  2020  	if len(res.Resources) == 0 || len(res.Resources[dockerImageResKey]) == 0 {
  2021  		t.Fatalf("no created resources: %#v", res)
  2022  	}
  2023  
  2024  	// Cleanup
  2025  	rescopy := res.Copy()
  2026  	if err := driver.Cleanup(tctx.ExecCtx, rescopy); err != nil {
  2027  		t.Fatalf("Cleanup failed: %v", err)
  2028  	}
  2029  
  2030  	// Make sure rescopy is updated
  2031  	if len(rescopy.Resources) > 0 {
  2032  		t.Errorf("Cleanup should have cleared resource map: %#v", rescopy.Resources)
  2033  	}
  2034  
  2035  	// Ensure image was removed
  2036  	tu.WaitForResult(func() (bool, error) {
  2037  		if _, err := client.InspectImage(driver.driverConfig.ImageName); err == nil {
  2038  			return false, fmt.Errorf("image exists but should have been removed. Does another %v container exist?", imageName)
  2039  		}
  2040  
  2041  		return true, nil
  2042  	}, func(err error) {
  2043  		t.Fatalf("err: %v", err)
  2044  	})
  2045  
  2046  	// The image doesn't exist which shouldn't be an error when calling
  2047  	// Cleanup, so call it again to make sure.
  2048  	if err := driver.Cleanup(tctx.ExecCtx, res.Copy()); err != nil {
  2049  		t.Fatalf("Cleanup failed: %v", err)
  2050  	}
  2051  }
  2052  
  2053  func copyImage(t *testing.T, taskDir *allocdir.TaskDir, image string) {
  2054  	dst := filepath.Join(taskDir.LocalDir, image)
  2055  	copyFile(filepath.Join("./test-resources/docker", image), dst, t)
  2056  }
  2057  
  2058  func TestDockerDriver_AuthConfiguration(t *testing.T) {
  2059  	if !tu.IsTravis() {
  2060  		t.Parallel()
  2061  	}
  2062  	if !testutil.DockerIsConnected(t) {
  2063  		t.Skip("Docker not connected")
  2064  	}
  2065  
  2066  	path := "./test-resources/docker/auth.json"
  2067  	cases := []struct {
  2068  		Repo       string
  2069  		AuthConfig *docker.AuthConfiguration
  2070  	}{
  2071  		{
  2072  			Repo:       "lolwhat.com/what:1337",
  2073  			AuthConfig: nil,
  2074  		},
  2075  		{
  2076  			Repo: "redis:3.2",
  2077  			AuthConfig: &docker.AuthConfiguration{
  2078  				Username:      "test",
  2079  				Password:      "1234",
  2080  				Email:         "",
  2081  				ServerAddress: "https://index.docker.io/v1/",
  2082  			},
  2083  		},
  2084  		{
  2085  			Repo: "quay.io/redis:3.2",
  2086  			AuthConfig: &docker.AuthConfiguration{
  2087  				Username:      "test",
  2088  				Password:      "5678",
  2089  				Email:         "",
  2090  				ServerAddress: "quay.io",
  2091  			},
  2092  		},
  2093  		{
  2094  			Repo: "other.io/redis:3.2",
  2095  			AuthConfig: &docker.AuthConfiguration{
  2096  				Username:      "test",
  2097  				Password:      "abcd",
  2098  				Email:         "",
  2099  				ServerAddress: "https://other.io/v1/",
  2100  			},
  2101  		},
  2102  	}
  2103  
  2104  	for i, c := range cases {
  2105  		act, err := authFromDockerConfig(path)(c.Repo)
  2106  		if err != nil {
  2107  			t.Fatalf("Test %d failed: %v", i+1, err)
  2108  		}
  2109  
  2110  		if !reflect.DeepEqual(act, c.AuthConfig) {
  2111  			t.Fatalf("Test %d failed: Unexpected auth config: got %+v; want %+v", i+1, act, c.AuthConfig)
  2112  		}
  2113  	}
  2114  }
  2115  
  2116  func TestDockerDriver_OOMKilled(t *testing.T) {
  2117  	if !tu.IsTravis() {
  2118  		t.Parallel()
  2119  	}
  2120  	if !testutil.DockerIsConnected(t) {
  2121  		t.Skip("Docker not connected")
  2122  	}
  2123  
  2124  	task := &structs.Task{
  2125  		Name:   "oom-killed",
  2126  		Driver: "docker",
  2127  		Config: map[string]interface{}{
  2128  			"image":   "busybox",
  2129  			"load":    "busybox.tar",
  2130  			"command": "sh",
  2131  			// Incrementally creates a bigger and bigger variable.
  2132  			"args": []string{"-c", "x=a; while true; do eval x='$x$x'; done"},
  2133  		},
  2134  		LogConfig: &structs.LogConfig{
  2135  			MaxFiles:      10,
  2136  			MaxFileSizeMB: 10,
  2137  		},
  2138  		Resources: &structs.Resources{
  2139  			CPU:      250,
  2140  			MemoryMB: 10,
  2141  			DiskMB:   20,
  2142  			Networks: []*structs.NetworkResource{},
  2143  		},
  2144  	}
  2145  
  2146  	_, handle, cleanup := dockerSetup(t, task)
  2147  	defer cleanup()
  2148  
  2149  	select {
  2150  	case res := <-handle.WaitCh():
  2151  		if res.Successful() {
  2152  			t.Fatalf("expected error, but container exited successful")
  2153  		}
  2154  
  2155  		if res.Err.Error() != "OOM Killed" {
  2156  			t.Fatalf("not killed by OOM killer: %s", res.Err)
  2157  		}
  2158  
  2159  		t.Logf("Successfully killed by OOM killer")
  2160  
  2161  	case <-time.After(time.Duration(tu.TestMultiplier()*5) * time.Second):
  2162  		t.Fatalf("timeout")
  2163  	}
  2164  }
  2165  
  2166  func TestDockerDriver_Devices_IsInvalidConfig(t *testing.T) {
  2167  	if !tu.IsTravis() {
  2168  		t.Parallel()
  2169  	}
  2170  	if !testutil.DockerIsConnected(t) {
  2171  		t.Skip("Docker not connected")
  2172  	}
  2173  
  2174  	brokenConfigs := []interface{}{
  2175  		map[string]interface{}{
  2176  			"host_path": "",
  2177  		},
  2178  		map[string]interface{}{
  2179  			"host_path":          "/dev/sda1",
  2180  			"cgroup_permissions": "rxb",
  2181  		},
  2182  	}
  2183  
  2184  	test_cases := []struct {
  2185  		deviceConfig interface{}
  2186  		err          error
  2187  	}{
  2188  		{[]interface{}{brokenConfigs[0]}, fmt.Errorf("host path must be set in configuration for devices")},
  2189  		{[]interface{}{brokenConfigs[1]}, fmt.Errorf("invalid cgroup permission string: \"rxb\"")},
  2190  	}
  2191  
  2192  	for _, tc := range test_cases {
  2193  		task, _, _ := dockerTask(t)
  2194  		task.Config["devices"] = tc.deviceConfig
  2195  
  2196  		ctx := testDockerDriverContexts(t, task)
  2197  		driver := NewDockerDriver(ctx.DriverCtx)
  2198  		copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar")
  2199  		defer ctx.AllocDir.Destroy()
  2200  
  2201  		if _, err := driver.Prestart(ctx.ExecCtx, task); err == nil || err.Error() != tc.err.Error() {
  2202  			t.Fatalf("error expected in prestart, got %v, expected %v", err, tc.err)
  2203  		}
  2204  	}
  2205  }
  2206  
  2207  func TestDockerDriver_Device_Success(t *testing.T) {
  2208  	if !tu.IsTravis() {
  2209  		t.Parallel()
  2210  	}
  2211  	if !testutil.DockerIsConnected(t) {
  2212  		t.Skip("Docker not connected")
  2213  	}
  2214  
  2215  	if runtime.GOOS != "linux" {
  2216  		t.Skip("test device mounts only on linux")
  2217  	}
  2218  
  2219  	hostPath := "/dev/random"
  2220  	containerPath := "/dev/myrandom"
  2221  	perms := "rwm"
  2222  
  2223  	expectedDevice := docker.Device{
  2224  		PathOnHost:        hostPath,
  2225  		PathInContainer:   containerPath,
  2226  		CgroupPermissions: perms,
  2227  	}
  2228  	config := map[string]interface{}{
  2229  		"host_path":      hostPath,
  2230  		"container_path": containerPath,
  2231  	}
  2232  
  2233  	task, _, _ := dockerTask(t)
  2234  	task.Config["devices"] = []interface{}{config}
  2235  
  2236  	client, handle, cleanup := dockerSetup(t, task)
  2237  	defer cleanup()
  2238  
  2239  	waitForExist(t, client, handle)
  2240  
  2241  	container, err := client.InspectContainer(handle.ContainerID())
  2242  	if err != nil {
  2243  		t.Fatalf("err: %v", err)
  2244  	}
  2245  
  2246  	assert.NotEmpty(t, container.HostConfig.Devices, "Expected one device")
  2247  	assert.Equal(t, expectedDevice, container.HostConfig.Devices[0], "Incorrect device ")
  2248  }
  2249  
  2250  func TestDockerDriver_Entrypoint(t *testing.T) {
  2251  	if !tu.IsTravis() {
  2252  		t.Parallel()
  2253  	}
  2254  	if !testutil.DockerIsConnected(t) {
  2255  		t.Skip("Docker not connected")
  2256  	}
  2257  
  2258  	entrypoint := []string{"/bin/sh", "-c"}
  2259  	task, _, _ := dockerTask(t)
  2260  	task.Config["entrypoint"] = entrypoint
  2261  
  2262  	client, handle, cleanup := dockerSetup(t, task)
  2263  	defer cleanup()
  2264  
  2265  	waitForExist(t, client, handle)
  2266  
  2267  	container, err := client.InspectContainer(handle.ContainerID())
  2268  	if err != nil {
  2269  		t.Fatalf("err: %v", err)
  2270  	}
  2271  
  2272  	require.Len(t, container.Config.Entrypoint, 2, "Expected one entrypoint")
  2273  	require.Equal(t, entrypoint, container.Config.Entrypoint, "Incorrect entrypoint ")
  2274  }
  2275  
  2276  func TestDockerDriver_Kill(t *testing.T) {
  2277  	assert := assert.New(t)
  2278  	if !tu.IsTravis() {
  2279  		t.Parallel()
  2280  	}
  2281  	if !testutil.DockerIsConnected(t) {
  2282  		t.Skip("Docker not connected")
  2283  	}
  2284  
  2285  	// Tasks started with a signal that is not supported should not error
  2286  	task := &structs.Task{
  2287  		Name:       "nc-demo",
  2288  		Driver:     "docker",
  2289  		KillSignal: "SIGKILL",
  2290  		Config: map[string]interface{}{
  2291  			"load":    "busybox.tar",
  2292  			"image":   "busybox",
  2293  			"command": "/bin/nc",
  2294  			"args":    []string{"-l", "127.0.0.1", "-p", "0"},
  2295  		},
  2296  		LogConfig: &structs.LogConfig{
  2297  			MaxFiles:      10,
  2298  			MaxFileSizeMB: 10,
  2299  		},
  2300  		Resources: basicResources,
  2301  	}
  2302  
  2303  	ctx := testDockerDriverContexts(t, task)
  2304  	defer ctx.AllocDir.Destroy()
  2305  	d := NewDockerDriver(ctx.DriverCtx)
  2306  	copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar")
  2307  
  2308  	_, err := d.Prestart(ctx.ExecCtx, task)
  2309  	if err != nil {
  2310  		t.Fatalf("error in prestart: %v", err)
  2311  	}
  2312  
  2313  	resp, err := d.Start(ctx.ExecCtx, task)
  2314  	assert.Nil(err)
  2315  	assert.NotNil(resp.Handle)
  2316  
  2317  	handle := resp.Handle.(*DockerHandle)
  2318  	waitForExist(t, client, handle)
  2319  	err = handle.Kill()
  2320  	assert.Nil(err)
  2321  }
  2322  
  2323  func TestDockerDriver_ReadonlyRootfs(t *testing.T) {
  2324  	if !tu.IsTravis() {
  2325  		t.Parallel()
  2326  	}
  2327  	if !testutil.DockerIsConnected(t) {
  2328  		t.Skip("Docker not connected")
  2329  	}
  2330  
  2331  	task, _, _ := dockerTask(t)
  2332  	task.Config["readonly_rootfs"] = true
  2333  
  2334  	client, handle, cleanup := dockerSetup(t, task)
  2335  	defer cleanup()
  2336  
  2337  	waitForExist(t, client, handle)
  2338  
  2339  	container, err := client.InspectContainer(handle.ContainerID())
  2340  	assert.Nil(t, err, "Error inspecting container: %v", err)
  2341  
  2342  	assert.True(t, container.HostConfig.ReadonlyRootfs, "ReadonlyRootfs option not set")
  2343  }
  2344  
  2345  // fakeDockerClient can be used in places that accept an interface for the
  2346  // docker client such as createContainer.
  2347  type fakeDockerClient struct{}
  2348  
  2349  func (fakeDockerClient) CreateContainer(docker.CreateContainerOptions) (*docker.Container, error) {
  2350  	return nil, fmt.Errorf("volume is attached on another node")
  2351  }
  2352  func (fakeDockerClient) InspectContainer(id string) (*docker.Container, error) {
  2353  	panic("not implemented")
  2354  }
  2355  func (fakeDockerClient) ListContainers(docker.ListContainersOptions) ([]docker.APIContainers, error) {
  2356  	panic("not implemented")
  2357  }
  2358  func (fakeDockerClient) RemoveContainer(opts docker.RemoveContainerOptions) error {
  2359  	panic("not implemented")
  2360  }
  2361  
  2362  // TestDockerDriver_VolumeError asserts volume related errors when creating a
  2363  // container are recoverable.
  2364  func TestDockerDriver_VolumeError(t *testing.T) {
  2365  	if !tu.IsTravis() {
  2366  		t.Parallel()
  2367  	}
  2368  
  2369  	// setup
  2370  	task, _, _ := dockerTask(t)
  2371  	tctx := testDockerDriverContexts(t, task)
  2372  	driver := NewDockerDriver(tctx.DriverCtx).(*DockerDriver)
  2373  	driver.driverConfig = &DockerDriverConfig{ImageName: "test"}
  2374  
  2375  	// assert volume error is recoverable
  2376  	_, err := driver.createContainer(fakeDockerClient{}, docker.CreateContainerOptions{})
  2377  	require.True(t, structs.IsRecoverable(err))
  2378  }
  2379  
  2380  func TestDockerDriver_AdvertiseIPv6Address(t *testing.T) {
  2381  	if !tu.IsTravis() {
  2382  		t.Parallel()
  2383  	}
  2384  	if !testutil.DockerIsConnected(t) {
  2385  		t.Skip("Docker not connected")
  2386  	}
  2387  
  2388  	expectedPrefix := "2001:db8:1::242:ac11"
  2389  	expectedAdvertise := true
  2390  	task := &structs.Task{
  2391  		Name:   "nc-demo",
  2392  		Driver: "docker",
  2393  		Config: map[string]interface{}{
  2394  			"image":   "busybox",
  2395  			"load":    "busybox.tar",
  2396  			"command": "/bin/nc",
  2397  			"args":    []string{"-l", "127.0.0.1", "-p", "0"},
  2398  			"advertise_ipv6_address": expectedAdvertise,
  2399  		},
  2400  		Resources: &structs.Resources{
  2401  			MemoryMB: 256,
  2402  			CPU:      512,
  2403  		},
  2404  		LogConfig: &structs.LogConfig{
  2405  			MaxFiles:      10,
  2406  			MaxFileSizeMB: 10,
  2407  		},
  2408  	}
  2409  
  2410  	client := newTestDockerClient(t)
  2411  
  2412  	// Make sure IPv6 is enabled
  2413  	net, err := client.NetworkInfo("bridge")
  2414  	if err != nil {
  2415  		t.Skip("error retrieving bridge network information, skipping")
  2416  	}
  2417  	if net == nil || !net.EnableIPv6 {
  2418  		t.Skip("IPv6 not enabled on bridge network, skipping")
  2419  	}
  2420  
  2421  	tctx := testDockerDriverContexts(t, task)
  2422  	driver := NewDockerDriver(tctx.DriverCtx)
  2423  	copyImage(t, tctx.ExecCtx.TaskDir, "busybox.tar")
  2424  	defer tctx.AllocDir.Destroy()
  2425  
  2426  	presp, err := driver.Prestart(tctx.ExecCtx, task)
  2427  	defer driver.Cleanup(tctx.ExecCtx, presp.CreatedResources)
  2428  	if err != nil {
  2429  		t.Fatalf("Error in prestart: %v", err)
  2430  	}
  2431  
  2432  	sresp, err := driver.Start(tctx.ExecCtx, task)
  2433  	if err != nil {
  2434  		t.Fatalf("Error in start: %v", err)
  2435  	}
  2436  
  2437  	if sresp.Handle == nil {
  2438  		t.Fatalf("handle is nil\nStack\n%s", debug.Stack())
  2439  	}
  2440  
  2441  	assert.Equal(t, expectedAdvertise, sresp.Network.AutoAdvertise, "Wrong autoadvertise. Expect: %s, got: %s", expectedAdvertise, sresp.Network.AutoAdvertise)
  2442  
  2443  	if !strings.HasPrefix(sresp.Network.IP, expectedPrefix) {
  2444  		t.Fatalf("Got IP address %q want ip address with prefix %q", sresp.Network.IP, expectedPrefix)
  2445  	}
  2446  
  2447  	defer sresp.Handle.Kill()
  2448  	handle := sresp.Handle.(*DockerHandle)
  2449  
  2450  	waitForExist(t, client, handle)
  2451  
  2452  	container, err := client.InspectContainer(handle.ContainerID())
  2453  	if err != nil {
  2454  		t.Fatalf("Error inspecting container: %v", err)
  2455  	}
  2456  
  2457  	if !strings.HasPrefix(container.NetworkSettings.GlobalIPv6Address, expectedPrefix) {
  2458  		t.Fatalf("Got GlobalIPv6address %s want GlobalIPv6address with prefix %s", expectedPrefix, container.NetworkSettings.GlobalIPv6Address)
  2459  	}
  2460  }