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