github.com/rhenning/terraform@v0.8.0-beta2/command/refresh_test.go (about) 1 package command 2 3 import ( 4 "bytes" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "reflect" 9 "strings" 10 "testing" 11 12 "github.com/hashicorp/terraform/terraform" 13 "github.com/mitchellh/cli" 14 ) 15 16 func TestRefresh(t *testing.T) { 17 state := testState() 18 statePath := testStateFile(t, state) 19 20 p := testProvider() 21 ui := new(cli.MockUi) 22 c := &RefreshCommand{ 23 Meta: Meta{ 24 ContextOpts: testCtxConfig(p), 25 Ui: ui, 26 }, 27 } 28 29 p.RefreshFn = nil 30 p.RefreshReturn = &terraform.InstanceState{ID: "yes"} 31 32 args := []string{ 33 "-state", statePath, 34 testFixturePath("refresh"), 35 } 36 if code := c.Run(args); code != 0 { 37 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 38 } 39 40 if !p.RefreshCalled { 41 t.Fatal("refresh should be called") 42 } 43 44 f, err := os.Open(statePath) 45 if err != nil { 46 t.Fatalf("err: %s", err) 47 } 48 49 newState, err := terraform.ReadState(f) 50 f.Close() 51 if err != nil { 52 t.Fatalf("err: %s", err) 53 } 54 55 actual := strings.TrimSpace(newState.String()) 56 expected := strings.TrimSpace(testRefreshStr) 57 if actual != expected { 58 t.Fatalf("bad:\n\n%s", actual) 59 } 60 } 61 62 func TestRefresh_badState(t *testing.T) { 63 p := testProvider() 64 ui := new(cli.MockUi) 65 c := &RefreshCommand{ 66 Meta: Meta{ 67 ContextOpts: testCtxConfig(p), 68 Ui: ui, 69 }, 70 } 71 72 args := []string{ 73 "-state", "i-should-not-exist-ever", 74 testFixturePath("refresh"), 75 } 76 if code := c.Run(args); code != 1 { 77 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 78 } 79 } 80 81 func TestRefresh_cwd(t *testing.T) { 82 cwd, err := os.Getwd() 83 if err != nil { 84 t.Fatalf("err: %s", err) 85 } 86 if err := os.Chdir(testFixturePath("refresh")); err != nil { 87 t.Fatalf("err: %s", err) 88 } 89 defer os.Chdir(cwd) 90 91 state := testState() 92 statePath := testStateFile(t, state) 93 94 p := testProvider() 95 ui := new(cli.MockUi) 96 c := &RefreshCommand{ 97 Meta: Meta{ 98 ContextOpts: testCtxConfig(p), 99 Ui: ui, 100 }, 101 } 102 103 p.RefreshFn = nil 104 p.RefreshReturn = &terraform.InstanceState{ID: "yes"} 105 106 args := []string{ 107 "-state", statePath, 108 } 109 if code := c.Run(args); code != 0 { 110 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 111 } 112 113 if !p.RefreshCalled { 114 t.Fatal("refresh should be called") 115 } 116 117 f, err := os.Open(statePath) 118 if err != nil { 119 t.Fatalf("err: %s", err) 120 } 121 122 newState, err := terraform.ReadState(f) 123 f.Close() 124 if err != nil { 125 t.Fatalf("err: %s", err) 126 } 127 128 actual := strings.TrimSpace(newState.String()) 129 expected := strings.TrimSpace(testRefreshCwdStr) 130 if actual != expected { 131 t.Fatalf("bad:\n\n%s", actual) 132 } 133 } 134 135 func TestRefresh_defaultState(t *testing.T) { 136 originalState := testState() 137 138 // Write the state file in a temporary directory with the 139 // default filename. 140 td, err := ioutil.TempDir("", "tf") 141 if err != nil { 142 t.Fatalf("err: %s", err) 143 } 144 statePath := filepath.Join(td, DefaultStateFilename) 145 146 f, err := os.Create(statePath) 147 if err != nil { 148 t.Fatalf("err: %s", err) 149 } 150 err = terraform.WriteState(originalState, f) 151 f.Close() 152 if err != nil { 153 t.Fatalf("err: %s", err) 154 } 155 156 // Change to that directory 157 cwd, err := os.Getwd() 158 if err != nil { 159 t.Fatalf("err: %s", err) 160 } 161 if err := os.Chdir(filepath.Dir(statePath)); err != nil { 162 t.Fatalf("err: %s", err) 163 } 164 defer os.Chdir(cwd) 165 166 p := testProvider() 167 ui := new(cli.MockUi) 168 c := &RefreshCommand{ 169 Meta: Meta{ 170 ContextOpts: testCtxConfig(p), 171 Ui: ui, 172 }, 173 } 174 175 p.RefreshFn = nil 176 p.RefreshReturn = newInstanceState("yes") 177 178 args := []string{ 179 testFixturePath("refresh"), 180 } 181 if code := c.Run(args); code != 0 { 182 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 183 } 184 185 if !p.RefreshCalled { 186 t.Fatal("refresh should be called") 187 } 188 189 f, err = os.Open(statePath) 190 if err != nil { 191 t.Fatalf("err: %s", err) 192 } 193 194 newState, err := terraform.ReadState(f) 195 f.Close() 196 if err != nil { 197 t.Fatalf("err: %s", err) 198 } 199 200 actual := newState.RootModule().Resources["test_instance.foo"].Primary 201 expected := p.RefreshReturn 202 if !reflect.DeepEqual(actual, expected) { 203 t.Logf("expected:\n%#v", expected) 204 t.Fatalf("bad:\n%#v", actual) 205 } 206 207 f, err = os.Open(statePath + DefaultBackupExtension) 208 if err != nil { 209 t.Fatalf("err: %s", err) 210 } 211 212 backupState, err := terraform.ReadState(f) 213 f.Close() 214 if err != nil { 215 t.Fatalf("err: %s", err) 216 } 217 218 actual = backupState.RootModule().Resources["test_instance.foo"].Primary 219 expected = originalState.RootModule().Resources["test_instance.foo"].Primary 220 if !reflect.DeepEqual(actual, expected) { 221 t.Fatalf("bad: %#v", actual) 222 } 223 } 224 225 func TestRefresh_futureState(t *testing.T) { 226 cwd, err := os.Getwd() 227 if err != nil { 228 t.Fatalf("err: %s", err) 229 } 230 if err := os.Chdir(testFixturePath("refresh")); err != nil { 231 t.Fatalf("err: %s", err) 232 } 233 defer os.Chdir(cwd) 234 235 state := testState() 236 state.TFVersion = "99.99.99" 237 statePath := testStateFile(t, state) 238 239 p := testProvider() 240 ui := new(cli.MockUi) 241 c := &RefreshCommand{ 242 Meta: Meta{ 243 ContextOpts: testCtxConfig(p), 244 Ui: ui, 245 }, 246 } 247 248 args := []string{ 249 "-state", statePath, 250 } 251 if code := c.Run(args); code == 0 { 252 t.Fatal("should fail") 253 } 254 255 if p.RefreshCalled { 256 t.Fatal("refresh should not be called") 257 } 258 259 f, err := os.Open(statePath) 260 if err != nil { 261 t.Fatalf("err: %s", err) 262 } 263 264 newState, err := terraform.ReadState(f) 265 f.Close() 266 if err != nil { 267 t.Fatalf("err: %s", err) 268 } 269 270 actual := strings.TrimSpace(newState.String()) 271 expected := strings.TrimSpace(state.String()) 272 if actual != expected { 273 t.Fatalf("bad:\n\n%s", actual) 274 } 275 } 276 277 func TestRefresh_pastState(t *testing.T) { 278 state := testState() 279 state.TFVersion = "0.1.0" 280 statePath := testStateFile(t, state) 281 282 p := testProvider() 283 ui := new(cli.MockUi) 284 c := &RefreshCommand{ 285 Meta: Meta{ 286 ContextOpts: testCtxConfig(p), 287 Ui: ui, 288 }, 289 } 290 291 p.RefreshFn = nil 292 p.RefreshReturn = &terraform.InstanceState{ID: "yes"} 293 294 args := []string{ 295 "-state", statePath, 296 testFixturePath("refresh"), 297 } 298 if code := c.Run(args); code != 0 { 299 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 300 } 301 302 if !p.RefreshCalled { 303 t.Fatal("refresh should be called") 304 } 305 306 f, err := os.Open(statePath) 307 if err != nil { 308 t.Fatalf("err: %s", err) 309 } 310 311 newState, err := terraform.ReadState(f) 312 f.Close() 313 if err != nil { 314 t.Fatalf("err: %s", err) 315 } 316 317 actual := strings.TrimSpace(newState.String()) 318 expected := strings.TrimSpace(testRefreshStr) 319 if actual != expected { 320 t.Fatalf("bad:\n\n%s", actual) 321 } 322 323 if newState.TFVersion != terraform.Version { 324 t.Fatalf("bad:\n\n%s", newState.TFVersion) 325 } 326 } 327 328 func TestRefresh_outPath(t *testing.T) { 329 state := testState() 330 statePath := testStateFile(t, state) 331 332 // Output path 333 outf, err := ioutil.TempFile("", "tf") 334 if err != nil { 335 t.Fatalf("err: %s", err) 336 } 337 outPath := outf.Name() 338 outf.Close() 339 os.Remove(outPath) 340 341 p := testProvider() 342 ui := new(cli.MockUi) 343 c := &RefreshCommand{ 344 Meta: Meta{ 345 ContextOpts: testCtxConfig(p), 346 Ui: ui, 347 }, 348 } 349 350 p.RefreshFn = nil 351 p.RefreshReturn = newInstanceState("yes") 352 353 args := []string{ 354 "-state", statePath, 355 "-state-out", outPath, 356 testFixturePath("refresh"), 357 } 358 if code := c.Run(args); code != 0 { 359 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 360 } 361 362 f, err := os.Open(statePath) 363 if err != nil { 364 t.Fatalf("err: %s", err) 365 } 366 367 newState, err := terraform.ReadState(f) 368 f.Close() 369 if err != nil { 370 t.Fatalf("err: %s", err) 371 } 372 373 if !reflect.DeepEqual(newState, state) { 374 t.Fatalf("bad: %#v", newState) 375 } 376 377 f, err = os.Open(outPath) 378 if err != nil { 379 t.Fatalf("err: %s", err) 380 } 381 382 newState, err = terraform.ReadState(f) 383 f.Close() 384 if err != nil { 385 t.Fatalf("err: %s", err) 386 } 387 388 actual := newState.RootModule().Resources["test_instance.foo"].Primary 389 expected := p.RefreshReturn 390 if !reflect.DeepEqual(actual, expected) { 391 t.Fatalf("bad: %#v", actual) 392 } 393 394 f, err = os.Open(outPath + DefaultBackupExtension) 395 if err != nil { 396 t.Fatalf("err: %s", err) 397 } 398 399 backupState, err := terraform.ReadState(f) 400 f.Close() 401 if err != nil { 402 t.Fatalf("err: %s", err) 403 } 404 405 actualStr := strings.TrimSpace(backupState.String()) 406 expectedStr := strings.TrimSpace(state.String()) 407 if actualStr != expectedStr { 408 t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr) 409 } 410 } 411 412 func TestRefresh_var(t *testing.T) { 413 state := testState() 414 statePath := testStateFile(t, state) 415 416 p := testProvider() 417 ui := new(cli.MockUi) 418 c := &RefreshCommand{ 419 Meta: Meta{ 420 ContextOpts: testCtxConfig(p), 421 Ui: ui, 422 }, 423 } 424 425 args := []string{ 426 "-var", "foo=bar", 427 "-state", statePath, 428 testFixturePath("refresh-var"), 429 } 430 if code := c.Run(args); code != 0 { 431 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 432 } 433 434 if !p.ConfigureCalled { 435 t.Fatal("configure should be called") 436 } 437 if p.ConfigureConfig.Config["value"].(string) != "bar" { 438 t.Fatalf("bad: %#v", p.ConfigureConfig.Config) 439 } 440 } 441 442 func TestRefresh_varFile(t *testing.T) { 443 state := testState() 444 statePath := testStateFile(t, state) 445 446 p := testProvider() 447 ui := new(cli.MockUi) 448 c := &RefreshCommand{ 449 Meta: Meta{ 450 ContextOpts: testCtxConfig(p), 451 Ui: ui, 452 }, 453 } 454 455 varFilePath := testTempFile(t) 456 if err := ioutil.WriteFile(varFilePath, []byte(refreshVarFile), 0644); err != nil { 457 t.Fatalf("err: %s", err) 458 } 459 460 args := []string{ 461 "-var-file", varFilePath, 462 "-state", statePath, 463 testFixturePath("refresh-var"), 464 } 465 if code := c.Run(args); code != 0 { 466 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 467 } 468 469 if !p.ConfigureCalled { 470 t.Fatal("configure should be called") 471 } 472 if p.ConfigureConfig.Config["value"].(string) != "bar" { 473 t.Fatalf("bad: %#v", p.ConfigureConfig.Config) 474 } 475 } 476 477 func TestRefresh_varFileDefault(t *testing.T) { 478 state := testState() 479 statePath := testStateFile(t, state) 480 481 p := testProvider() 482 ui := new(cli.MockUi) 483 c := &RefreshCommand{ 484 Meta: Meta{ 485 ContextOpts: testCtxConfig(p), 486 Ui: ui, 487 }, 488 } 489 490 varFileDir := testTempDir(t) 491 varFilePath := filepath.Join(varFileDir, "terraform.tfvars") 492 if err := ioutil.WriteFile(varFilePath, []byte(refreshVarFile), 0644); err != nil { 493 t.Fatalf("err: %s", err) 494 } 495 496 cwd, err := os.Getwd() 497 if err != nil { 498 t.Fatalf("err: %s", err) 499 } 500 if err := os.Chdir(varFileDir); err != nil { 501 t.Fatalf("err: %s", err) 502 } 503 defer os.Chdir(cwd) 504 505 args := []string{ 506 "-state", statePath, 507 testFixturePath("refresh-var"), 508 } 509 if code := c.Run(args); code != 0 { 510 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 511 } 512 513 if !p.ConfigureCalled { 514 t.Fatal("configure should be called") 515 } 516 if p.ConfigureConfig.Config["value"].(string) != "bar" { 517 t.Fatalf("bad: %#v", p.ConfigureConfig.Config) 518 } 519 } 520 521 func TestRefresh_varsUnset(t *testing.T) { 522 // Disable test mode so input would be asked 523 test = false 524 defer func() { test = true }() 525 526 defaultInputReader = bytes.NewBufferString("bar\n") 527 528 state := testState() 529 statePath := testStateFile(t, state) 530 531 p := testProvider() 532 ui := new(cli.MockUi) 533 c := &RefreshCommand{ 534 Meta: Meta{ 535 ContextOpts: testCtxConfig(p), 536 Ui: ui, 537 }, 538 } 539 540 args := []string{ 541 "-state", statePath, 542 testFixturePath("refresh-unset-var"), 543 } 544 if code := c.Run(args); code != 0 { 545 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 546 } 547 } 548 549 func TestRefresh_backup(t *testing.T) { 550 state := testState() 551 statePath := testStateFile(t, state) 552 553 // Output path 554 outf, err := ioutil.TempFile("", "tf") 555 if err != nil { 556 t.Fatalf("err: %s", err) 557 } 558 outPath := outf.Name() 559 outf.Close() 560 os.Remove(outPath) 561 562 // Backup path 563 backupf, err := ioutil.TempFile("", "tf") 564 if err != nil { 565 t.Fatalf("err: %s", err) 566 } 567 backupPath := backupf.Name() 568 backupf.Close() 569 os.Remove(backupPath) 570 571 p := testProvider() 572 ui := new(cli.MockUi) 573 c := &RefreshCommand{ 574 Meta: Meta{ 575 ContextOpts: testCtxConfig(p), 576 Ui: ui, 577 }, 578 } 579 580 p.RefreshFn = nil 581 p.RefreshReturn = newInstanceState("yes") 582 583 args := []string{ 584 "-state", statePath, 585 "-state-out", outPath, 586 "-backup", backupPath, 587 testFixturePath("refresh"), 588 } 589 if code := c.Run(args); code != 0 { 590 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 591 } 592 593 f, err := os.Open(statePath) 594 if err != nil { 595 t.Fatalf("err: %s", err) 596 } 597 598 newState, err := terraform.ReadState(f) 599 f.Close() 600 if err != nil { 601 t.Fatalf("err: %s", err) 602 } 603 604 if !reflect.DeepEqual(newState, state) { 605 t.Fatalf("bad: %#v", newState) 606 } 607 608 f, err = os.Open(outPath) 609 if err != nil { 610 t.Fatalf("err: %s", err) 611 } 612 613 newState, err = terraform.ReadState(f) 614 f.Close() 615 if err != nil { 616 t.Fatalf("err: %s", err) 617 } 618 619 actual := newState.RootModule().Resources["test_instance.foo"].Primary 620 expected := p.RefreshReturn 621 if !reflect.DeepEqual(actual, expected) { 622 t.Fatalf("bad: %#v", actual) 623 } 624 625 f, err = os.Open(backupPath) 626 if err != nil { 627 t.Fatalf("err: %s", err) 628 } 629 630 backupState, err := terraform.ReadState(f) 631 f.Close() 632 if err != nil { 633 t.Fatalf("err: %s", err) 634 } 635 636 actualStr := strings.TrimSpace(backupState.String()) 637 expectedStr := strings.TrimSpace(state.String()) 638 if actualStr != expectedStr { 639 t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr) 640 } 641 } 642 643 func TestRefresh_disableBackup(t *testing.T) { 644 state := testState() 645 statePath := testStateFile(t, state) 646 647 // Output path 648 outf, err := ioutil.TempFile("", "tf") 649 if err != nil { 650 t.Fatalf("err: %s", err) 651 } 652 outPath := outf.Name() 653 outf.Close() 654 os.Remove(outPath) 655 656 p := testProvider() 657 ui := new(cli.MockUi) 658 c := &RefreshCommand{ 659 Meta: Meta{ 660 ContextOpts: testCtxConfig(p), 661 Ui: ui, 662 }, 663 } 664 665 p.RefreshFn = nil 666 p.RefreshReturn = newInstanceState("yes") 667 668 args := []string{ 669 "-state", statePath, 670 "-state-out", outPath, 671 "-backup", "-", 672 testFixturePath("refresh"), 673 } 674 if code := c.Run(args); code != 0 { 675 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 676 } 677 678 f, err := os.Open(statePath) 679 if err != nil { 680 t.Fatalf("err: %s", err) 681 } 682 683 newState, err := terraform.ReadState(f) 684 f.Close() 685 if err != nil { 686 t.Fatalf("err: %s", err) 687 } 688 689 if !reflect.DeepEqual(newState, state) { 690 t.Fatalf("bad: %#v", newState) 691 } 692 693 f, err = os.Open(outPath) 694 if err != nil { 695 t.Fatalf("err: %s", err) 696 } 697 698 newState, err = terraform.ReadState(f) 699 f.Close() 700 if err != nil { 701 t.Fatalf("err: %s", err) 702 } 703 704 actual := newState.RootModule().Resources["test_instance.foo"].Primary 705 expected := p.RefreshReturn 706 if !reflect.DeepEqual(actual, expected) { 707 t.Fatalf("bad: %#v", actual) 708 } 709 710 // Ensure there is no backup 711 _, err = os.Stat(outPath + DefaultBackupExtension) 712 if err == nil || !os.IsNotExist(err) { 713 t.Fatalf("backup should not exist") 714 } 715 } 716 717 func TestRefresh_displaysOutputs(t *testing.T) { 718 state := testState() 719 statePath := testStateFile(t, state) 720 721 p := testProvider() 722 ui := new(cli.MockUi) 723 c := &RefreshCommand{ 724 Meta: Meta{ 725 ContextOpts: testCtxConfig(p), 726 Ui: ui, 727 }, 728 } 729 730 args := []string{ 731 "-state", statePath, 732 testFixturePath("refresh-output"), 733 } 734 if code := c.Run(args); code != 0 { 735 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 736 } 737 738 // Test that outputs were displayed 739 outputValue := "foo.example.com" 740 actual := ui.OutputWriter.String() 741 if !strings.Contains(actual, outputValue) { 742 t.Fatalf("Expected:\n%s\n\nTo include: %q", actual, outputValue) 743 } 744 } 745 746 // When creating an InstaneState for direct comparison to one contained in 747 // terraform.State, all fields must be initialized (duplicating the 748 // InstanceState.init() method) 749 func newInstanceState(id string) *terraform.InstanceState { 750 return &terraform.InstanceState{ 751 ID: id, 752 Attributes: make(map[string]string), 753 Ephemeral: terraform.EphemeralState{ 754 ConnInfo: make(map[string]string), 755 }, 756 Meta: make(map[string]string), 757 } 758 } 759 760 const refreshVarFile = ` 761 foo = "bar" 762 ` 763 764 const testRefreshStr = ` 765 test_instance.foo: 766 ID = yes 767 ` 768 const testRefreshCwdStr = ` 769 test_instance.foo: 770 ID = yes 771 `