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