github.com/opentofu/opentofu@v1.7.1/internal/command/import_test.go (about)

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