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