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 }