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