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