github.com/StackPointCloud/packer@v0.10.2-0.20180716202532-b28098e0f79b/provisioner/ansible-local/provisioner_test.go (about) 1 package ansiblelocal 2 3 import ( 4 "io/ioutil" 5 "os" 6 "path/filepath" 7 "strings" 8 "testing" 9 10 "fmt" 11 "github.com/hashicorp/packer/builder/docker" 12 "github.com/hashicorp/packer/packer" 13 "github.com/hashicorp/packer/provisioner/file" 14 "github.com/hashicorp/packer/template" 15 "os/exec" 16 ) 17 18 func TestProvisioner_Impl(t *testing.T) { 19 var raw interface{} 20 raw = &Provisioner{} 21 if _, ok := raw.(packer.Provisioner); !ok { 22 t.Fatalf("must be a Provisioner") 23 } 24 } 25 26 func TestProvisionerPrepare_Defaults(t *testing.T) { 27 var p Provisioner 28 config := testConfig() 29 30 playbook_file, err := ioutil.TempFile("", "playbook") 31 if err != nil { 32 t.Fatalf("err: %s", err) 33 } 34 defer os.Remove(playbook_file.Name()) 35 36 config["playbook_file"] = playbook_file.Name() 37 err = p.Prepare(config) 38 if err != nil { 39 t.Fatalf("err: %s", err) 40 } 41 42 if !strings.HasPrefix(filepath.ToSlash(p.config.StagingDir), DefaultStagingDir) { 43 t.Fatalf("unexpected staging dir %s, expected %s", 44 p.config.StagingDir, DefaultStagingDir) 45 } 46 } 47 48 func TestProvisionerPrepare_PlaybookFile(t *testing.T) { 49 var p Provisioner 50 config := testConfig() 51 52 err := p.Prepare(config) 53 if err == nil { 54 t.Fatal("should have error") 55 } 56 57 config["playbook_file"] = "" 58 err = p.Prepare(config) 59 if err == nil { 60 t.Fatal("should have error") 61 } 62 63 playbook_file, err := ioutil.TempFile("", "playbook") 64 if err != nil { 65 t.Fatalf("err: %s", err) 66 } 67 defer os.Remove(playbook_file.Name()) 68 69 config["playbook_file"] = playbook_file.Name() 70 err = p.Prepare(config) 71 if err != nil { 72 t.Fatalf("err: %s", err) 73 } 74 } 75 76 func TestProvisionerPrepare_PlaybookFiles(t *testing.T) { 77 var p Provisioner 78 config := testConfig() 79 80 err := p.Prepare(config) 81 if err == nil { 82 t.Fatal("should have error") 83 } 84 85 config["playbook_file"] = "" 86 config["playbook_files"] = []string{} 87 err = p.Prepare(config) 88 if err == nil { 89 t.Fatal("should have error") 90 } 91 92 playbook_file, err := ioutil.TempFile("", "playbook") 93 if err != nil { 94 t.Fatalf("err: %s", err) 95 } 96 defer os.Remove(playbook_file.Name()) 97 98 config["playbook_file"] = playbook_file.Name() 99 config["playbook_files"] = []string{"some_other_file"} 100 err = p.Prepare(config) 101 if err == nil { 102 t.Fatal("should have error") 103 } 104 105 p = Provisioner{} 106 config["playbook_file"] = playbook_file.Name() 107 config["playbook_files"] = []string{} 108 err = p.Prepare(config) 109 if err != nil { 110 t.Fatalf("err: %s", err) 111 } 112 113 config["playbook_file"] = "" 114 config["playbook_files"] = []string{playbook_file.Name()} 115 err = p.Prepare(config) 116 if err != nil { 117 t.Fatalf("err: %s", err) 118 } 119 } 120 121 func TestProvisionerProvision_PlaybookFiles(t *testing.T) { 122 var p Provisioner 123 config := testConfig() 124 125 playbooks := createTempFiles("", 3) 126 defer removeFiles(playbooks...) 127 128 config["playbook_files"] = playbooks 129 err := p.Prepare(config) 130 if err != nil { 131 t.Fatalf("err: %s", err) 132 } 133 134 comm := &communicatorMock{} 135 if err := p.Provision(&uiStub{}, comm); err != nil { 136 t.Fatalf("err: %s", err) 137 } 138 139 assertPlaybooksUploaded(comm, playbooks) 140 assertPlaybooksExecuted(comm, playbooks) 141 } 142 143 func TestProvisionerProvision_PlaybookFilesWithPlaybookDir(t *testing.T) { 144 var p Provisioner 145 config := testConfig() 146 147 playbook_dir, err := ioutil.TempDir("", "") 148 if err != nil { 149 t.Fatalf("Failed to create playbook_dir: %s", err) 150 } 151 defer os.RemoveAll(playbook_dir) 152 playbooks := createTempFiles(playbook_dir, 3) 153 154 playbookNames := make([]string, 0, len(playbooks)) 155 playbooksInPlaybookDir := make([]string, 0, len(playbooks)) 156 for _, playbook := range playbooks { 157 playbooksInPlaybookDir = append(playbooksInPlaybookDir, strings.TrimPrefix(playbook, playbook_dir)) 158 playbookNames = append(playbookNames, filepath.Base(playbook)) 159 } 160 161 config["playbook_files"] = playbooks 162 config["playbook_dir"] = playbook_dir 163 err = p.Prepare(config) 164 if err != nil { 165 t.Fatalf("err: %s", err) 166 } 167 168 comm := &communicatorMock{} 169 if err := p.Provision(&uiStub{}, comm); err != nil { 170 t.Fatalf("err: %s", err) 171 } 172 173 assertPlaybooksNotUploaded(comm, playbookNames) 174 assertPlaybooksExecuted(comm, playbooksInPlaybookDir) 175 } 176 177 func TestProvisionerPrepare_InventoryFile(t *testing.T) { 178 var p Provisioner 179 config := testConfig() 180 181 err := p.Prepare(config) 182 if err == nil { 183 t.Fatal("should have error") 184 } 185 186 config["playbook_file"] = "" 187 err = p.Prepare(config) 188 if err == nil { 189 t.Fatal("should have error") 190 } 191 192 playbook_file, err := ioutil.TempFile("", "playbook") 193 if err != nil { 194 t.Fatalf("err: %s", err) 195 } 196 defer os.Remove(playbook_file.Name()) 197 198 config["playbook_file"] = playbook_file.Name() 199 err = p.Prepare(config) 200 if err != nil { 201 t.Fatalf("err: %s", err) 202 } 203 204 inventory_file, err := ioutil.TempFile("", "inventory") 205 if err != nil { 206 t.Fatalf("err: %s", err) 207 } 208 defer os.Remove(inventory_file.Name()) 209 210 config["inventory_file"] = inventory_file.Name() 211 err = p.Prepare(config) 212 if err != nil { 213 t.Fatalf("err: %s", err) 214 } 215 } 216 217 func TestProvisionerPrepare_Dirs(t *testing.T) { 218 var p Provisioner 219 config := testConfig() 220 221 err := p.Prepare(config) 222 if err == nil { 223 t.Fatal("should have error") 224 } 225 226 config["playbook_file"] = "" 227 err = p.Prepare(config) 228 if err == nil { 229 t.Fatal("should have error") 230 } 231 232 playbook_file, err := ioutil.TempFile("", "playbook") 233 if err != nil { 234 t.Fatalf("err: %s", err) 235 } 236 defer os.Remove(playbook_file.Name()) 237 238 config["playbook_file"] = playbook_file.Name() 239 err = p.Prepare(config) 240 if err != nil { 241 t.Fatalf("err: %s", err) 242 } 243 244 config["playbook_paths"] = []string{playbook_file.Name()} 245 err = p.Prepare(config) 246 if err == nil { 247 t.Fatal("should error if playbook paths is not a dir") 248 } 249 250 config["playbook_paths"] = []string{os.TempDir()} 251 err = p.Prepare(config) 252 if err != nil { 253 t.Fatalf("err: %s", err) 254 } 255 256 config["role_paths"] = []string{playbook_file.Name()} 257 err = p.Prepare(config) 258 if err == nil { 259 t.Fatal("should error if role paths is not a dir") 260 } 261 262 config["role_paths"] = []string{os.TempDir()} 263 err = p.Prepare(config) 264 if err != nil { 265 t.Fatalf("err: %s", err) 266 } 267 268 config["group_vars"] = playbook_file.Name() 269 err = p.Prepare(config) 270 if err == nil { 271 t.Fatalf("should error if group_vars path is not a dir") 272 } 273 274 config["group_vars"] = os.TempDir() 275 err = p.Prepare(config) 276 if err != nil { 277 t.Fatalf("err: %s", err) 278 } 279 280 config["host_vars"] = playbook_file.Name() 281 err = p.Prepare(config) 282 if err == nil { 283 t.Fatalf("should error if host_vars path is not a dir") 284 } 285 286 config["host_vars"] = os.TempDir() 287 err = p.Prepare(config) 288 if err != nil { 289 t.Fatalf("err: %s", err) 290 } 291 } 292 293 func TestProvisionerPrepare_CleanStagingDir(t *testing.T) { 294 var p Provisioner 295 config := testConfig() 296 297 playbook_file, err := ioutil.TempFile("", "playbook") 298 if err != nil { 299 t.Fatalf("err: %s", err) 300 } 301 defer os.Remove(playbook_file.Name()) 302 303 config["playbook_file"] = playbook_file.Name() 304 config["clean_staging_directory"] = true 305 306 err = p.Prepare(config) 307 if err != nil { 308 t.Fatalf("err: %s", err) 309 } 310 311 if !p.config.CleanStagingDir { 312 t.Fatalf("expected clean_staging_directory to be set") 313 } 314 } 315 316 func TestProvisionerProvisionDocker_PlaybookFiles(t *testing.T) { 317 testProvisionerProvisionDockerWithPlaybookFiles(t, playbookFilesDockerTemplate) 318 } 319 320 func TestProvisionerProvisionDocker_PlaybookFilesWithPlaybookDir(t *testing.T) { 321 testProvisionerProvisionDockerWithPlaybookFiles(t, playbookFilesWithPlaybookDirDockerTemplate) 322 } 323 324 func testProvisionerProvisionDockerWithPlaybookFiles(t *testing.T, templateString string) { 325 if os.Getenv("PACKER_ACC") == "" { 326 t.Skip("This test is only run with PACKER_ACC=1") 327 } 328 329 ui := packer.TestUi(t) 330 cache := &packer.FileCache{CacheDir: os.TempDir()} 331 332 tpl, err := template.Parse(strings.NewReader(templateString)) 333 if err != nil { 334 t.Fatalf("Unable to parse config: %s", err) 335 } 336 337 // Check if docker executable can be found. 338 _, err = exec.LookPath("docker") 339 if err != nil { 340 t.Error("docker command not found; please make sure docker is installed") 341 } 342 343 // Setup the builder 344 builder := &docker.Builder{} 345 warnings, err := builder.Prepare(tpl.Builders["docker"].Config) 346 if err != nil { 347 t.Fatalf("Error preparing configuration %s", err) 348 } 349 if len(warnings) > 0 { 350 t.Fatal("Encountered configuration warnings; aborting") 351 } 352 353 ansible := &Provisioner{} 354 err = ansible.Prepare(tpl.Provisioners[0].Config) 355 if err != nil { 356 t.Fatalf("Error preparing ansible-local provisioner: %s", err) 357 } 358 359 download := &file.Provisioner{} 360 err = download.Prepare(tpl.Provisioners[1].Config) 361 if err != nil { 362 t.Fatalf("Error preparing download: %s", err) 363 } 364 365 // Add hooks so the provisioners run during the build 366 hooks := map[string][]packer.Hook{} 367 hooks[packer.HookProvision] = []packer.Hook{ 368 &packer.ProvisionHook{ 369 Provisioners: []*packer.HookedProvisioner{ 370 {ansible, nil, ""}, 371 {download, nil, ""}, 372 }, 373 }, 374 } 375 hook := &packer.DispatchHook{Mapping: hooks} 376 377 artifact, err := builder.Run(ui, hook, cache) 378 if err != nil { 379 t.Fatalf("Error running build %s", err) 380 } 381 defer os.Remove("hello_world") 382 defer artifact.Destroy() 383 384 actualContent, err := ioutil.ReadFile("hello_world") 385 if err != nil { 386 t.Fatalf("Expected file not found: %s", err) 387 } 388 389 expectedContent := "Hello world!" 390 if string(actualContent) != expectedContent { 391 t.Fatalf(`Unexpected file content: expected="%s", actual="%s"`, expectedContent, actualContent) 392 } 393 } 394 395 func assertPlaybooksExecuted(comm *communicatorMock, playbooks []string) { 396 cmdIndex := 0 397 for _, playbook := range playbooks { 398 playbook = filepath.ToSlash(playbook) 399 for ; cmdIndex < len(comm.startCommand); cmdIndex++ { 400 cmd := comm.startCommand[cmdIndex] 401 if strings.Contains(cmd, "ansible-playbook") && strings.Contains(cmd, playbook) { 402 break 403 } 404 } 405 if cmdIndex == len(comm.startCommand) { 406 panic(fmt.Sprintf("Playbook %s was not executed", playbook)) 407 } 408 } 409 } 410 411 func assertPlaybooksUploaded(comm *communicatorMock, playbooks []string) { 412 uploadIndex := 0 413 for _, playbook := range playbooks { 414 playbook = filepath.ToSlash(playbook) 415 for ; uploadIndex < len(comm.uploadDestination); uploadIndex++ { 416 dest := comm.uploadDestination[uploadIndex] 417 if strings.HasSuffix(dest, playbook) { 418 break 419 } 420 } 421 if uploadIndex == len(comm.uploadDestination) { 422 panic(fmt.Sprintf("Playbook %s was not uploaded", playbook)) 423 } 424 } 425 } 426 427 func assertPlaybooksNotUploaded(comm *communicatorMock, playbooks []string) { 428 for _, playbook := range playbooks { 429 playbook = filepath.ToSlash(playbook) 430 for _, destination := range comm.uploadDestination { 431 if strings.HasSuffix(destination, playbook) { 432 panic(fmt.Sprintf("Playbook %s was uploaded", playbook)) 433 } 434 } 435 } 436 } 437 438 func testConfig() map[string]interface{} { 439 m := make(map[string]interface{}) 440 return m 441 } 442 443 func createTempFile(dir string) string { 444 file, err := ioutil.TempFile(dir, "") 445 if err != nil { 446 panic(fmt.Sprintf("err: %s", err)) 447 } 448 return file.Name() 449 } 450 451 func createTempFiles(dir string, numFiles int) []string { 452 files := make([]string, 0, numFiles) 453 defer func() { 454 // Cleanup the files if not all were created. 455 if len(files) < numFiles { 456 for _, file := range files { 457 os.Remove(file) 458 } 459 } 460 }() 461 462 for i := 0; i < numFiles; i++ { 463 files = append(files, createTempFile(dir)) 464 } 465 return files 466 } 467 468 func removeFiles(files ...string) { 469 for _, file := range files { 470 os.Remove(file) 471 } 472 } 473 474 const playbookFilesDockerTemplate = ` 475 { 476 "builders": [ 477 { 478 "type": "docker", 479 "image": "williamyeh/ansible:centos7", 480 "discard": true 481 } 482 ], 483 "provisioners": [ 484 { 485 "type": "ansible-local", 486 "playbook_files": [ 487 "test-fixtures/hello.yml", 488 "test-fixtures/world.yml" 489 ] 490 }, 491 { 492 "type": "file", 493 "source": "/tmp/hello_world", 494 "destination": "hello_world", 495 "direction": "download" 496 } 497 ] 498 } 499 ` 500 501 const playbookFilesWithPlaybookDirDockerTemplate = ` 502 { 503 "builders": [ 504 { 505 "type": "docker", 506 "image": "williamyeh/ansible:centos7", 507 "discard": true 508 } 509 ], 510 "provisioners": [ 511 { 512 "type": "ansible-local", 513 "playbook_files": [ 514 "test-fixtures/hello.yml", 515 "test-fixtures/world.yml" 516 ], 517 "playbook_dir": "test-fixtures" 518 }, 519 { 520 "type": "file", 521 "source": "/tmp/hello_world", 522 "destination": "hello_world", 523 "direction": "download" 524 } 525 ] 526 } 527 `