github.com/kardianos/nomad@v0.1.3-0.20151022182107-b13df73ee850/client/driver/docker_test.go (about)

     1  package driver
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os/exec"
     7  	"path/filepath"
     8  	"reflect"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/hashicorp/nomad/client/config"
    13  	"github.com/hashicorp/nomad/client/driver/environment"
    14  	"github.com/hashicorp/nomad/nomad/structs"
    15  )
    16  
    17  func testDockerDriverContext(task string) *DriverContext {
    18  	cfg := testConfig()
    19  	cfg.DevMode = true
    20  	return NewDriverContext(task, cfg, cfg.Node, testLogger())
    21  }
    22  
    23  // dockerLocated looks to see whether docker is available on this system before
    24  // we try to run tests. We'll keep it simple and just check for the CLI.
    25  func dockerLocated() bool {
    26  	_, err := exec.Command("docker", "-v").CombinedOutput()
    27  	return err == nil
    28  }
    29  
    30  func TestDockerDriver_Handle(t *testing.T) {
    31  	h := &dockerHandle{
    32  		imageID:     "imageid",
    33  		containerID: "containerid",
    34  		doneCh:      make(chan struct{}),
    35  		waitCh:      make(chan error, 1),
    36  	}
    37  
    38  	actual := h.ID()
    39  	expected := `DOCKER:{"ImageID":"imageid","ContainerID":"containerid"}`
    40  	if actual != expected {
    41  		t.Errorf("Expected `%s`, found `%s`", expected, actual)
    42  	}
    43  }
    44  
    45  // The fingerprinter test should always pass, even if Docker is not installed.
    46  func TestDockerDriver_Fingerprint(t *testing.T) {
    47  	d := NewDockerDriver(testDockerDriverContext(""))
    48  	node := &structs.Node{
    49  		Attributes: make(map[string]string),
    50  	}
    51  	apply, err := d.Fingerprint(&config.Config{}, node)
    52  	if err != nil {
    53  		t.Fatalf("err: %v", err)
    54  	}
    55  	if apply != dockerLocated() {
    56  		t.Fatalf("Fingerprinter should detect Docker when it is installed")
    57  	}
    58  	if node.Attributes["driver.docker"] != "1" {
    59  		t.Log("Docker not found. The remainder of the docker tests will be skipped.")
    60  	}
    61  	t.Logf("Found docker version %s", node.Attributes["driver.docker.version"])
    62  }
    63  
    64  func TestDockerDriver_StartOpen_Wait(t *testing.T) {
    65  	if !dockerLocated() {
    66  		t.SkipNow()
    67  	}
    68  
    69  	task := &structs.Task{
    70  		Name: "redis-demo",
    71  		Config: map[string]string{
    72  			"image": "redis",
    73  		},
    74  		Resources: basicResources,
    75  	}
    76  
    77  	driverCtx := testDockerDriverContext(task.Name)
    78  	ctx := testDriverExecContext(task, driverCtx)
    79  	defer ctx.AllocDir.Destroy()
    80  	d := NewDockerDriver(driverCtx)
    81  
    82  	handle, err := d.Start(ctx, task)
    83  	if err != nil {
    84  		t.Fatalf("err: %v", err)
    85  	}
    86  	if handle == nil {
    87  		t.Fatalf("missing handle")
    88  	}
    89  	defer handle.Kill()
    90  
    91  	// Attempt to open
    92  	handle2, err := d.Open(ctx, handle.ID())
    93  	if err != nil {
    94  		t.Fatalf("err: %v", err)
    95  	}
    96  	if handle2 == nil {
    97  		t.Fatalf("missing handle")
    98  	}
    99  }
   100  
   101  func TestDockerDriver_Start_Wait(t *testing.T) {
   102  	if !dockerLocated() {
   103  		t.SkipNow()
   104  	}
   105  
   106  	task := &structs.Task{
   107  		Name: "redis-demo",
   108  		Config: map[string]string{
   109  			"image":   "redis",
   110  			"command": "redis-server",
   111  			"args":    "-v",
   112  		},
   113  		Resources: &structs.Resources{
   114  			MemoryMB: 256,
   115  			CPU:      512,
   116  		},
   117  	}
   118  
   119  	driverCtx := testDockerDriverContext(task.Name)
   120  	ctx := testDriverExecContext(task, driverCtx)
   121  	defer ctx.AllocDir.Destroy()
   122  	d := NewDockerDriver(driverCtx)
   123  
   124  	handle, err := d.Start(ctx, task)
   125  	if err != nil {
   126  		t.Fatalf("err: %v", err)
   127  	}
   128  	if handle == nil {
   129  		t.Fatalf("missing handle")
   130  	}
   131  	defer handle.Kill()
   132  
   133  	// Update should be a no-op
   134  	err = handle.Update(task)
   135  	if err != nil {
   136  		t.Fatalf("err: %v", err)
   137  	}
   138  
   139  	select {
   140  	case err := <-handle.WaitCh():
   141  		if err != nil {
   142  			t.Fatalf("err: %v", err)
   143  		}
   144  	case <-time.After(5 * time.Second):
   145  		t.Fatalf("timeout")
   146  	}
   147  }
   148  
   149  func TestDockerDriver_Start_Wait_AllocDir(t *testing.T) {
   150  	if !dockerLocated() {
   151  		t.SkipNow()
   152  	}
   153  
   154  	exp := []byte{'w', 'i', 'n'}
   155  	file := "output.txt"
   156  	task := &structs.Task{
   157  		Name: "redis-demo",
   158  		Config: map[string]string{
   159  			"image":   "redis",
   160  			"command": "/bin/bash",
   161  			"args":    fmt.Sprintf(`-c "sleep 1; echo -n %s > $%s/%s"`, string(exp), environment.AllocDir, file),
   162  		},
   163  		Resources: &structs.Resources{
   164  			MemoryMB: 256,
   165  			CPU:      512,
   166  		},
   167  	}
   168  
   169  	driverCtx := testDockerDriverContext(task.Name)
   170  	ctx := testDriverExecContext(task, driverCtx)
   171  	defer ctx.AllocDir.Destroy()
   172  	d := NewDockerDriver(driverCtx)
   173  
   174  	handle, err := d.Start(ctx, task)
   175  	if err != nil {
   176  		t.Fatalf("err: %v", err)
   177  	}
   178  	if handle == nil {
   179  		t.Fatalf("missing handle")
   180  	}
   181  	defer handle.Kill()
   182  
   183  	select {
   184  	case err := <-handle.WaitCh():
   185  		if err != nil {
   186  			t.Fatalf("err: %v", err)
   187  		}
   188  	case <-time.After(5 * time.Second):
   189  		t.Fatalf("timeout")
   190  	}
   191  
   192  	// Check that data was written to the shared alloc directory.
   193  	outputFile := filepath.Join(ctx.AllocDir.SharedDir, file)
   194  	act, err := ioutil.ReadFile(outputFile)
   195  	if err != nil {
   196  		t.Fatalf("Couldn't read expected output: %v", err)
   197  	}
   198  
   199  	if !reflect.DeepEqual(act, exp) {
   200  		t.Fatalf("Command outputted %v; want %v", act, exp)
   201  	}
   202  }
   203  
   204  func TestDockerDriver_Start_Kill_Wait(t *testing.T) {
   205  	if !dockerLocated() {
   206  		t.SkipNow()
   207  	}
   208  
   209  	task := &structs.Task{
   210  		Name: "redis-demo",
   211  		Config: map[string]string{
   212  			"image":   "redis",
   213  			"command": "/bin/sleep",
   214  			"args":    "10",
   215  		},
   216  		Resources: basicResources,
   217  	}
   218  
   219  	driverCtx := testDockerDriverContext(task.Name)
   220  	ctx := testDriverExecContext(task, driverCtx)
   221  	defer ctx.AllocDir.Destroy()
   222  	d := NewDockerDriver(driverCtx)
   223  
   224  	handle, err := d.Start(ctx, task)
   225  	if err != nil {
   226  		t.Fatalf("err: %v", err)
   227  	}
   228  	if handle == nil {
   229  		t.Fatalf("missing handle")
   230  	}
   231  	defer handle.Kill()
   232  
   233  	go func() {
   234  		time.Sleep(100 * time.Millisecond)
   235  		err := handle.Kill()
   236  		if err != nil {
   237  			t.Fatalf("err: %v", err)
   238  		}
   239  	}()
   240  
   241  	select {
   242  	case err := <-handle.WaitCh():
   243  		if err == nil {
   244  			t.Fatalf("should err: %v", err)
   245  		}
   246  	case <-time.After(10 * time.Second):
   247  		t.Fatalf("timeout")
   248  	}
   249  }
   250  
   251  func taskTemplate() *structs.Task {
   252  	return &structs.Task{
   253  		Name: "redis-demo",
   254  		Config: map[string]string{
   255  			"image": "redis",
   256  		},
   257  		Resources: &structs.Resources{
   258  			MemoryMB: 256,
   259  			CPU:      512,
   260  			Networks: []*structs.NetworkResource{
   261  				&structs.NetworkResource{
   262  					IP:            "127.0.0.1",
   263  					ReservedPorts: []int{11110},
   264  					DynamicPorts:  []string{"REDIS"},
   265  				},
   266  			},
   267  		},
   268  	}
   269  }
   270  
   271  func TestDocker_StartN(t *testing.T) {
   272  	if !dockerLocated() {
   273  		t.SkipNow()
   274  	}
   275  
   276  	task1 := taskTemplate()
   277  	task1.Resources.Networks[0].ReservedPorts[0] = 11111
   278  
   279  	task2 := taskTemplate()
   280  	task2.Resources.Networks[0].ReservedPorts[0] = 22222
   281  
   282  	task3 := taskTemplate()
   283  	task3.Resources.Networks[0].ReservedPorts[0] = 33333
   284  
   285  	taskList := []*structs.Task{task1, task2, task3}
   286  
   287  	handles := make([]DriverHandle, len(taskList))
   288  
   289  	t.Logf("==> Starting %d tasks", len(taskList))
   290  
   291  	// Let's spin up a bunch of things
   292  	var err error
   293  	for idx, task := range taskList {
   294  		driverCtx := testDockerDriverContext(task.Name)
   295  		ctx := testDriverExecContext(task, driverCtx)
   296  		defer ctx.AllocDir.Destroy()
   297  		d := NewDockerDriver(driverCtx)
   298  
   299  		handles[idx], err = d.Start(ctx, task)
   300  		if err != nil {
   301  			t.Errorf("Failed starting task #%d: %s", idx+1, err)
   302  		}
   303  	}
   304  
   305  	t.Log("==> All tasks are started. Terminating...")
   306  
   307  	for idx, handle := range handles {
   308  		if handle == nil {
   309  			t.Errorf("Bad handle for task #%d", idx+1)
   310  			continue
   311  		}
   312  
   313  		err := handle.Kill()
   314  		if err != nil {
   315  			t.Errorf("Failed stopping task #%d: %s", idx+1, err)
   316  		}
   317  	}
   318  
   319  	t.Log("==> Test complete!")
   320  }
   321  
   322  func TestDocker_StartNVersions(t *testing.T) {
   323  	if !dockerLocated() {
   324  		t.SkipNow()
   325  	}
   326  
   327  	task1 := taskTemplate()
   328  	task1.Config["image"] = "redis"
   329  	task1.Resources.Networks[0].ReservedPorts[0] = 11111
   330  
   331  	task2 := taskTemplate()
   332  	task2.Config["image"] = "redis:latest"
   333  	task2.Resources.Networks[0].ReservedPorts[0] = 22222
   334  
   335  	task3 := taskTemplate()
   336  	task3.Config["image"] = "redis:3.0"
   337  	task3.Resources.Networks[0].ReservedPorts[0] = 33333
   338  
   339  	taskList := []*structs.Task{task1, task2, task3}
   340  
   341  	handles := make([]DriverHandle, len(taskList))
   342  
   343  	t.Logf("==> Starting %d tasks", len(taskList))
   344  
   345  	// Let's spin up a bunch of things
   346  	var err error
   347  	for idx, task := range taskList {
   348  		driverCtx := testDockerDriverContext(task.Name)
   349  		ctx := testDriverExecContext(task, driverCtx)
   350  		defer ctx.AllocDir.Destroy()
   351  		d := NewDockerDriver(driverCtx)
   352  
   353  		handles[idx], err = d.Start(ctx, task)
   354  		if err != nil {
   355  			t.Errorf("Failed starting task #%d: %s", idx+1, err)
   356  		}
   357  	}
   358  
   359  	t.Log("==> All tasks are started. Terminating...")
   360  
   361  	for idx, handle := range handles {
   362  		if handle == nil {
   363  			t.Errorf("Bad handle for task #%d", idx+1)
   364  			continue
   365  		}
   366  
   367  		err := handle.Kill()
   368  		if err != nil {
   369  			t.Errorf("Failed stopping task #%d: %s", idx+1, err)
   370  		}
   371  	}
   372  
   373  	t.Log("==> Test complete!")
   374  }
   375  
   376  func TestDockerHostNet(t *testing.T) {
   377  	if !dockerLocated() {
   378  		t.SkipNow()
   379  	}
   380  
   381  	task := &structs.Task{
   382  		Name: "redis-demo",
   383  		Config: map[string]string{
   384  			"image":        "redis",
   385  			"network_mode": "host",
   386  		},
   387  		Resources: &structs.Resources{
   388  			MemoryMB: 256,
   389  			CPU:      512,
   390  		},
   391  	}
   392  	driverCtx := testDockerDriverContext(task.Name)
   393  	ctx := testDriverExecContext(task, driverCtx)
   394  	defer ctx.AllocDir.Destroy()
   395  	d := NewDockerDriver(driverCtx)
   396  
   397  	handle, err := d.Start(ctx, task)
   398  	if err != nil {
   399  		t.Fatalf("err: %v", err)
   400  	}
   401  	if handle == nil {
   402  		t.Fatalf("missing handle")
   403  	}
   404  	defer handle.Kill()
   405  }