github.com/spenczar/terraform@v0.6.9-0.20151213231906-86882e39edae/command/plan_test.go (about) 1 package command 2 3 import ( 4 "bytes" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "strings" 9 "testing" 10 11 "github.com/hashicorp/terraform/terraform" 12 "github.com/mitchellh/cli" 13 ) 14 15 func TestPlan(t *testing.T) { 16 cwd, err := os.Getwd() 17 if err != nil { 18 t.Fatalf("err: %s", err) 19 } 20 if err := os.Chdir(testFixturePath("plan")); err != nil { 21 t.Fatalf("err: %s", err) 22 } 23 defer os.Chdir(cwd) 24 25 p := testProvider() 26 ui := new(cli.MockUi) 27 c := &PlanCommand{ 28 Meta: Meta{ 29 ContextOpts: testCtxConfig(p), 30 Ui: ui, 31 }, 32 } 33 34 args := []string{} 35 if code := c.Run(args); code != 0 { 36 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 37 } 38 } 39 40 func TestPlan_destroy(t *testing.T) { 41 originalState := &terraform.State{ 42 Modules: []*terraform.ModuleState{ 43 &terraform.ModuleState{ 44 Path: []string{"root"}, 45 Resources: map[string]*terraform.ResourceState{ 46 "test_instance.foo": &terraform.ResourceState{ 47 Type: "test_instance", 48 Primary: &terraform.InstanceState{ 49 ID: "bar", 50 }, 51 }, 52 }, 53 }, 54 }, 55 } 56 57 outPath := testTempFile(t) 58 statePath := testStateFile(t, originalState) 59 60 p := testProvider() 61 ui := new(cli.MockUi) 62 c := &PlanCommand{ 63 Meta: Meta{ 64 ContextOpts: testCtxConfig(p), 65 Ui: ui, 66 }, 67 } 68 69 args := []string{ 70 "-destroy", 71 "-out", outPath, 72 "-state", statePath, 73 testFixturePath("plan"), 74 } 75 if code := c.Run(args); code != 0 { 76 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 77 } 78 79 if !p.RefreshCalled { 80 t.Fatal("refresh should be called") 81 } 82 83 plan := testReadPlan(t, outPath) 84 for _, m := range plan.Diff.Modules { 85 for _, r := range m.Resources { 86 if !r.Destroy { 87 t.Fatalf("bad: %#v", r) 88 } 89 } 90 } 91 92 f, err := os.Open(statePath + DefaultBackupExtension) 93 if err != nil { 94 t.Fatalf("err: %s", err) 95 } 96 97 backupState, err := terraform.ReadState(f) 98 f.Close() 99 if err != nil { 100 t.Fatalf("err: %s", err) 101 } 102 103 actualStr := strings.TrimSpace(backupState.String()) 104 expectedStr := strings.TrimSpace(originalState.String()) 105 if actualStr != expectedStr { 106 t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr) 107 } 108 } 109 110 func TestPlan_noState(t *testing.T) { 111 p := testProvider() 112 ui := new(cli.MockUi) 113 c := &PlanCommand{ 114 Meta: Meta{ 115 ContextOpts: testCtxConfig(p), 116 Ui: ui, 117 }, 118 } 119 120 args := []string{ 121 testFixturePath("plan"), 122 } 123 if code := c.Run(args); code != 0 { 124 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 125 } 126 127 // Verify that refresh was called 128 if p.RefreshCalled { 129 t.Fatal("refresh should not be called") 130 } 131 132 // Verify that the provider was called with the existing state 133 actual := strings.TrimSpace(p.DiffState.String()) 134 expected := strings.TrimSpace(testPlanNoStateStr) 135 if actual != expected { 136 t.Fatalf("bad:\n\n%s", actual) 137 } 138 } 139 140 func TestPlan_outPath(t *testing.T) { 141 tf, err := ioutil.TempFile("", "tf") 142 if err != nil { 143 t.Fatalf("err: %s", err) 144 } 145 outPath := tf.Name() 146 os.Remove(tf.Name()) 147 148 p := testProvider() 149 ui := new(cli.MockUi) 150 c := &PlanCommand{ 151 Meta: Meta{ 152 ContextOpts: testCtxConfig(p), 153 Ui: ui, 154 }, 155 } 156 157 p.DiffReturn = &terraform.InstanceDiff{ 158 Destroy: true, 159 } 160 161 args := []string{ 162 "-out", outPath, 163 testFixturePath("plan"), 164 } 165 if code := c.Run(args); code != 0 { 166 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 167 } 168 169 f, err := os.Open(outPath) 170 if err != nil { 171 t.Fatalf("err: %s", err) 172 } 173 defer f.Close() 174 175 if _, err := terraform.ReadPlan(f); err != nil { 176 t.Fatalf("err: %s", err) 177 } 178 } 179 180 func TestPlan_refresh(t *testing.T) { 181 p := testProvider() 182 ui := new(cli.MockUi) 183 c := &PlanCommand{ 184 Meta: Meta{ 185 ContextOpts: testCtxConfig(p), 186 Ui: ui, 187 }, 188 } 189 190 args := []string{ 191 "-refresh=false", 192 testFixturePath("plan"), 193 } 194 if code := c.Run(args); code != 0 { 195 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 196 } 197 198 if p.RefreshCalled { 199 t.Fatal("refresh should not be called") 200 } 201 } 202 203 func TestPlan_state(t *testing.T) { 204 // Write out some prior state 205 tf, err := ioutil.TempFile("", "tf") 206 if err != nil { 207 t.Fatalf("err: %s", err) 208 } 209 statePath := tf.Name() 210 defer os.Remove(tf.Name()) 211 212 originalState := testState() 213 err = terraform.WriteState(originalState, tf) 214 tf.Close() 215 if err != nil { 216 t.Fatalf("err: %s", err) 217 } 218 219 p := testProvider() 220 ui := new(cli.MockUi) 221 c := &PlanCommand{ 222 Meta: Meta{ 223 ContextOpts: testCtxConfig(p), 224 Ui: ui, 225 }, 226 } 227 228 args := []string{ 229 "-state", statePath, 230 testFixturePath("plan"), 231 } 232 if code := c.Run(args); code != 0 { 233 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 234 } 235 236 // Verify that the provider was called with the existing state 237 actual := strings.TrimSpace(p.DiffState.String()) 238 expected := strings.TrimSpace(testPlanStateStr) 239 if actual != expected { 240 t.Fatalf("bad:\n\n%s", actual) 241 } 242 } 243 244 func TestPlan_stateDefault(t *testing.T) { 245 originalState := testState() 246 247 // Write the state file in a temporary directory with the 248 // default filename. 249 td, err := ioutil.TempDir("", "tf") 250 if err != nil { 251 t.Fatalf("err: %s", err) 252 } 253 statePath := filepath.Join(td, DefaultStateFilename) 254 255 f, err := os.Create(statePath) 256 if err != nil { 257 t.Fatalf("err: %s", err) 258 } 259 err = terraform.WriteState(originalState, f) 260 f.Close() 261 if err != nil { 262 t.Fatalf("err: %s", err) 263 } 264 265 // Change to that directory 266 cwd, err := os.Getwd() 267 if err != nil { 268 t.Fatalf("err: %s", err) 269 } 270 if err := os.Chdir(filepath.Dir(statePath)); err != nil { 271 t.Fatalf("err: %s", err) 272 } 273 defer os.Chdir(cwd) 274 275 p := testProvider() 276 ui := new(cli.MockUi) 277 c := &PlanCommand{ 278 Meta: Meta{ 279 ContextOpts: testCtxConfig(p), 280 Ui: ui, 281 }, 282 } 283 284 args := []string{ 285 testFixturePath("plan"), 286 } 287 if code := c.Run(args); code != 0 { 288 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 289 } 290 291 // Verify that the provider was called with the existing state 292 actual := strings.TrimSpace(p.DiffState.String()) 293 expected := strings.TrimSpace(testPlanStateDefaultStr) 294 if actual != expected { 295 t.Fatalf("bad:\n\n%s", actual) 296 } 297 } 298 299 func TestPlan_vars(t *testing.T) { 300 p := testProvider() 301 ui := new(cli.MockUi) 302 c := &PlanCommand{ 303 Meta: Meta{ 304 ContextOpts: testCtxConfig(p), 305 Ui: ui, 306 }, 307 } 308 309 actual := "" 310 p.DiffFn = func( 311 info *terraform.InstanceInfo, 312 s *terraform.InstanceState, 313 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 314 if v, ok := c.Config["value"]; ok { 315 actual = v.(string) 316 } 317 318 return nil, nil 319 } 320 321 args := []string{ 322 "-var", "foo=bar", 323 testFixturePath("plan-vars"), 324 } 325 if code := c.Run(args); code != 0 { 326 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 327 } 328 329 if actual != "bar" { 330 t.Fatal("didn't work") 331 } 332 } 333 334 func TestPlan_varsUnset(t *testing.T) { 335 // Disable test mode so input would be asked 336 test = false 337 defer func() { test = true }() 338 339 defaultInputReader = bytes.NewBufferString("bar\n") 340 341 p := testProvider() 342 ui := new(cli.MockUi) 343 c := &PlanCommand{ 344 Meta: Meta{ 345 ContextOpts: testCtxConfig(p), 346 Ui: ui, 347 }, 348 } 349 350 args := []string{ 351 testFixturePath("plan-vars"), 352 } 353 if code := c.Run(args); code != 0 { 354 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 355 } 356 } 357 358 func TestPlan_varFile(t *testing.T) { 359 varFilePath := testTempFile(t) 360 if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); err != nil { 361 t.Fatalf("err: %s", err) 362 } 363 364 p := testProvider() 365 ui := new(cli.MockUi) 366 c := &PlanCommand{ 367 Meta: Meta{ 368 ContextOpts: testCtxConfig(p), 369 Ui: ui, 370 }, 371 } 372 373 actual := "" 374 p.DiffFn = func( 375 info *terraform.InstanceInfo, 376 s *terraform.InstanceState, 377 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 378 if v, ok := c.Config["value"]; ok { 379 actual = v.(string) 380 } 381 382 return nil, nil 383 } 384 385 args := []string{ 386 "-var-file", varFilePath, 387 testFixturePath("plan-vars"), 388 } 389 if code := c.Run(args); code != 0 { 390 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 391 } 392 393 if actual != "bar" { 394 t.Fatal("didn't work") 395 } 396 } 397 398 func TestPlan_varFileDefault(t *testing.T) { 399 varFileDir := testTempDir(t) 400 varFilePath := filepath.Join(varFileDir, "terraform.tfvars") 401 if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); err != nil { 402 t.Fatalf("err: %s", err) 403 } 404 405 cwd, err := os.Getwd() 406 if err != nil { 407 t.Fatalf("err: %s", err) 408 } 409 if err := os.Chdir(varFileDir); err != nil { 410 t.Fatalf("err: %s", err) 411 } 412 defer os.Chdir(cwd) 413 414 p := testProvider() 415 ui := new(cli.MockUi) 416 c := &PlanCommand{ 417 Meta: Meta{ 418 ContextOpts: testCtxConfig(p), 419 Ui: ui, 420 }, 421 } 422 423 actual := "" 424 p.DiffFn = func( 425 info *terraform.InstanceInfo, 426 s *terraform.InstanceState, 427 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 428 if v, ok := c.Config["value"]; ok { 429 actual = v.(string) 430 } 431 432 return nil, nil 433 } 434 435 args := []string{ 436 testFixturePath("plan-vars"), 437 } 438 if code := c.Run(args); code != 0 { 439 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 440 } 441 442 if actual != "bar" { 443 t.Fatal("didn't work") 444 } 445 } 446 447 func TestPlan_backup(t *testing.T) { 448 // Write out some prior state 449 tf, err := ioutil.TempFile("", "tf") 450 if err != nil { 451 t.Fatalf("err: %s", err) 452 } 453 statePath := tf.Name() 454 defer os.Remove(tf.Name()) 455 456 // Write out some prior state 457 backupf, err := ioutil.TempFile("", "tf") 458 if err != nil { 459 t.Fatalf("err: %s", err) 460 } 461 backupPath := backupf.Name() 462 backupf.Close() 463 os.Remove(backupPath) 464 defer os.Remove(backupPath) 465 466 originalState := &terraform.State{ 467 Modules: []*terraform.ModuleState{ 468 &terraform.ModuleState{ 469 Path: []string{"root"}, 470 Resources: map[string]*terraform.ResourceState{ 471 "test_instance.foo": &terraform.ResourceState{ 472 Type: "test_instance", 473 Primary: &terraform.InstanceState{ 474 ID: "bar", 475 }, 476 }, 477 }, 478 }, 479 }, 480 } 481 482 err = terraform.WriteState(originalState, tf) 483 tf.Close() 484 if err != nil { 485 t.Fatalf("err: %s", err) 486 } 487 488 p := testProvider() 489 ui := new(cli.MockUi) 490 c := &PlanCommand{ 491 Meta: Meta{ 492 ContextOpts: testCtxConfig(p), 493 Ui: ui, 494 }, 495 } 496 497 args := []string{ 498 "-state", statePath, 499 "-backup", backupPath, 500 testFixturePath("plan"), 501 } 502 if code := c.Run(args); code != 0 { 503 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 504 } 505 506 // Verify that the provider was called with the existing state 507 actual := strings.TrimSpace(p.DiffState.String()) 508 expected := strings.TrimSpace(testPlanBackupStr) 509 if actual != expected { 510 t.Fatalf("bad:\n\n%s", actual) 511 } 512 513 // Verify the backup exist 514 f, err := os.Open(backupPath) 515 if err != nil { 516 t.Fatalf("err: %s", err) 517 } 518 519 backupState, err := terraform.ReadState(f) 520 f.Close() 521 if err != nil { 522 t.Fatalf("err: %s", err) 523 } 524 525 actualStr := strings.TrimSpace(backupState.String()) 526 expectedStr := strings.TrimSpace(originalState.String()) 527 if actualStr != expectedStr { 528 t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr) 529 } 530 } 531 532 func TestPlan_disableBackup(t *testing.T) { 533 // Write out some prior state 534 tf, err := ioutil.TempFile("", "tf") 535 if err != nil { 536 t.Fatalf("err: %s", err) 537 } 538 statePath := tf.Name() 539 defer os.Remove(tf.Name()) 540 541 originalState := &terraform.State{ 542 Modules: []*terraform.ModuleState{ 543 &terraform.ModuleState{ 544 Path: []string{"root"}, 545 Resources: map[string]*terraform.ResourceState{ 546 "test_instance.foo": &terraform.ResourceState{ 547 Type: "test_instance", 548 Primary: &terraform.InstanceState{ 549 ID: "bar", 550 }, 551 }, 552 }, 553 }, 554 }, 555 } 556 557 err = terraform.WriteState(originalState, tf) 558 tf.Close() 559 if err != nil { 560 t.Fatalf("err: %s", err) 561 } 562 563 p := testProvider() 564 ui := new(cli.MockUi) 565 c := &PlanCommand{ 566 Meta: Meta{ 567 ContextOpts: testCtxConfig(p), 568 Ui: ui, 569 }, 570 } 571 572 args := []string{ 573 "-state", statePath, 574 "-backup", "-", 575 testFixturePath("plan"), 576 } 577 if code := c.Run(args); code != 0 { 578 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 579 } 580 581 // Verify that the provider was called with the existing state 582 actual := strings.TrimSpace(p.DiffState.String()) 583 expected := strings.TrimSpace(testPlanDisableBackupStr) 584 if actual != expected { 585 t.Fatalf("bad:\n\n%s", actual) 586 } 587 588 // Ensure there is no backup 589 _, err = os.Stat(statePath + DefaultBackupExtension) 590 if err == nil || !os.IsNotExist(err) { 591 t.Fatalf("backup should not exist") 592 } 593 } 594 595 func TestPlan_detailedExitcode(t *testing.T) { 596 cwd, err := os.Getwd() 597 if err != nil { 598 t.Fatalf("err: %s", err) 599 } 600 if err := os.Chdir(testFixturePath("plan")); err != nil { 601 t.Fatalf("err: %s", err) 602 } 603 defer os.Chdir(cwd) 604 605 p := testProvider() 606 ui := new(cli.MockUi) 607 c := &PlanCommand{ 608 Meta: Meta{ 609 ContextOpts: testCtxConfig(p), 610 Ui: ui, 611 }, 612 } 613 614 args := []string{"-detailed-exitcode"} 615 if code := c.Run(args); code != 2 { 616 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 617 } 618 } 619 620 func TestPlan_detailedExitcode_emptyDiff(t *testing.T) { 621 cwd, err := os.Getwd() 622 if err != nil { 623 t.Fatalf("err: %s", err) 624 } 625 if err := os.Chdir(testFixturePath("plan-emptydiff")); err != nil { 626 t.Fatalf("err: %s", err) 627 } 628 defer os.Chdir(cwd) 629 630 p := testProvider() 631 ui := new(cli.MockUi) 632 c := &PlanCommand{ 633 Meta: Meta{ 634 ContextOpts: testCtxConfig(p), 635 Ui: ui, 636 }, 637 } 638 639 args := []string{"-detailed-exitcode"} 640 if code := c.Run(args); code != 0 { 641 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 642 } 643 } 644 645 const planVarFile = ` 646 foo = "bar" 647 ` 648 649 const testPlanBackupStr = ` 650 ID = bar 651 ` 652 653 const testPlanDisableBackupStr = ` 654 ID = bar 655 ` 656 657 const testPlanNoStateStr = ` 658 <not created> 659 ` 660 661 const testPlanStateStr = ` 662 ID = bar 663 ` 664 665 const testPlanStateDefaultStr = ` 666 ID = bar 667 `