github.com/tam7t/terraform@v0.7.0-rc2.0.20160705125922-be2469a05c5e/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 93 func TestPlan_noState(t *testing.T) { 94 tmp, cwd := testCwd(t) 95 defer testFixCwd(t, tmp, cwd) 96 97 p := testProvider() 98 ui := new(cli.MockUi) 99 c := &PlanCommand{ 100 Meta: Meta{ 101 ContextOpts: testCtxConfig(p), 102 Ui: ui, 103 }, 104 } 105 106 args := []string{ 107 testFixturePath("plan"), 108 } 109 if code := c.Run(args); code != 0 { 110 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 111 } 112 113 // Verify that refresh was called 114 if p.RefreshCalled { 115 t.Fatal("refresh should not be called") 116 } 117 118 // Verify that the provider was called with the existing state 119 actual := strings.TrimSpace(p.DiffState.String()) 120 expected := strings.TrimSpace(testPlanNoStateStr) 121 if actual != expected { 122 t.Fatalf("bad:\n\n%s", actual) 123 } 124 } 125 126 func TestPlan_outPath(t *testing.T) { 127 tf, err := ioutil.TempFile("", "tf") 128 if err != nil { 129 t.Fatalf("err: %s", err) 130 } 131 outPath := tf.Name() 132 os.Remove(tf.Name()) 133 134 p := testProvider() 135 ui := new(cli.MockUi) 136 c := &PlanCommand{ 137 Meta: Meta{ 138 ContextOpts: testCtxConfig(p), 139 Ui: ui, 140 }, 141 } 142 143 p.DiffReturn = &terraform.InstanceDiff{ 144 Destroy: true, 145 } 146 147 args := []string{ 148 "-out", outPath, 149 testFixturePath("plan"), 150 } 151 if code := c.Run(args); code != 0 { 152 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 153 } 154 155 f, err := os.Open(outPath) 156 if err != nil { 157 t.Fatalf("err: %s", err) 158 } 159 defer f.Close() 160 161 if _, err := terraform.ReadPlan(f); err != nil { 162 t.Fatalf("err: %s", err) 163 } 164 } 165 166 func TestPlan_outPathNoChange(t *testing.T) { 167 originalState := &terraform.State{ 168 Modules: []*terraform.ModuleState{ 169 &terraform.ModuleState{ 170 Path: []string{"root"}, 171 Resources: map[string]*terraform.ResourceState{ 172 "test_instance.foo": &terraform.ResourceState{ 173 Type: "test_instance", 174 Primary: &terraform.InstanceState{ 175 ID: "bar", 176 }, 177 }, 178 }, 179 }, 180 }, 181 } 182 statePath := testStateFile(t, originalState) 183 184 tf, err := ioutil.TempFile("", "tf") 185 if err != nil { 186 t.Fatalf("err: %s", err) 187 } 188 outPath := tf.Name() 189 os.Remove(tf.Name()) 190 191 p := testProvider() 192 ui := new(cli.MockUi) 193 c := &PlanCommand{ 194 Meta: Meta{ 195 ContextOpts: testCtxConfig(p), 196 Ui: ui, 197 }, 198 } 199 200 args := []string{ 201 "-out", outPath, 202 "-state", statePath, 203 testFixturePath("plan"), 204 } 205 if code := c.Run(args); code != 0 { 206 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 207 } 208 209 plan := testReadPlan(t, outPath) 210 if !plan.Diff.Empty() { 211 t.Fatalf("Expected empty plan to be written to plan file, got: %s", plan) 212 } 213 } 214 215 func TestPlan_refresh(t *testing.T) { 216 p := testProvider() 217 ui := new(cli.MockUi) 218 c := &PlanCommand{ 219 Meta: Meta{ 220 ContextOpts: testCtxConfig(p), 221 Ui: ui, 222 }, 223 } 224 225 args := []string{ 226 "-refresh=false", 227 testFixturePath("plan"), 228 } 229 if code := c.Run(args); code != 0 { 230 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 231 } 232 233 if p.RefreshCalled { 234 t.Fatal("refresh should not be called") 235 } 236 } 237 238 func TestPlan_state(t *testing.T) { 239 // Write out some prior state 240 tf, err := ioutil.TempFile("", "tf") 241 if err != nil { 242 t.Fatalf("err: %s", err) 243 } 244 statePath := tf.Name() 245 defer os.Remove(tf.Name()) 246 247 originalState := testState() 248 err = terraform.WriteState(originalState, tf) 249 tf.Close() 250 if err != nil { 251 t.Fatalf("err: %s", err) 252 } 253 254 p := testProvider() 255 ui := new(cli.MockUi) 256 c := &PlanCommand{ 257 Meta: Meta{ 258 ContextOpts: testCtxConfig(p), 259 Ui: ui, 260 }, 261 } 262 263 args := []string{ 264 "-state", statePath, 265 testFixturePath("plan"), 266 } 267 if code := c.Run(args); code != 0 { 268 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 269 } 270 271 // Verify that the provider was called with the existing state 272 actual := strings.TrimSpace(p.DiffState.String()) 273 expected := strings.TrimSpace(testPlanStateStr) 274 if actual != expected { 275 t.Fatalf("bad:\n\n%s", actual) 276 } 277 } 278 279 func TestPlan_stateDefault(t *testing.T) { 280 originalState := testState() 281 282 // Write the state file in a temporary directory with the 283 // default filename. 284 td, err := ioutil.TempDir("", "tf") 285 if err != nil { 286 t.Fatalf("err: %s", err) 287 } 288 statePath := filepath.Join(td, DefaultStateFilename) 289 290 f, err := os.Create(statePath) 291 if err != nil { 292 t.Fatalf("err: %s", err) 293 } 294 err = terraform.WriteState(originalState, f) 295 f.Close() 296 if err != nil { 297 t.Fatalf("err: %s", err) 298 } 299 300 // Change to that directory 301 cwd, err := os.Getwd() 302 if err != nil { 303 t.Fatalf("err: %s", err) 304 } 305 if err := os.Chdir(filepath.Dir(statePath)); err != nil { 306 t.Fatalf("err: %s", err) 307 } 308 defer os.Chdir(cwd) 309 310 p := testProvider() 311 ui := new(cli.MockUi) 312 c := &PlanCommand{ 313 Meta: Meta{ 314 ContextOpts: testCtxConfig(p), 315 Ui: ui, 316 }, 317 } 318 319 args := []string{ 320 testFixturePath("plan"), 321 } 322 if code := c.Run(args); code != 0 { 323 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 324 } 325 326 // Verify that the provider was called with the existing state 327 actual := strings.TrimSpace(p.DiffState.String()) 328 expected := strings.TrimSpace(testPlanStateDefaultStr) 329 if actual != expected { 330 t.Fatalf("bad:\n\n%s", actual) 331 } 332 } 333 334 func TestPlan_stateFuture(t *testing.T) { 335 originalState := testState() 336 originalState.TFVersion = "99.99.99" 337 statePath := testStateFile(t, originalState) 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 args := []string{ 349 "-state", statePath, 350 testFixturePath("plan"), 351 } 352 if code := c.Run(args); code == 0 { 353 t.Fatal("should fail") 354 } 355 356 f, err := os.Open(statePath) 357 if err != nil { 358 t.Fatalf("err: %s", err) 359 } 360 361 newState, err := terraform.ReadState(f) 362 f.Close() 363 if err != nil { 364 t.Fatalf("err: %s", err) 365 } 366 367 if !newState.Equal(originalState) { 368 t.Fatalf("bad: %#v", newState) 369 } 370 if newState.TFVersion != originalState.TFVersion { 371 t.Fatalf("bad: %#v", newState) 372 } 373 } 374 375 func TestPlan_statePast(t *testing.T) { 376 originalState := testState() 377 originalState.TFVersion = "0.1.0" 378 statePath := testStateFile(t, originalState) 379 380 p := testProvider() 381 ui := new(cli.MockUi) 382 c := &PlanCommand{ 383 Meta: Meta{ 384 ContextOpts: testCtxConfig(p), 385 Ui: ui, 386 }, 387 } 388 389 args := []string{ 390 "-state", statePath, 391 testFixturePath("plan"), 392 } 393 if code := c.Run(args); code != 0 { 394 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 395 } 396 } 397 398 func TestPlan_vars(t *testing.T) { 399 p := testProvider() 400 ui := new(cli.MockUi) 401 c := &PlanCommand{ 402 Meta: Meta{ 403 ContextOpts: testCtxConfig(p), 404 Ui: ui, 405 }, 406 } 407 408 actual := "" 409 p.DiffFn = func( 410 info *terraform.InstanceInfo, 411 s *terraform.InstanceState, 412 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 413 if v, ok := c.Config["value"]; ok { 414 actual = v.(string) 415 } 416 417 return nil, nil 418 } 419 420 args := []string{ 421 "-var", "foo=bar", 422 testFixturePath("plan-vars"), 423 } 424 if code := c.Run(args); code != 0 { 425 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 426 } 427 428 if actual != "bar" { 429 t.Fatal("didn't work") 430 } 431 } 432 433 func TestPlan_varsUnset(t *testing.T) { 434 // Disable test mode so input would be asked 435 test = false 436 defer func() { test = true }() 437 438 defaultInputReader = bytes.NewBufferString("bar\n") 439 440 p := testProvider() 441 ui := new(cli.MockUi) 442 c := &PlanCommand{ 443 Meta: Meta{ 444 ContextOpts: testCtxConfig(p), 445 Ui: ui, 446 }, 447 } 448 449 args := []string{ 450 testFixturePath("plan-vars"), 451 } 452 if code := c.Run(args); code != 0 { 453 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 454 } 455 } 456 457 func TestPlan_varFile(t *testing.T) { 458 varFilePath := testTempFile(t) 459 if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); 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 actual := "" 473 p.DiffFn = func( 474 info *terraform.InstanceInfo, 475 s *terraform.InstanceState, 476 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 477 if v, ok := c.Config["value"]; ok { 478 actual = v.(string) 479 } 480 481 return nil, nil 482 } 483 484 args := []string{ 485 "-var-file", varFilePath, 486 testFixturePath("plan-vars"), 487 } 488 if code := c.Run(args); code != 0 { 489 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 490 } 491 492 if actual != "bar" { 493 t.Fatal("didn't work") 494 } 495 } 496 497 func TestPlan_varFileDefault(t *testing.T) { 498 varFileDir := testTempDir(t) 499 varFilePath := filepath.Join(varFileDir, "terraform.tfvars") 500 if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); err != nil { 501 t.Fatalf("err: %s", err) 502 } 503 504 cwd, err := os.Getwd() 505 if err != nil { 506 t.Fatalf("err: %s", err) 507 } 508 if err := os.Chdir(varFileDir); err != nil { 509 t.Fatalf("err: %s", err) 510 } 511 defer os.Chdir(cwd) 512 513 p := testProvider() 514 ui := new(cli.MockUi) 515 c := &PlanCommand{ 516 Meta: Meta{ 517 ContextOpts: testCtxConfig(p), 518 Ui: ui, 519 }, 520 } 521 522 actual := "" 523 p.DiffFn = func( 524 info *terraform.InstanceInfo, 525 s *terraform.InstanceState, 526 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 527 if v, ok := c.Config["value"]; ok { 528 actual = v.(string) 529 } 530 531 return nil, nil 532 } 533 534 args := []string{ 535 testFixturePath("plan-vars"), 536 } 537 if code := c.Run(args); code != 0 { 538 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 539 } 540 541 if actual != "bar" { 542 t.Fatal("didn't work") 543 } 544 } 545 546 func TestPlan_detailedExitcode(t *testing.T) { 547 cwd, err := os.Getwd() 548 if err != nil { 549 t.Fatalf("err: %s", err) 550 } 551 if err := os.Chdir(testFixturePath("plan")); err != nil { 552 t.Fatalf("err: %s", err) 553 } 554 defer os.Chdir(cwd) 555 556 p := testProvider() 557 ui := new(cli.MockUi) 558 c := &PlanCommand{ 559 Meta: Meta{ 560 ContextOpts: testCtxConfig(p), 561 Ui: ui, 562 }, 563 } 564 565 args := []string{"-detailed-exitcode"} 566 if code := c.Run(args); code != 2 { 567 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 568 } 569 } 570 571 func TestPlan_detailedExitcode_emptyDiff(t *testing.T) { 572 cwd, err := os.Getwd() 573 if err != nil { 574 t.Fatalf("err: %s", err) 575 } 576 if err := os.Chdir(testFixturePath("plan-emptydiff")); err != nil { 577 t.Fatalf("err: %s", err) 578 } 579 defer os.Chdir(cwd) 580 581 p := testProvider() 582 ui := new(cli.MockUi) 583 c := &PlanCommand{ 584 Meta: Meta{ 585 ContextOpts: testCtxConfig(p), 586 Ui: ui, 587 }, 588 } 589 590 args := []string{"-detailed-exitcode"} 591 if code := c.Run(args); code != 0 { 592 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 593 } 594 } 595 596 const planVarFile = ` 597 foo = "bar" 598 ` 599 600 const testPlanNoStateStr = ` 601 <not created> 602 ` 603 604 const testPlanStateStr = ` 605 ID = bar 606 Tainted = false 607 ` 608 609 const testPlanStateDefaultStr = ` 610 ID = bar 611 Tainted = false 612 `