github.com/LorbusChris/terraform@v0.11.12-beta1/command/import_test.go (about) 1 package command 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "strings" 9 "testing" 10 11 "github.com/hashicorp/terraform/helper/copy" 12 "github.com/hashicorp/terraform/plugin" 13 "github.com/hashicorp/terraform/plugin/discovery" 14 "github.com/hashicorp/terraform/terraform" 15 "github.com/mitchellh/cli" 16 ) 17 18 func TestImport(t *testing.T) { 19 defer testChdir(t, testFixturePath("import-provider-implicit"))() 20 21 statePath := testTempFile(t) 22 23 p := testProvider() 24 ui := new(cli.MockUi) 25 c := &ImportCommand{ 26 Meta: Meta{ 27 testingOverrides: metaOverridesForProvider(p), 28 Ui: ui, 29 }, 30 } 31 32 p.ImportStateFn = nil 33 p.ImportStateReturn = []*terraform.InstanceState{ 34 &terraform.InstanceState{ 35 ID: "yay", 36 Ephemeral: terraform.EphemeralState{ 37 Type: "test_instance", 38 }, 39 }, 40 } 41 42 args := []string{ 43 "-state", statePath, 44 "test_instance.foo", 45 "bar", 46 } 47 if code := c.Run(args); code != 0 { 48 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 49 } 50 51 if !p.ImportStateCalled { 52 t.Fatal("ImportState should be called") 53 } 54 55 testStateOutput(t, statePath, testImportStr) 56 } 57 58 func TestImport_providerConfig(t *testing.T) { 59 defer testChdir(t, testFixturePath("import-provider"))() 60 61 statePath := testTempFile(t) 62 63 p := testProvider() 64 ui := new(cli.MockUi) 65 c := &ImportCommand{ 66 Meta: Meta{ 67 testingOverrides: metaOverridesForProvider(p), 68 Ui: ui, 69 }, 70 } 71 72 p.ImportStateFn = nil 73 p.ImportStateReturn = []*terraform.InstanceState{ 74 &terraform.InstanceState{ 75 ID: "yay", 76 Ephemeral: terraform.EphemeralState{ 77 Type: "test_instance", 78 }, 79 }, 80 } 81 82 configured := false 83 p.ConfigureFn = func(c *terraform.ResourceConfig) error { 84 configured = true 85 86 if v, ok := c.Get("foo"); !ok || v.(string) != "bar" { 87 return fmt.Errorf("bad value: %#v", v) 88 } 89 90 return nil 91 } 92 93 args := []string{ 94 "-state", statePath, 95 "test_instance.foo", 96 "bar", 97 } 98 if code := c.Run(args); code != 0 { 99 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 100 } 101 102 // Verify that we were called 103 if !configured { 104 t.Fatal("Configure should be called") 105 } 106 107 if !p.ImportStateCalled { 108 t.Fatal("ImportState should be called") 109 } 110 111 testStateOutput(t, statePath, testImportStr) 112 } 113 114 // "remote" state provided by the "local" backend 115 func TestImport_remoteState(t *testing.T) { 116 td := tempDir(t) 117 copy.CopyDir(testFixturePath("import-provider-remote-state"), td) 118 defer os.RemoveAll(td) 119 defer testChdir(t, td)() 120 121 statePath := "imported.tfstate" 122 123 // init our backend 124 ui := new(cli.MockUi) 125 m := Meta{ 126 testingOverrides: metaOverridesForProvider(testProvider()), 127 Ui: ui, 128 } 129 130 ic := &InitCommand{ 131 Meta: m, 132 providerInstaller: &mockProviderInstaller{ 133 Providers: map[string][]string{ 134 "test": []string{"1.2.3"}, 135 }, 136 137 Dir: m.pluginDir(), 138 }, 139 } 140 141 if code := ic.Run([]string{}); code != 0 { 142 t.Fatalf("bad: \n%s", ui.ErrorWriter) 143 } 144 145 p := testProvider() 146 ui = new(cli.MockUi) 147 c := &ImportCommand{ 148 Meta: Meta{ 149 testingOverrides: metaOverridesForProvider(p), 150 Ui: ui, 151 }, 152 } 153 154 p.ImportStateFn = nil 155 p.ImportStateReturn = []*terraform.InstanceState{ 156 &terraform.InstanceState{ 157 ID: "yay", 158 Ephemeral: terraform.EphemeralState{ 159 Type: "test_instance", 160 }, 161 }, 162 } 163 164 configured := false 165 p.ConfigureFn = func(c *terraform.ResourceConfig) error { 166 configured = true 167 168 if v, ok := c.Get("foo"); !ok || v.(string) != "bar" { 169 return fmt.Errorf("bad value: %#v", v) 170 } 171 172 return nil 173 } 174 175 args := []string{ 176 "test_instance.foo", 177 "bar", 178 } 179 180 if code := c.Run(args); code != 0 { 181 fmt.Println(ui.OutputWriter) 182 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 183 } 184 185 // verify that the local state was unlocked after import 186 if _, err := os.Stat(filepath.Join(td, fmt.Sprintf(".%s.lock.info", statePath))); !os.IsNotExist(err) { 187 t.Fatal("state left locked after import") 188 } 189 190 // Verify that we were called 191 if !configured { 192 t.Fatal("Configure should be called") 193 } 194 195 if !p.ImportStateCalled { 196 t.Fatal("ImportState should be called") 197 } 198 199 testStateOutput(t, statePath, testImportStr) 200 } 201 202 func TestImport_providerConfigWithVar(t *testing.T) { 203 defer testChdir(t, testFixturePath("import-provider-var"))() 204 205 statePath := testTempFile(t) 206 207 p := testProvider() 208 ui := new(cli.MockUi) 209 c := &ImportCommand{ 210 Meta: Meta{ 211 testingOverrides: metaOverridesForProvider(p), 212 Ui: ui, 213 }, 214 } 215 216 p.ImportStateFn = nil 217 p.ImportStateReturn = []*terraform.InstanceState{ 218 &terraform.InstanceState{ 219 ID: "yay", 220 Ephemeral: terraform.EphemeralState{ 221 Type: "test_instance", 222 }, 223 }, 224 } 225 226 configured := false 227 p.ConfigureFn = func(c *terraform.ResourceConfig) error { 228 configured = true 229 230 if v, ok := c.Get("foo"); !ok || v.(string) != "bar" { 231 return fmt.Errorf("bad value: %#v", v) 232 } 233 234 return nil 235 } 236 237 args := []string{ 238 "-state", statePath, 239 "-var", "foo=bar", 240 "test_instance.foo", 241 "bar", 242 } 243 if code := c.Run(args); code != 0 { 244 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 245 } 246 247 // Verify that we were called 248 if !configured { 249 t.Fatal("Configure should be called") 250 } 251 252 if !p.ImportStateCalled { 253 t.Fatal("ImportState should be called") 254 } 255 256 testStateOutput(t, statePath, testImportStr) 257 } 258 259 func TestImport_providerConfigWithVarDefault(t *testing.T) { 260 defer testChdir(t, testFixturePath("import-provider-var-default"))() 261 262 statePath := testTempFile(t) 263 264 p := testProvider() 265 ui := new(cli.MockUi) 266 c := &ImportCommand{ 267 Meta: Meta{ 268 testingOverrides: metaOverridesForProvider(p), 269 Ui: ui, 270 }, 271 } 272 273 p.ImportStateFn = nil 274 p.ImportStateReturn = []*terraform.InstanceState{ 275 &terraform.InstanceState{ 276 ID: "yay", 277 Ephemeral: terraform.EphemeralState{ 278 Type: "test_instance", 279 }, 280 }, 281 } 282 283 configured := false 284 p.ConfigureFn = func(c *terraform.ResourceConfig) error { 285 configured = true 286 287 if v, ok := c.Get("foo"); !ok || v.(string) != "bar" { 288 return fmt.Errorf("bad value: %#v", v) 289 } 290 291 return nil 292 } 293 294 args := []string{ 295 "-state", statePath, 296 "test_instance.foo", 297 "bar", 298 } 299 if code := c.Run(args); code != 0 { 300 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 301 } 302 303 // Verify that we were called 304 if !configured { 305 t.Fatal("Configure should be called") 306 } 307 308 if !p.ImportStateCalled { 309 t.Fatal("ImportState should be called") 310 } 311 312 testStateOutput(t, statePath, testImportStr) 313 } 314 315 func TestImport_providerConfigWithVarFile(t *testing.T) { 316 defer testChdir(t, testFixturePath("import-provider-var-file"))() 317 318 statePath := testTempFile(t) 319 320 p := testProvider() 321 ui := new(cli.MockUi) 322 c := &ImportCommand{ 323 Meta: Meta{ 324 testingOverrides: metaOverridesForProvider(p), 325 Ui: ui, 326 }, 327 } 328 329 p.ImportStateFn = nil 330 p.ImportStateReturn = []*terraform.InstanceState{ 331 &terraform.InstanceState{ 332 ID: "yay", 333 Ephemeral: terraform.EphemeralState{ 334 Type: "test_instance", 335 }, 336 }, 337 } 338 339 configured := false 340 p.ConfigureFn = func(c *terraform.ResourceConfig) error { 341 configured = true 342 343 if v, ok := c.Get("foo"); !ok || v.(string) != "bar" { 344 return fmt.Errorf("bad value: %#v", v) 345 } 346 347 return nil 348 } 349 350 args := []string{ 351 "-state", statePath, 352 "-var-file", "blah.tfvars", 353 "test_instance.foo", 354 "bar", 355 } 356 if code := c.Run(args); code != 0 { 357 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 358 } 359 360 // Verify that we were called 361 if !configured { 362 t.Fatal("Configure should be called") 363 } 364 365 if !p.ImportStateCalled { 366 t.Fatal("ImportState should be called") 367 } 368 369 testStateOutput(t, statePath, testImportStr) 370 } 371 372 func TestImport_customProvider(t *testing.T) { 373 defer testChdir(t, testFixturePath("import-provider-aliased"))() 374 375 statePath := testTempFile(t) 376 377 p := testProvider() 378 ui := new(cli.MockUi) 379 c := &ImportCommand{ 380 Meta: Meta{ 381 testingOverrides: metaOverridesForProvider(p), 382 Ui: ui, 383 }, 384 } 385 386 p.ImportStateFn = nil 387 p.ImportStateReturn = []*terraform.InstanceState{ 388 &terraform.InstanceState{ 389 ID: "yay", 390 Ephemeral: terraform.EphemeralState{ 391 Type: "test_instance", 392 }, 393 }, 394 } 395 396 args := []string{ 397 "-provider", "test.alias", 398 "-state", statePath, 399 "test_instance.foo", 400 "bar", 401 } 402 if code := c.Run(args); code != 0 { 403 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 404 } 405 406 if !p.ImportStateCalled { 407 t.Fatal("ImportState should be called") 408 } 409 410 testStateOutput(t, statePath, testImportCustomProviderStr) 411 } 412 413 func TestImport_allowMissingResourceConfig(t *testing.T) { 414 defer testChdir(t, testFixturePath("import-missing-resource-config"))() 415 416 statePath := testTempFile(t) 417 418 p := testProvider() 419 ui := new(cli.MockUi) 420 c := &ImportCommand{ 421 Meta: Meta{ 422 testingOverrides: metaOverridesForProvider(p), 423 Ui: ui, 424 }, 425 } 426 427 p.ImportStateFn = nil 428 p.ImportStateReturn = []*terraform.InstanceState{ 429 { 430 ID: "yay", 431 Ephemeral: terraform.EphemeralState{ 432 Type: "test_instance", 433 }, 434 }, 435 } 436 437 args := []string{ 438 "-state", statePath, 439 "-allow-missing-config", 440 "test_instance.foo", 441 "bar", 442 } 443 if code := c.Run(args); code != 0 { 444 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 445 } 446 447 if !p.ImportStateCalled { 448 t.Fatal("ImportState should be called") 449 } 450 451 testStateOutput(t, statePath, testImportStr) 452 } 453 454 func TestImport_emptyConfig(t *testing.T) { 455 defer testChdir(t, testFixturePath("empty"))() 456 457 statePath := testTempFile(t) 458 459 p := testProvider() 460 ui := new(cli.MockUi) 461 c := &ImportCommand{ 462 Meta: Meta{ 463 testingOverrides: metaOverridesForProvider(p), 464 Ui: ui, 465 }, 466 } 467 468 args := []string{ 469 "-state", statePath, 470 "test_instance.foo", 471 "bar", 472 } 473 code := c.Run(args) 474 if code != 1 { 475 t.Fatalf("import succeeded; expected failure") 476 } 477 478 msg := ui.ErrorWriter.String() 479 if want := `No Terraform configuration files`; !strings.Contains(msg, want) { 480 t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) 481 } 482 } 483 484 func TestImport_missingResourceConfig(t *testing.T) { 485 defer testChdir(t, testFixturePath("import-missing-resource-config"))() 486 487 statePath := testTempFile(t) 488 489 p := testProvider() 490 ui := new(cli.MockUi) 491 c := &ImportCommand{ 492 Meta: Meta{ 493 testingOverrides: metaOverridesForProvider(p), 494 Ui: ui, 495 }, 496 } 497 498 args := []string{ 499 "-state", statePath, 500 "test_instance.foo", 501 "bar", 502 } 503 code := c.Run(args) 504 if code != 1 { 505 t.Fatalf("import succeeded; expected failure") 506 } 507 508 msg := ui.ErrorWriter.String() 509 if want := `resource address "test_instance.foo" does not exist`; !strings.Contains(msg, want) { 510 t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) 511 } 512 } 513 514 func TestImport_missingModuleConfig(t *testing.T) { 515 defer testChdir(t, testFixturePath("import-missing-resource-config"))() 516 517 statePath := testTempFile(t) 518 519 p := testProvider() 520 ui := new(cli.MockUi) 521 c := &ImportCommand{ 522 Meta: Meta{ 523 testingOverrides: metaOverridesForProvider(p), 524 Ui: ui, 525 }, 526 } 527 528 args := []string{ 529 "-state", statePath, 530 "module.baz.test_instance.foo", 531 "bar", 532 } 533 code := c.Run(args) 534 if code != 1 { 535 t.Fatalf("import succeeded; expected failure") 536 } 537 538 msg := ui.ErrorWriter.String() 539 if want := `module.baz is not defined in the configuration`; !strings.Contains(msg, want) { 540 t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) 541 } 542 } 543 544 func TestImport_dataResource(t *testing.T) { 545 defer testChdir(t, testFixturePath("import-missing-resource-config"))() 546 547 statePath := testTempFile(t) 548 549 p := testProvider() 550 ui := new(cli.MockUi) 551 c := &ImportCommand{ 552 Meta: Meta{ 553 testingOverrides: metaOverridesForProvider(p), 554 Ui: ui, 555 }, 556 } 557 558 args := []string{ 559 "-state", statePath, 560 "data.test_data_source.foo", 561 "bar", 562 } 563 code := c.Run(args) 564 if code != 1 { 565 t.Fatalf("import succeeded; expected failure") 566 } 567 568 msg := ui.ErrorWriter.String() 569 if want := `resource address must refer to a managed resource`; !strings.Contains(msg, want) { 570 t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) 571 } 572 } 573 574 func TestImport_invalidResourceAddr(t *testing.T) { 575 defer testChdir(t, testFixturePath("import-missing-resource-config"))() 576 577 statePath := testTempFile(t) 578 579 p := testProvider() 580 ui := new(cli.MockUi) 581 c := &ImportCommand{ 582 Meta: Meta{ 583 testingOverrides: metaOverridesForProvider(p), 584 Ui: ui, 585 }, 586 } 587 588 args := []string{ 589 "-state", statePath, 590 "bananas", 591 "bar", 592 } 593 code := c.Run(args) 594 if code != 1 { 595 t.Fatalf("import succeeded; expected failure") 596 } 597 598 msg := ui.ErrorWriter.String() 599 if want := `invalid resource address "bananas"`; !strings.Contains(msg, want) { 600 t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) 601 } 602 } 603 604 func TestImport_targetIsModule(t *testing.T) { 605 defer testChdir(t, testFixturePath("import-missing-resource-config"))() 606 607 statePath := testTempFile(t) 608 609 p := testProvider() 610 ui := new(cli.MockUi) 611 c := &ImportCommand{ 612 Meta: Meta{ 613 testingOverrides: metaOverridesForProvider(p), 614 Ui: ui, 615 }, 616 } 617 618 args := []string{ 619 "-state", statePath, 620 "module.foo", 621 "bar", 622 } 623 code := c.Run(args) 624 if code != 1 { 625 t.Fatalf("import succeeded; expected failure") 626 } 627 628 msg := ui.ErrorWriter.String() 629 if want := `resource address must include a full resource spec`; !strings.Contains(msg, want) { 630 t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) 631 } 632 } 633 634 // make sure we search the full plugin path during import 635 func TestImport_pluginDir(t *testing.T) { 636 td := tempDir(t) 637 copy.CopyDir(testFixturePath("import-provider"), td) 638 defer os.RemoveAll(td) 639 defer testChdir(t, td)() 640 641 // make a fake provider in a custom plugin directory 642 if err := os.Mkdir("plugins", 0755); err != nil { 643 t.Fatal(err) 644 } 645 if err := ioutil.WriteFile("plugins/terraform-provider-test_v1.1.1_x4", []byte("invalid binary"), 0755); err != nil { 646 t.Fatal(err) 647 } 648 649 ui := new(cli.MockUi) 650 c := &ImportCommand{ 651 Meta: Meta{ 652 Ui: ui, 653 }, 654 } 655 656 // store our custom plugin path, which would normally happen during init 657 if err := c.storePluginPath([]string{"./plugins"}); err != nil { 658 t.Fatal(err) 659 } 660 661 // Now we need to go through some plugin init. 662 // This discovers our fake plugin and writes the lock file. 663 initCmd := &InitCommand{ 664 Meta: Meta{ 665 pluginPath: []string{"./plugins"}, 666 Ui: new(cli.MockUi), 667 }, 668 providerInstaller: &discovery.ProviderInstaller{ 669 PluginProtocolVersion: plugin.Handshake.ProtocolVersion, 670 }, 671 } 672 if err := initCmd.getProviders(".", nil, false); err != nil { 673 t.Fatal(err) 674 } 675 676 args := []string{ 677 "test_instance.foo", 678 "bar", 679 } 680 if code := c.Run(args); code == 0 { 681 t.Fatalf("expected error, got: %s", ui.OutputWriter) 682 } 683 684 outMsg := ui.OutputWriter.String() 685 // if we were missing a plugin, the output will have some explanation 686 // about requirements. If discovery starts verifying binary compatibility, 687 // we will need to write a dummy provider above. 688 if strings.Contains(outMsg, "requirements") { 689 t.Fatal("unexpected output:", outMsg) 690 } 691 692 // We wanted a plugin execution error, rather than a requirement error. 693 // Looking for "exec" in the error should suffice for now. 694 errMsg := ui.ErrorWriter.String() 695 if !strings.Contains(errMsg, "exec") { 696 t.Fatal("unexpected error:", errMsg) 697 } 698 } 699 700 const testImportStr = ` 701 test_instance.foo: 702 ID = yay 703 provider = provider.test 704 ` 705 706 const testImportCustomProviderStr = ` 707 test_instance.foo: 708 ID = yay 709 provider = provider.test.alias 710 `