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  `