github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/command/import_test.go (about) 1 package command 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "log" 7 "os" 8 "path/filepath" 9 "strings" 10 "testing" 11 12 "github.com/mitchellh/cli" 13 "github.com/zclconf/go-cty/cty" 14 15 "github.com/hashicorp/terraform/addrs" 16 "github.com/hashicorp/terraform/configs/configschema" 17 "github.com/hashicorp/terraform/helper/copy" 18 "github.com/hashicorp/terraform/plugin/discovery" 19 "github.com/hashicorp/terraform/providers" 20 "github.com/hashicorp/terraform/terraform" 21 "github.com/hashicorp/terraform/tfdiags" 22 ) 23 24 func TestImport(t *testing.T) { 25 defer testChdir(t, testFixturePath("import-provider-implicit"))() 26 27 statePath := testTempFile(t) 28 29 p := testProvider() 30 ui := new(cli.MockUi) 31 c := &ImportCommand{ 32 Meta: Meta{ 33 testingOverrides: metaOverridesForProvider(p), 34 Ui: ui, 35 }, 36 } 37 38 p.ImportResourceStateFn = nil 39 p.ImportResourceStateResponse = providers.ImportResourceStateResponse{ 40 ImportedResources: []providers.ImportedResource{ 41 { 42 TypeName: "test_instance", 43 State: cty.ObjectVal(map[string]cty.Value{ 44 "id": cty.StringVal("yay"), 45 }), 46 }, 47 }, 48 } 49 p.GetSchemaReturn = &terraform.ProviderSchema{ 50 ResourceTypes: map[string]*configschema.Block{ 51 "test_instance": { 52 Attributes: map[string]*configschema.Attribute{ 53 "id": {Type: cty.String, Optional: true, Computed: true}, 54 }, 55 }, 56 }, 57 } 58 59 args := []string{ 60 "-state", statePath, 61 "test_instance.foo", 62 "bar", 63 } 64 if code := c.Run(args); code != 0 { 65 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 66 } 67 68 if !p.ImportResourceStateCalled { 69 t.Fatal("ImportResourceState should be called") 70 } 71 72 testStateOutput(t, statePath, testImportStr) 73 } 74 75 func TestImport_providerConfig(t *testing.T) { 76 defer testChdir(t, testFixturePath("import-provider"))() 77 78 statePath := testTempFile(t) 79 80 p := testProvider() 81 ui := new(cli.MockUi) 82 c := &ImportCommand{ 83 Meta: Meta{ 84 testingOverrides: metaOverridesForProvider(p), 85 Ui: ui, 86 }, 87 } 88 89 p.ImportResourceStateFn = nil 90 p.ImportResourceStateResponse = providers.ImportResourceStateResponse{ 91 ImportedResources: []providers.ImportedResource{ 92 { 93 TypeName: "test_instance", 94 State: cty.ObjectVal(map[string]cty.Value{ 95 "id": cty.StringVal("yay"), 96 }), 97 }, 98 }, 99 } 100 p.GetSchemaReturn = &terraform.ProviderSchema{ 101 Provider: &configschema.Block{ 102 Attributes: map[string]*configschema.Attribute{ 103 "foo": {Type: cty.String, Optional: true}, 104 }, 105 }, 106 ResourceTypes: map[string]*configschema.Block{ 107 "test_instance": { 108 Attributes: map[string]*configschema.Attribute{ 109 "id": {Type: cty.String, Optional: true, Computed: true}, 110 }, 111 }, 112 }, 113 } 114 115 configured := false 116 p.ConfigureNewFn = func(req providers.ConfigureRequest) providers.ConfigureResponse { 117 configured = true 118 119 cfg := req.Config 120 if !cfg.Type().HasAttribute("foo") { 121 return providers.ConfigureResponse{ 122 Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("configuration has no foo argument")), 123 } 124 } 125 if got, want := cfg.GetAttr("foo"), cty.StringVal("bar"); !want.RawEquals(got) { 126 return providers.ConfigureResponse{ 127 Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("foo argument is %#v, but want %#v", got, want)), 128 } 129 } 130 131 return providers.ConfigureResponse{} 132 } 133 134 args := []string{ 135 "-state", statePath, 136 "test_instance.foo", 137 "bar", 138 } 139 if code := c.Run(args); code != 0 { 140 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 141 } 142 143 // Verify that we were called 144 if !configured { 145 t.Fatal("Configure should be called") 146 } 147 148 if !p.ImportResourceStateCalled { 149 t.Fatal("ImportResourceState should be called") 150 } 151 152 testStateOutput(t, statePath, testImportStr) 153 } 154 155 // "remote" state provided by the "local" backend 156 func TestImport_remoteState(t *testing.T) { 157 td := tempDir(t) 158 copy.CopyDir(testFixturePath("import-provider-remote-state"), td) 159 defer os.RemoveAll(td) 160 defer testChdir(t, td)() 161 162 statePath := "imported.tfstate" 163 164 // init our backend 165 ui := cli.NewMockUi() 166 m := Meta{ 167 testingOverrides: metaOverridesForProvider(testProvider()), 168 Ui: ui, 169 } 170 171 ic := &InitCommand{ 172 Meta: m, 173 providerInstaller: &mockProviderInstaller{ 174 Providers: map[string][]string{ 175 "test": []string{"1.2.3"}, 176 }, 177 178 Dir: m.pluginDir(), 179 }, 180 } 181 182 // (Using log here rather than t.Log so that these messages interleave with other trace logs) 183 log.Print("[TRACE] TestImport_remoteState running: terraform init") 184 if code := ic.Run([]string{}); code != 0 { 185 t.Fatalf("init failed\n%s", ui.ErrorWriter) 186 } 187 188 p := testProvider() 189 ui = new(cli.MockUi) 190 c := &ImportCommand{ 191 Meta: Meta{ 192 testingOverrides: metaOverridesForProvider(p), 193 Ui: ui, 194 }, 195 } 196 197 p.ImportResourceStateFn = nil 198 p.ImportResourceStateResponse = providers.ImportResourceStateResponse{ 199 ImportedResources: []providers.ImportedResource{ 200 { 201 TypeName: "test_instance", 202 State: cty.ObjectVal(map[string]cty.Value{ 203 "id": cty.StringVal("yay"), 204 }), 205 }, 206 }, 207 } 208 p.GetSchemaReturn = &terraform.ProviderSchema{ 209 Provider: &configschema.Block{ 210 Attributes: map[string]*configschema.Attribute{ 211 "foo": {Type: cty.String, Optional: true}, 212 }, 213 }, 214 ResourceTypes: map[string]*configschema.Block{ 215 "test_instance": { 216 Attributes: map[string]*configschema.Attribute{ 217 "id": {Type: cty.String, Optional: true, Computed: true}, 218 }, 219 }, 220 }, 221 } 222 223 configured := false 224 p.ConfigureNewFn = func(req providers.ConfigureRequest) providers.ConfigureResponse { 225 var diags tfdiags.Diagnostics 226 configured = true 227 if got, want := req.Config.GetAttr("foo"), cty.StringVal("bar"); !want.RawEquals(got) { 228 diags = diags.Append(fmt.Errorf("wrong \"foo\" value %#v; want %#v", got, want)) 229 } 230 return providers.ConfigureResponse{ 231 Diagnostics: diags, 232 } 233 } 234 235 args := []string{ 236 "test_instance.foo", 237 "bar", 238 } 239 log.Printf("[TRACE] TestImport_remoteState running: terraform import %s %s", args[0], args[1]) 240 if code := c.Run(args); code != 0 { 241 fmt.Println(ui.OutputWriter) 242 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 243 } 244 245 // verify that the local state was unlocked after import 246 if _, err := os.Stat(filepath.Join(td, fmt.Sprintf(".%s.lock.info", statePath))); !os.IsNotExist(err) { 247 t.Fatal("state left locked after import") 248 } 249 250 // Verify that we were called 251 if !configured { 252 t.Fatal("Configure should be called") 253 } 254 255 if !p.ImportResourceStateCalled { 256 t.Fatal("ImportResourceState should be called") 257 } 258 259 testStateOutput(t, statePath, testImportStr) 260 } 261 262 // early failure on import should not leave stale lock 263 func TestImport_initializationErrorShouldUnlock(t *testing.T) { 264 td := tempDir(t) 265 copy.CopyDir(testFixturePath("import-provider-remote-state"), td) 266 defer os.RemoveAll(td) 267 defer testChdir(t, td)() 268 269 statePath := "imported.tfstate" 270 271 // init our backend 272 ui := cli.NewMockUi() 273 m := Meta{ 274 testingOverrides: metaOverridesForProvider(testProvider()), 275 Ui: ui, 276 } 277 278 ic := &InitCommand{ 279 Meta: m, 280 providerInstaller: &mockProviderInstaller{ 281 Providers: map[string][]string{ 282 "test": []string{"1.2.3"}, 283 }, 284 285 Dir: m.pluginDir(), 286 }, 287 } 288 289 // (Using log here rather than t.Log so that these messages interleave with other trace logs) 290 log.Print("[TRACE] TestImport_initializationErrorShouldUnlock running: terraform init") 291 if code := ic.Run([]string{}); code != 0 { 292 t.Fatalf("init failed\n%s", ui.ErrorWriter) 293 } 294 295 // overwrite the config with one including a resource from an invalid provider 296 copy.CopyFile(filepath.Join(testFixturePath("import-provider-invalid"), "main.tf"), filepath.Join(td, "main.tf")) 297 298 p := testProvider() 299 ui = new(cli.MockUi) 300 c := &ImportCommand{ 301 Meta: Meta{ 302 testingOverrides: metaOverridesForProvider(p), 303 Ui: ui, 304 }, 305 } 306 307 args := []string{ 308 "unknown_instance.baz", 309 "bar", 310 } 311 log.Printf("[TRACE] TestImport_initializationErrorShouldUnlock running: terraform import %s %s", args[0], args[1]) 312 313 // this should fail 314 if code := c.Run(args); code != 1 { 315 fmt.Println(ui.OutputWriter) 316 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 317 } 318 319 // specifically, it should fail due to a missing provider 320 msg := ui.ErrorWriter.String() 321 if want := "Could not satisfy plugin requirements"; !strings.Contains(msg, want) { 322 t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) 323 } 324 325 // verify that the local state was unlocked after initialization error 326 if _, err := os.Stat(filepath.Join(td, fmt.Sprintf(".%s.lock.info", statePath))); !os.IsNotExist(err) { 327 t.Fatal("state left locked after import") 328 } 329 } 330 331 func TestImport_providerConfigWithVar(t *testing.T) { 332 defer testChdir(t, testFixturePath("import-provider-var"))() 333 334 statePath := testTempFile(t) 335 336 p := testProvider() 337 ui := new(cli.MockUi) 338 c := &ImportCommand{ 339 Meta: Meta{ 340 testingOverrides: metaOverridesForProvider(p), 341 Ui: ui, 342 }, 343 } 344 345 p.ImportResourceStateFn = nil 346 p.ImportResourceStateResponse = providers.ImportResourceStateResponse{ 347 ImportedResources: []providers.ImportedResource{ 348 { 349 TypeName: "test_instance", 350 State: cty.ObjectVal(map[string]cty.Value{ 351 "id": cty.StringVal("yay"), 352 }), 353 }, 354 }, 355 } 356 p.GetSchemaReturn = &terraform.ProviderSchema{ 357 Provider: &configschema.Block{ 358 Attributes: map[string]*configschema.Attribute{ 359 "foo": {Type: cty.String, Optional: true}, 360 }, 361 }, 362 ResourceTypes: map[string]*configschema.Block{ 363 "test_instance": { 364 Attributes: map[string]*configschema.Attribute{ 365 "id": {Type: cty.String, Optional: true, Computed: true}, 366 }, 367 }, 368 }, 369 } 370 371 configured := false 372 p.ConfigureNewFn = func(req providers.ConfigureRequest) providers.ConfigureResponse { 373 var diags tfdiags.Diagnostics 374 configured = true 375 if got, want := req.Config.GetAttr("foo"), cty.StringVal("bar"); !want.RawEquals(got) { 376 diags = diags.Append(fmt.Errorf("wrong \"foo\" value %#v; want %#v", got, want)) 377 } 378 return providers.ConfigureResponse{ 379 Diagnostics: diags, 380 } 381 } 382 383 args := []string{ 384 "-state", statePath, 385 "-var", "foo=bar", 386 "test_instance.foo", 387 "bar", 388 } 389 if code := c.Run(args); code != 0 { 390 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 391 } 392 393 // Verify that we were called 394 if !configured { 395 t.Fatal("Configure should be called") 396 } 397 398 if !p.ImportResourceStateCalled { 399 t.Fatal("ImportResourceState should be called") 400 } 401 402 testStateOutput(t, statePath, testImportStr) 403 } 404 405 func TestImport_providerConfigWithVarDefault(t *testing.T) { 406 defer testChdir(t, testFixturePath("import-provider-var-default"))() 407 408 statePath := testTempFile(t) 409 410 p := testProvider() 411 ui := new(cli.MockUi) 412 c := &ImportCommand{ 413 Meta: Meta{ 414 testingOverrides: metaOverridesForProvider(p), 415 Ui: ui, 416 }, 417 } 418 419 p.ImportResourceStateFn = nil 420 p.ImportResourceStateResponse = providers.ImportResourceStateResponse{ 421 ImportedResources: []providers.ImportedResource{ 422 { 423 TypeName: "test_instance", 424 State: cty.ObjectVal(map[string]cty.Value{ 425 "id": cty.StringVal("yay"), 426 }), 427 }, 428 }, 429 } 430 p.GetSchemaReturn = &terraform.ProviderSchema{ 431 Provider: &configschema.Block{ 432 Attributes: map[string]*configschema.Attribute{ 433 "foo": {Type: cty.String, Optional: true}, 434 }, 435 }, 436 ResourceTypes: map[string]*configschema.Block{ 437 "test_instance": { 438 Attributes: map[string]*configschema.Attribute{ 439 "id": {Type: cty.String, Optional: true, Computed: true}, 440 }, 441 }, 442 }, 443 } 444 445 configured := false 446 p.ConfigureNewFn = func(req providers.ConfigureRequest) providers.ConfigureResponse { 447 var diags tfdiags.Diagnostics 448 configured = true 449 if got, want := req.Config.GetAttr("foo"), cty.StringVal("bar"); !want.RawEquals(got) { 450 diags = diags.Append(fmt.Errorf("wrong \"foo\" value %#v; want %#v", got, want)) 451 } 452 return providers.ConfigureResponse{ 453 Diagnostics: diags, 454 } 455 } 456 457 args := []string{ 458 "-state", statePath, 459 "test_instance.foo", 460 "bar", 461 } 462 if code := c.Run(args); code != 0 { 463 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 464 } 465 466 // Verify that we were called 467 if !configured { 468 t.Fatal("Configure should be called") 469 } 470 471 if !p.ImportResourceStateCalled { 472 t.Fatal("ImportResourceState should be called") 473 } 474 475 testStateOutput(t, statePath, testImportStr) 476 } 477 478 func TestImport_providerConfigWithVarFile(t *testing.T) { 479 defer testChdir(t, testFixturePath("import-provider-var-file"))() 480 481 statePath := testTempFile(t) 482 483 p := testProvider() 484 ui := new(cli.MockUi) 485 c := &ImportCommand{ 486 Meta: Meta{ 487 testingOverrides: metaOverridesForProvider(p), 488 Ui: ui, 489 }, 490 } 491 492 p.ImportResourceStateFn = nil 493 p.ImportResourceStateResponse = providers.ImportResourceStateResponse{ 494 ImportedResources: []providers.ImportedResource{ 495 { 496 TypeName: "test_instance", 497 State: cty.ObjectVal(map[string]cty.Value{ 498 "id": cty.StringVal("yay"), 499 }), 500 }, 501 }, 502 } 503 p.GetSchemaReturn = &terraform.ProviderSchema{ 504 Provider: &configschema.Block{ 505 Attributes: map[string]*configschema.Attribute{ 506 "foo": {Type: cty.String, Optional: true}, 507 }, 508 }, 509 ResourceTypes: map[string]*configschema.Block{ 510 "test_instance": { 511 Attributes: map[string]*configschema.Attribute{ 512 "id": {Type: cty.String, Optional: true, Computed: true}, 513 }, 514 }, 515 }, 516 } 517 518 configured := false 519 p.ConfigureNewFn = func(req providers.ConfigureRequest) providers.ConfigureResponse { 520 var diags tfdiags.Diagnostics 521 configured = true 522 if got, want := req.Config.GetAttr("foo"), cty.StringVal("bar"); !want.RawEquals(got) { 523 diags = diags.Append(fmt.Errorf("wrong \"foo\" value %#v; want %#v", got, want)) 524 } 525 return providers.ConfigureResponse{ 526 Diagnostics: diags, 527 } 528 } 529 530 args := []string{ 531 "-state", statePath, 532 "-var-file", "blah.tfvars", 533 "test_instance.foo", 534 "bar", 535 } 536 if code := c.Run(args); code != 0 { 537 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 538 } 539 540 // Verify that we were called 541 if !configured { 542 t.Fatal("Configure should be called") 543 } 544 545 if !p.ImportResourceStateCalled { 546 t.Fatal("ImportResourceState should be called") 547 } 548 549 testStateOutput(t, statePath, testImportStr) 550 } 551 552 func TestImport_customProvider(t *testing.T) { 553 defer testChdir(t, testFixturePath("import-provider-aliased"))() 554 555 statePath := testTempFile(t) 556 557 p := testProvider() 558 ui := new(cli.MockUi) 559 c := &ImportCommand{ 560 Meta: Meta{ 561 testingOverrides: metaOverridesForProvider(p), 562 Ui: ui, 563 }, 564 } 565 566 p.ImportResourceStateFn = nil 567 p.ImportResourceStateResponse = providers.ImportResourceStateResponse{ 568 ImportedResources: []providers.ImportedResource{ 569 { 570 TypeName: "test_instance", 571 State: cty.ObjectVal(map[string]cty.Value{ 572 "id": cty.StringVal("yay"), 573 }), 574 }, 575 }, 576 } 577 p.GetSchemaReturn = &terraform.ProviderSchema{ 578 Provider: &configschema.Block{ 579 Attributes: map[string]*configschema.Attribute{ 580 "foo": {Type: cty.String, Optional: true}, 581 }, 582 }, 583 ResourceTypes: map[string]*configschema.Block{ 584 "test_instance": { 585 Attributes: map[string]*configschema.Attribute{ 586 "id": {Type: cty.String, Optional: true, Computed: true}, 587 }, 588 }, 589 }, 590 } 591 592 args := []string{ 593 "-provider", "test.alias", 594 "-state", statePath, 595 "test_instance.foo", 596 "bar", 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 !p.ImportResourceStateCalled { 603 t.Fatal("ImportResourceState should be called") 604 } 605 606 testStateOutput(t, statePath, testImportCustomProviderStr) 607 } 608 609 // This tests behavior when the provider name does not match the implied 610 // provider name 611 func TestImport_providerNameMismatch(t *testing.T) { 612 defer testChdir(t, testFixturePath("import-provider-mismatch"))() 613 614 statePath := testTempFile(t) 615 616 p := testProvider() 617 ui := new(cli.MockUi) 618 c := &ImportCommand{ 619 Meta: Meta{ 620 testingOverrides: &testingOverrides{ 621 ProviderResolver: providers.ResolverFixed( 622 map[addrs.Provider]providers.Factory{ 623 addrs.NewLegacyProvider("test-beta"): providers.FactoryFixed(p), 624 }, 625 ), 626 }, 627 Ui: ui, 628 }, 629 } 630 631 configured := false 632 p.ConfigureNewFn = func(req providers.ConfigureRequest) providers.ConfigureResponse { 633 configured = true 634 635 cfg := req.Config 636 if !cfg.Type().HasAttribute("foo") { 637 return providers.ConfigureResponse{ 638 Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("configuration has no foo argument")), 639 } 640 } 641 if got, want := cfg.GetAttr("foo"), cty.StringVal("baz"); !want.RawEquals(got) { 642 return providers.ConfigureResponse{ 643 Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("foo argument is %#v, but want %#v", got, want)), 644 } 645 } 646 647 return providers.ConfigureResponse{} 648 } 649 650 p.ImportResourceStateFn = nil 651 p.ImportResourceStateResponse = providers.ImportResourceStateResponse{ 652 ImportedResources: []providers.ImportedResource{ 653 { 654 TypeName: "test_instance", 655 State: cty.ObjectVal(map[string]cty.Value{ 656 "id": cty.StringVal("yay"), 657 }), 658 }, 659 }, 660 } 661 p.GetSchemaReturn = &terraform.ProviderSchema{ 662 Provider: &configschema.Block{ 663 Attributes: map[string]*configschema.Attribute{ 664 "foo": {Type: cty.String, Optional: true}, 665 }, 666 }, 667 ResourceTypes: map[string]*configschema.Block{ 668 "test_instance": { 669 Attributes: map[string]*configschema.Attribute{ 670 "id": {Type: cty.String, Optional: true, Computed: true}, 671 }, 672 }, 673 }, 674 } 675 676 args := []string{ 677 "-provider", "test-beta", 678 "-state", statePath, 679 "test_instance.foo", 680 "bar", 681 } 682 683 if code := c.Run(args); code != 0 { 684 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 685 } 686 687 // Verify that the test-beta provider was configured 688 if !configured { 689 t.Fatal("Configure should be called") 690 } 691 692 if !p.ImportResourceStateCalled { 693 t.Fatal("ImportResourceState (provider 'test-beta') should be called") 694 } 695 696 if !p.ReadResourceCalled { 697 t.Fatal("ReadResource (provider 'test-beta' should be called") 698 } 699 700 testStateOutput(t, statePath, testImportProviderMismatchStr) 701 } 702 func TestImport_allowMissingResourceConfig(t *testing.T) { 703 defer testChdir(t, testFixturePath("import-missing-resource-config"))() 704 705 statePath := testTempFile(t) 706 707 p := testProvider() 708 ui := new(cli.MockUi) 709 c := &ImportCommand{ 710 Meta: Meta{ 711 testingOverrides: metaOverridesForProvider(p), 712 Ui: ui, 713 }, 714 } 715 716 p.ImportResourceStateFn = nil 717 p.ImportResourceStateResponse = providers.ImportResourceStateResponse{ 718 ImportedResources: []providers.ImportedResource{ 719 { 720 TypeName: "test_instance", 721 State: cty.ObjectVal(map[string]cty.Value{ 722 "id": cty.StringVal("yay"), 723 }), 724 }, 725 }, 726 } 727 p.GetSchemaReturn = &terraform.ProviderSchema{ 728 ResourceTypes: map[string]*configschema.Block{ 729 "test_instance": { 730 Attributes: map[string]*configschema.Attribute{ 731 "id": {Type: cty.String, Optional: true, Computed: true}, 732 }, 733 }, 734 }, 735 } 736 737 args := []string{ 738 "-state", statePath, 739 "-allow-missing-config", 740 "test_instance.foo", 741 "bar", 742 } 743 if code := c.Run(args); code != 0 { 744 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 745 } 746 747 if !p.ImportResourceStateCalled { 748 t.Fatal("ImportResourceState should be called") 749 } 750 751 testStateOutput(t, statePath, testImportStr) 752 } 753 754 func TestImport_emptyConfig(t *testing.T) { 755 defer testChdir(t, testFixturePath("empty"))() 756 757 statePath := testTempFile(t) 758 759 p := testProvider() 760 ui := new(cli.MockUi) 761 c := &ImportCommand{ 762 Meta: Meta{ 763 testingOverrides: metaOverridesForProvider(p), 764 Ui: ui, 765 }, 766 } 767 768 args := []string{ 769 "-state", statePath, 770 "test_instance.foo", 771 "bar", 772 } 773 code := c.Run(args) 774 if code != 1 { 775 t.Fatalf("import succeeded; expected failure") 776 } 777 778 msg := ui.ErrorWriter.String() 779 if want := `No Terraform configuration files`; !strings.Contains(msg, want) { 780 t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) 781 } 782 } 783 784 func TestImport_missingResourceConfig(t *testing.T) { 785 defer testChdir(t, testFixturePath("import-missing-resource-config"))() 786 787 statePath := testTempFile(t) 788 789 p := testProvider() 790 ui := new(cli.MockUi) 791 c := &ImportCommand{ 792 Meta: Meta{ 793 testingOverrides: metaOverridesForProvider(p), 794 Ui: ui, 795 }, 796 } 797 798 args := []string{ 799 "-state", statePath, 800 "test_instance.foo", 801 "bar", 802 } 803 code := c.Run(args) 804 if code != 1 { 805 t.Fatalf("import succeeded; expected failure") 806 } 807 808 msg := ui.ErrorWriter.String() 809 if want := `resource address "test_instance.foo" does not exist`; !strings.Contains(msg, want) { 810 t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) 811 } 812 } 813 814 func TestImport_missingModuleConfig(t *testing.T) { 815 defer testChdir(t, testFixturePath("import-missing-resource-config"))() 816 817 statePath := testTempFile(t) 818 819 p := testProvider() 820 ui := new(cli.MockUi) 821 c := &ImportCommand{ 822 Meta: Meta{ 823 testingOverrides: metaOverridesForProvider(p), 824 Ui: ui, 825 }, 826 } 827 828 args := []string{ 829 "-state", statePath, 830 "module.baz.test_instance.foo", 831 "bar", 832 } 833 code := c.Run(args) 834 if code != 1 { 835 t.Fatalf("import succeeded; expected failure") 836 } 837 838 msg := ui.ErrorWriter.String() 839 if want := `module.baz is not defined in the configuration`; !strings.Contains(msg, want) { 840 t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) 841 } 842 } 843 844 func TestImport_dataResource(t *testing.T) { 845 defer testChdir(t, testFixturePath("import-missing-resource-config"))() 846 847 statePath := testTempFile(t) 848 849 p := testProvider() 850 ui := new(cli.MockUi) 851 c := &ImportCommand{ 852 Meta: Meta{ 853 testingOverrides: metaOverridesForProvider(p), 854 Ui: ui, 855 }, 856 } 857 858 args := []string{ 859 "-state", statePath, 860 "data.test_data_source.foo", 861 "bar", 862 } 863 code := c.Run(args) 864 if code != 1 { 865 t.Fatalf("import succeeded; expected failure") 866 } 867 868 msg := ui.ErrorWriter.String() 869 if want := `A managed resource address is required`; !strings.Contains(msg, want) { 870 t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) 871 } 872 } 873 874 func TestImport_invalidResourceAddr(t *testing.T) { 875 defer testChdir(t, testFixturePath("import-missing-resource-config"))() 876 877 statePath := testTempFile(t) 878 879 p := testProvider() 880 ui := new(cli.MockUi) 881 c := &ImportCommand{ 882 Meta: Meta{ 883 testingOverrides: metaOverridesForProvider(p), 884 Ui: ui, 885 }, 886 } 887 888 args := []string{ 889 "-state", statePath, 890 "bananas", 891 "bar", 892 } 893 code := c.Run(args) 894 if code != 1 { 895 t.Fatalf("import succeeded; expected failure") 896 } 897 898 msg := ui.ErrorWriter.String() 899 if want := `Error: Invalid address`; !strings.Contains(msg, want) { 900 t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) 901 } 902 } 903 904 func TestImport_targetIsModule(t *testing.T) { 905 defer testChdir(t, testFixturePath("import-missing-resource-config"))() 906 907 statePath := testTempFile(t) 908 909 p := testProvider() 910 ui := new(cli.MockUi) 911 c := &ImportCommand{ 912 Meta: Meta{ 913 testingOverrides: metaOverridesForProvider(p), 914 Ui: ui, 915 }, 916 } 917 918 args := []string{ 919 "-state", statePath, 920 "module.foo", 921 "bar", 922 } 923 code := c.Run(args) 924 if code != 1 { 925 t.Fatalf("import succeeded; expected failure") 926 } 927 928 msg := ui.ErrorWriter.String() 929 if want := `Error: Invalid address`; !strings.Contains(msg, want) { 930 t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) 931 } 932 } 933 934 // make sure we search the full plugin path during import 935 func TestImport_pluginDir(t *testing.T) { 936 td := tempDir(t) 937 copy.CopyDir(testFixturePath("import-provider"), td) 938 defer os.RemoveAll(td) 939 defer testChdir(t, td)() 940 941 // make a fake provider in a custom plugin directory 942 if err := os.Mkdir("plugins", 0755); err != nil { 943 t.Fatal(err) 944 } 945 if err := ioutil.WriteFile("plugins/terraform-provider-test_v1.1.1_x4", []byte("invalid binary"), 0755); err != nil { 946 t.Fatal(err) 947 } 948 949 ui := new(cli.MockUi) 950 c := &ImportCommand{ 951 Meta: Meta{ 952 Ui: ui, 953 }, 954 } 955 956 // store our custom plugin path, which would normally happen during init 957 if err := c.storePluginPath([]string{"./plugins"}); err != nil { 958 t.Fatal(err) 959 } 960 961 // Now we need to go through some plugin init. 962 // This discovers our fake plugin and writes the lock file. 963 initUi := new(cli.MockUi) 964 initCmd := &InitCommand{ 965 Meta: Meta{ 966 pluginPath: []string{"./plugins"}, 967 Ui: initUi, 968 }, 969 providerInstaller: &discovery.ProviderInstaller{ 970 PluginProtocolVersion: discovery.PluginInstallProtocolVersion, 971 }, 972 } 973 if code := initCmd.Run(nil); code != 0 { 974 t.Fatal(initUi.ErrorWriter.String()) 975 } 976 977 args := []string{ 978 "test_instance.foo", 979 "bar", 980 } 981 if code := c.Run(args); code == 0 { 982 t.Fatalf("expected error, got: %s", ui.OutputWriter) 983 } 984 985 outMsg := ui.OutputWriter.String() 986 // if we were missing a plugin, the output will have some explanation 987 // about requirements. If discovery starts verifying binary compatibility, 988 // we will need to write a dummy provider above. 989 if strings.Contains(outMsg, "requirements") { 990 t.Fatal("unexpected output:", outMsg) 991 } 992 993 // We wanted a plugin execution error, rather than a requirement error. 994 // Looking for "exec" in the error should suffice for now. 995 errMsg := ui.ErrorWriter.String() 996 if !strings.Contains(errMsg, "exec") { 997 t.Fatal("unexpected error:", errMsg) 998 } 999 } 1000 1001 const testImportStr = ` 1002 test_instance.foo: 1003 ID = yay 1004 provider = provider.test 1005 ` 1006 1007 const testImportCustomProviderStr = ` 1008 test_instance.foo: 1009 ID = yay 1010 provider = provider.test.alias 1011 ` 1012 1013 const testImportProviderMismatchStr = ` 1014 test_instance.foo: 1015 ID = yay 1016 provider = provider.test-beta 1017 `