Mildwonkey/ps import (#24412)

* import: remove Config from ImportOpts

`Config` in ImportOpts was any provider configuration provided by the
user on the command line. This option has already been removed in favor
of only taking the provider from the configuration loaded in the current
context.

* terrafrom: add Config to ImportStateTransformer and refactor Transform
to get the resource provider FQN from the Config
This commit is contained in:
Kristin Laemmert 2020-03-20 08:15:29 -04:00 committed by GitHub
parent 9d0c8c5970
commit c8d64846ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 152 additions and 699 deletions

View File

@ -26,8 +26,6 @@ func Provider() terraform.ResourceProvider {
ResourcesMap: map[string]*schema.Resource{
"test_resource": testResource(),
"test_resource_gh12183": testResourceGH12183(),
"test_resource_import_other": testResourceImportOther(),
"test_resource_import_removed": testResourceImportRemoved(),
"test_resource_with_custom_diff": testResourceCustomDiff(),
"test_resource_timeout": testResourceTimeout(),
"test_resource_diff_suppress": testResourceDiffSuppress(),

View File

@ -1,74 +0,0 @@
package test
import (
"fmt"
"github.com/hashicorp/terraform/helper/schema"
)
func testResourceImportOther() *schema.Resource {
return &schema.Resource{
Create: testResourceImportOtherCreate,
Read: testResourceImportOtherRead,
Delete: testResourceImportOtherDelete,
Update: testResourceImportOtherUpdate,
Importer: &schema.ResourceImporter{
State: testResourceImportOtherImportState,
},
Schema: map[string]*schema.Schema{
"default_string": {
Type: schema.TypeString,
Optional: true,
Default: "default string",
},
"default_bool": {
Type: schema.TypeString,
Optional: true,
Default: true,
},
"computed": {
Type: schema.TypeString,
Computed: true,
},
},
}
}
func testResourceImportOtherImportState(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
var results []*schema.ResourceData
results = append(results, d)
{
other := testResourceDefaults()
od := other.Data(nil)
od.SetType("test_resource_defaults")
od.SetId("import_other_other")
results = append(results, od)
}
return results, nil
}
func testResourceImportOtherCreate(d *schema.ResourceData, meta interface{}) error {
d.SetId("import_other_main")
return testResourceImportOtherRead(d, meta)
}
func testResourceImportOtherUpdate(d *schema.ResourceData, meta interface{}) error {
return testResourceImportOtherRead(d, meta)
}
func testResourceImportOtherRead(d *schema.ResourceData, meta interface{}) error {
err := d.Set("computed", "hello!")
if err != nil {
return fmt.Errorf("failed to set 'computed' attribute: %s", err)
}
return nil
}
func testResourceImportOtherDelete(d *schema.ResourceData, meta interface{}) error {
return nil
}

View File

@ -1,54 +0,0 @@
package test
import (
"fmt"
"strings"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestResourceImportOther(t *testing.T) {
resource.UnitTest(t, resource.TestCase{
Providers: testAccProviders,
CheckDestroy: testAccCheckResourceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: strings.TrimSpace(`
resource "test_resource_import_other" "foo" {
}
`),
},
{
ImportState: true,
ResourceName: "test_resource_import_other.foo",
ImportStateCheck: func(iss []*terraform.InstanceState) error {
if got, want := len(iss), 2; got != want {
return fmt.Errorf("wrong number of resources %d; want %d", got, want)
}
byID := make(map[string]*terraform.InstanceState, len(iss))
for _, is := range iss {
byID[is.ID] = is
}
if is, ok := byID["import_other_main"]; !ok {
return fmt.Errorf("no instance with id import_other_main in state")
} else if got, want := is.Ephemeral.Type, "test_resource_import_other"; got != want {
return fmt.Errorf("import_other_main has wrong type %q; want %q", got, want)
} else if got, want := is.Attributes["computed"], "hello!"; got != want {
return fmt.Errorf("import_other_main has wrong value %q for its computed attribute; want %q", got, want)
}
if is, ok := byID["import_other_other"]; !ok {
return fmt.Errorf("no instance with id import_other_other in state")
} else if got, want := is.Ephemeral.Type, "test_resource_defaults"; got != want {
return fmt.Errorf("import_other_other has wrong type %q; want %q", got, want)
}
return nil
},
},
},
})
}

View File

@ -1,60 +0,0 @@
package test
import (
"github.com/hashicorp/terraform/helper/schema"
)
func testResourceImportRemoved() *schema.Resource {
return &schema.Resource{
Create: testResourceImportRemovedCreate,
Read: testResourceImportRemovedRead,
Delete: testResourceImportRemovedDelete,
Update: testResourceImportRemovedUpdate,
Importer: &schema.ResourceImporter{
State: testResourceImportRemovedImportState,
},
Schema: map[string]*schema.Schema{
"removed": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
Removed: "do not use",
},
},
}
}
func testResourceImportRemovedImportState(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
var results []*schema.ResourceData
results = append(results, d)
{
other := testResourceDefaults()
od := other.Data(nil)
od.SetType("test_resource_import_removed")
od.SetId("foo")
results = append(results, od)
}
return results, nil
}
func testResourceImportRemovedCreate(d *schema.ResourceData, meta interface{}) error {
d.SetId("foo")
return testResourceImportRemovedRead(d, meta)
}
func testResourceImportRemovedUpdate(d *schema.ResourceData, meta interface{}) error {
return testResourceImportRemovedRead(d, meta)
}
func testResourceImportRemovedRead(d *schema.ResourceData, meta interface{}) error {
return nil
}
func testResourceImportRemovedDelete(d *schema.ResourceData, meta interface{}) error {
return nil
}

View File

@ -1,41 +0,0 @@
package test
import (
"strings"
"testing"
"github.com/hashicorp/terraform/helper/resource"
)
func TestResourceImportRemoved(t *testing.T) {
resource.UnitTest(t, resource.TestCase{
Providers: testAccProviders,
CheckDestroy: testAccCheckResourceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: strings.TrimSpace(`
resource "test_resource_import_removed" "foo" {
}
`),
},
{
ImportState: true,
ResourceName: "test_resource_import_removed.foo",
// This is attempting to guard against regressions of:
// https://github.com/hashicorp/terraform/issues/20985
//
// Removed attributes are generally not populated during Create,
// Update, Read, or Import by provider code but due to our
// legacy diff format being lossy they end up getting populated
// with zero values during shimming in all cases except Import,
// which doesn't go through a diff.
//
// This is testing that the shimming inconsistency won't cause
// ImportStateVerify failures for these, since we now ignore
// attributes marked as Removed when comparing.
ImportStateVerify: true,
},
},
})
}

View File

@ -605,7 +605,7 @@ func TestImport_providerConfigWithVarFile(t *testing.T) {
testStateOutput(t, statePath, testImportStr)
}
func TestImport_allowMissingResourceConfig(t *testing.T) {
func TestImport_disallowMissingResourceConfig(t *testing.T) {
defer testChdir(t, testFixturePath("import-missing-resource-config"))()
statePath := testTempFile(t)
@ -646,15 +646,16 @@ func TestImport_allowMissingResourceConfig(t *testing.T) {
"test_instance.foo",
"bar",
}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
if code := c.Run(args); code != 1 {
t.Fatalf("import succeeded; expected failure")
}
if !p.ImportResourceStateCalled {
t.Fatal("ImportResourceState should be called")
}
msg := ui.ErrorWriter.String()
testStateOutput(t, statePath, testImportStr)
if want := `Error: Resource test_instance.foo not found in the configuration.`; !strings.Contains(msg, want) {
t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg)
}
}
func TestImport_emptyConfig(t *testing.T) {

View File

@ -77,9 +77,6 @@ func testStepImportState(
// Do the import
importedState, stepDiags := ctx.Import(&terraform.ImportOpts{
// Set the module so that any provider config is loaded
Config: cfg,
Targets: []*terraform.ImportTarget{
&terraform.ImportTarget{
Addr: importAddr,

View File

@ -2,7 +2,6 @@ package terraform
import (
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/tfdiags"
)
@ -11,11 +10,6 @@ import (
type ImportOpts struct {
// Targets are the targets to import
Targets []*ImportTarget
// Config is optional, and specifies a config tree that will be loaded
// into the graph and evaluated. This is the source for provider
// configurations.
Config *configs.Config
}
// ImportTarget is a single resource to import.
@ -50,17 +44,10 @@ func (c *Context) Import(opts *ImportOpts) (*states.State, tfdiags.Diagnostics)
// Copy our own state
c.state = c.state.DeepCopy()
// If no module is given, default to the module configured with
// the Context.
config := opts.Config
if config == nil {
config = c.config
}
// Initialize our graph builder
builder := &ImportGraphBuilder{
ImportTargets: opts.Targets,
Config: config,
Config: c.config,
Components: c.components,
Schemas: c.schemas,
}

View File

@ -51,6 +51,42 @@ func TestContextImport_basic(t *testing.T) {
}
}
// Importing a resource which does not exist in the configuration results in an error
func TestContextImport_basic_errpr(t *testing.T) {
p := testProvider("aws")
m := testModule(t, "import-provider")
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
map[addrs.Provider]providers.Factory{
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
},
),
})
p.ImportStateReturn = []*InstanceState{
&InstanceState{
ID: "foo",
Ephemeral: EphemeralState{Type: "aws_instance"},
},
}
_, diags := ctx.Import(&ImportOpts{
Targets: []*ImportTarget{
&ImportTarget{
Addr: addrs.RootModuleInstance.ResourceInstance(
addrs.ManagedResourceMode, "aws_instance", "test", addrs.NoKey,
),
ID: "bar",
},
},
})
if !diags.HasErrors() {
t.Fatal("should error")
}
}
func TestContextImport_countIndex(t *testing.T) {
p := testProvider("aws")
m := testModule(t, "import-provider")
@ -226,7 +262,6 @@ func TestContextImport_moduleProvider(t *testing.T) {
})
state, diags := ctx.Import(&ImportOpts{
Config: m,
Targets: []*ImportTarget{
&ImportTarget{
Addr: addrs.RootModuleInstance.ResourceInstance(
@ -254,7 +289,7 @@ func TestContextImport_moduleProvider(t *testing.T) {
// Importing into a module requires a provider config in that module.
func TestContextImport_providerModule(t *testing.T) {
p := testProvider("aws")
m := testModule(t, "import-provider-module")
m := testModule(t, "import-module")
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
@ -283,7 +318,6 @@ func TestContextImport_providerModule(t *testing.T) {
}
_, diags := ctx.Import(&ImportOpts{
Config: m,
Targets: []*ImportTarget{
&ImportTarget{
Addr: addrs.RootModuleInstance.Child("child", addrs.NoKey).ResourceInstance(
@ -391,8 +425,7 @@ func TestContextImport_providerNonVarConfig(t *testing.T) {
Addr: addrs.RootModuleInstance.ResourceInstance(
addrs.ManagedResourceMode, "aws_instance", "foo", addrs.NoKey,
),
ID: "bar",
ProviderAddr: addrs.RootModuleInstance.ProviderConfigDefault(addrs.NewLegacyProvider("aws")),
ID: "bar",
},
},
})
@ -435,8 +468,7 @@ func TestContextImport_refresh(t *testing.T) {
Addr: addrs.RootModuleInstance.ResourceInstance(
addrs.ManagedResourceMode, "aws_instance", "foo", addrs.NoKey,
),
ID: "bar",
ProviderAddr: addrs.RootModuleInstance.ProviderConfigDefault(addrs.NewLegacyProvider("aws")),
ID: "bar",
},
},
})
@ -482,8 +514,7 @@ func TestContextImport_refreshNil(t *testing.T) {
Addr: addrs.RootModuleInstance.ResourceInstance(
addrs.ManagedResourceMode, "aws_instance", "foo", addrs.NoKey,
),
ID: "bar",
ProviderAddr: addrs.RootModuleInstance.ProviderConfigDefault(addrs.NewLegacyProvider("aws")),
ID: "bar",
},
},
})
@ -500,7 +531,7 @@ func TestContextImport_refreshNil(t *testing.T) {
func TestContextImport_module(t *testing.T) {
p := testProvider("aws")
m := testModule(t, "import-provider")
m := testModule(t, "import-module")
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
@ -520,11 +551,10 @@ func TestContextImport_module(t *testing.T) {
state, diags := ctx.Import(&ImportOpts{
Targets: []*ImportTarget{
&ImportTarget{
Addr: addrs.RootModuleInstance.Child("foo", addrs.NoKey).ResourceInstance(
Addr: addrs.RootModuleInstance.Child("child", addrs.NoKey).ResourceInstance(
addrs.ManagedResourceMode, "aws_instance", "foo", addrs.NoKey,
),
ID: "bar",
ProviderAddr: addrs.RootModuleInstance.ProviderConfigDefault(addrs.NewLegacyProvider("aws")),
ID: "bar",
},
},
})
@ -541,7 +571,7 @@ func TestContextImport_module(t *testing.T) {
func TestContextImport_moduleDepth2(t *testing.T) {
p := testProvider("aws")
m := testModule(t, "import-provider")
m := testModule(t, "import-module")
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
@ -561,11 +591,10 @@ func TestContextImport_moduleDepth2(t *testing.T) {
state, diags := ctx.Import(&ImportOpts{
Targets: []*ImportTarget{
&ImportTarget{
Addr: addrs.RootModuleInstance.Child("a", addrs.NoKey).Child("b", addrs.NoKey).ResourceInstance(
Addr: addrs.RootModuleInstance.Child("child", addrs.NoKey).Child("nested", addrs.NoKey).ResourceInstance(
addrs.ManagedResourceMode, "aws_instance", "foo", addrs.NoKey,
),
ID: "bar",
ProviderAddr: addrs.RootModuleInstance.ProviderConfigDefault(addrs.NewLegacyProvider("aws")),
ID: "baz",
},
},
})
@ -582,7 +611,7 @@ func TestContextImport_moduleDepth2(t *testing.T) {
func TestContextImport_moduleDiff(t *testing.T) {
p := testProvider("aws")
m := testModule(t, "import-provider")
m := testModule(t, "import-module")
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
@ -590,26 +619,6 @@ func TestContextImport_moduleDiff(t *testing.T) {
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
},
),
State: states.BuildState(func(s *states.SyncState) {
s.SetResourceInstanceCurrent(
addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "aws_instance",
Name: "bar",
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance.Child("bar", addrs.NoKey)),
&states.ResourceInstanceObjectSrc{
AttrsFlat: map[string]string{
"id": "bar",
},
Status: states.ObjectReady,
},
addrs.AbsProviderConfig{
Provider: addrs.NewLegacyProvider("aws"),
Module: addrs.RootModule,
},
)
}),
})
p.ImportStateReturn = []*InstanceState{
@ -622,11 +631,10 @@ func TestContextImport_moduleDiff(t *testing.T) {
state, diags := ctx.Import(&ImportOpts{
Targets: []*ImportTarget{
&ImportTarget{
Addr: addrs.RootModuleInstance.Child("foo", addrs.NoKey).ResourceInstance(
Addr: addrs.RootModuleInstance.Child("child", addrs.NoKey).ResourceInstance(
addrs.ManagedResourceMode, "aws_instance", "foo", addrs.NoKey,
),
ID: "bar",
ProviderAddr: addrs.RootModuleInstance.ProviderConfigDefault(addrs.NewLegacyProvider("aws")),
ID: "baz",
},
},
})
@ -635,68 +643,7 @@ func TestContextImport_moduleDiff(t *testing.T) {
}
actual := strings.TrimSpace(state.String())
expected := strings.TrimSpace(testImportModuleDiffStr)
if actual != expected {
t.Fatalf("\nexpected: %q\ngot: %q\n", expected, actual)
}
}
func TestContextImport_moduleExisting(t *testing.T) {
p := testProvider("aws")
m := testModule(t, "import-provider")
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
map[addrs.Provider]providers.Factory{
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
},
),
State: states.BuildState(func(s *states.SyncState) {
s.SetResourceInstanceCurrent(
addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "aws_instance",
Name: "bar",
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance.Child("foo", addrs.NoKey)),
&states.ResourceInstanceObjectSrc{
AttrsFlat: map[string]string{
"id": "bar",
},
Status: states.ObjectReady,
},
addrs.AbsProviderConfig{
Provider: addrs.NewLegacyProvider("aws"),
Module: addrs.RootModule,
},
)
}),
})
p.ImportStateReturn = []*InstanceState{
&InstanceState{
ID: "foo",
Ephemeral: EphemeralState{Type: "aws_instance"},
},
}
state, diags := ctx.Import(&ImportOpts{
Targets: []*ImportTarget{
&ImportTarget{
Addr: addrs.RootModuleInstance.Child("foo", addrs.NoKey).ResourceInstance(
addrs.ManagedResourceMode, "aws_instance", "foo", addrs.NoKey,
),
ID: "bar",
ProviderAddr: addrs.RootModuleInstance.ProviderConfigDefault(addrs.NewLegacyProvider("aws")),
},
},
})
if diags.HasErrors() {
t.Fatalf("unexpected errors: %s", diags.Err())
}
actual := strings.TrimSpace(state.String())
expected := strings.TrimSpace(testImportModuleExistingStr)
expected := strings.TrimSpace(testImportModuleStr)
if actual != expected {
t.Fatalf("\nexpected: %q\ngot: %q\n", expected, actual)
}
@ -752,8 +699,7 @@ func TestContextImport_multiState(t *testing.T) {
Addr: addrs.RootModuleInstance.ResourceInstance(
addrs.ManagedResourceMode, "aws_instance", "foo", addrs.NoKey,
),
ID: "bar",
ProviderAddr: addrs.RootModuleInstance.ProviderConfigDefault(addrs.NewLegacyProvider("aws")),
ID: "bar",
},
},
})
@ -822,8 +768,7 @@ func TestContextImport_multiStateSame(t *testing.T) {
Addr: addrs.RootModuleInstance.ResourceInstance(
addrs.ManagedResourceMode, "aws_instance", "foo", addrs.NoKey,
),
ID: "bar",
ProviderAddr: addrs.RootModuleInstance.ProviderConfigDefault(addrs.NewLegacyProvider("aws")),
ID: "bar",
},
},
})
@ -838,83 +783,6 @@ func TestContextImport_multiStateSame(t *testing.T) {
}
}
// import missing a provider alias should fail
func TestContextImport_customProviderMissing(t *testing.T) {
p := testProvider("aws")
m := testModule(t, "import-provider")
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
map[addrs.Provider]providers.Factory{
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
},
),
})
p.ImportStateReturn = []*InstanceState{
&InstanceState{
ID: "foo",
Ephemeral: EphemeralState{Type: "aws_instance"},
},
}
_, diags := ctx.Import(&ImportOpts{
Targets: []*ImportTarget{
&ImportTarget{
Addr: addrs.RootModuleInstance.ResourceInstance(
addrs.ManagedResourceMode, "aws_instance", "foo", addrs.NoKey,
),
ID: "bar",
ProviderAddr: addrs.RootModuleInstance.ProviderConfigAliased(addrs.NewLegacyProvider("aws"), "alias"),
},
},
})
if !diags.HasErrors() {
t.Fatal("expected error")
}
}
func TestContextImport_customProvider(t *testing.T) {
p := testProvider("aws")
m := testModule(t, "import-provider-alias")
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
map[addrs.Provider]providers.Factory{
addrs.NewLegacyProvider("aws"): testProviderFuncFixed(p),
},
),
})
p.ImportStateReturn = []*InstanceState{
&InstanceState{
ID: "foo",
Ephemeral: EphemeralState{Type: "aws_instance"},
},
}
state, diags := ctx.Import(&ImportOpts{
Targets: []*ImportTarget{
&ImportTarget{
Addr: addrs.RootModuleInstance.ResourceInstance(
addrs.ManagedResourceMode, "aws_instance", "foo", addrs.NoKey,
),
ID: "bar",
ProviderAddr: addrs.RootModuleInstance.ProviderConfigAliased(addrs.NewLegacyProvider("aws"), "alias"),
},
},
})
if diags.HasErrors() {
t.Fatalf("unexpected errors: %s", diags.Err())
}
actual := strings.TrimSpace(state.String())
expected := strings.TrimSpace(testImportCustomProviderStr)
if actual != expected {
t.Fatalf("bad: \n%s", actual)
}
}
const testImportStr = `
aws_instance.foo:
ID = foo
@ -929,7 +797,7 @@ aws_instance.foo.0:
const testImportModuleStr = `
<no state>
module.foo:
module.child:
aws_instance.foo:
ID = foo
provider = provider["registry.terraform.io/-/aws"]
@ -937,19 +805,7 @@ module.foo:
const testImportModuleDepth2Str = `
<no state>
module.a.b:
aws_instance.foo:
ID = foo
provider = provider["registry.terraform.io/-/aws"]
`
const testImportModuleDiffStr = `
<no state>
module.bar:
aws_instance.bar:
ID = bar
provider = provider["registry.terraform.io/-/aws"]
module.foo:
module.child.nested:
aws_instance.foo:
ID = foo
provider = provider["registry.terraform.io/-/aws"]
@ -993,9 +849,3 @@ aws_instance.foo:
provider = provider["registry.terraform.io/-/aws"]
foo = bar
`
const testImportCustomProviderStr = `
aws_instance.foo:
ID = foo
provider = provider["registry.terraform.io/-/aws"].alias
`

View File

@ -59,7 +59,7 @@ func (b *ImportGraphBuilder) Steps() []GraphTransformer {
&AttachResourceConfigTransformer{Config: b.Config},
// Add the import steps
&ImportStateTransformer{Targets: b.ImportTargets},
&ImportStateTransformer{Targets: b.ImportTargets, Config: b.Config},
// Add root variables
&RootVariableTransformer{Config: b.Config},

View File

@ -0,0 +1,10 @@
# Empty
provider "aws" {}
resource "aws_instance" "foo" {
id = "bar"
}
module "nested" {
source = "./submodule"
}

View File

@ -0,0 +1,3 @@
resource "aws_instance" "foo" {
id = "baz"
}

View File

@ -1,2 +0,0 @@
# Empty
provider "aws" {}

View File

@ -3,3 +3,7 @@ variable "foo" {}
provider "aws" {
foo = "${var.foo}"
}
resource "aws_instance" "foo" {
id = "bar"
}

View File

@ -1,3 +1,7 @@
provider "aws" {
foo = "bar"
}
resource "aws_instance" "foo" {
id = "bar"
}

View File

@ -4,6 +4,7 @@ import (
"fmt"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/providers"
"github.com/hashicorp/terraform/tfdiags"
)
@ -12,20 +13,44 @@ import (
// graph to represent the imports we want to do for resources.
type ImportStateTransformer struct {
Targets []*ImportTarget
Config *configs.Config
}
func (t *ImportStateTransformer) Transform(g *Graph) error {
for _, target := range t.Targets {
// The ProviderAddr may not be supplied for non-aliased providers.
// This will be populated if the targets come from the cli, but tests
// may not specify implied provider addresses.
providerAddr := target.ProviderAddr
if providerAddr.Provider.Type == "" {
defaultFQN := addrs.NewLegacyProvider(target.Addr.Resource.Resource.ImpliedProvider())
providerAddr = addrs.AbsProviderConfig{
Provider: defaultFQN,
Module: target.Addr.Module.Module(),
}
// This is only likely to happen in misconfigured tests
if t.Config == nil {
return fmt.Errorf("Cannot import into an empty configuration.")
}
// Get the module config
modCfg := t.Config.Descendent(target.Addr.Module.Module())
if modCfg == nil {
return fmt.Errorf("Module %s not found.", target.Addr.Module.Module())
}
// Get the resource config
rsCfg := modCfg.Module.ResourceByAddr(target.Addr.Resource.Resource)
if rsCfg == nil {
return fmt.Errorf("Resource %s not found in the configuration.", target.Addr)
}
// Get the provider FQN for the resource from the resource configuration
providerFqn := rsCfg.Provider
// This is only likely to happen in misconfigured tests.
if rsCfg == nil {
return fmt.Errorf("provider for resource %s not found in the configuration.", target.Addr)
}
// Get the provider local config for the resource
localpCfg := rsCfg.ProviderConfigAddr()
providerAddr := addrs.AbsProviderConfig{
Provider: providerFqn,
Alias: localpCfg.Alias,
Module: target.Addr.Module.Module(),
}
node := &graphNodeImportState{

View File

@ -46,28 +46,43 @@ func TestProviderTransformer(t *testing.T) {
}
}
func TestProviderTransformer_moduleChild(t *testing.T) {
func TestProviderTransformer_ImportModuleChild(t *testing.T) {
mod := testModule(t, "import-module")
g := Graph{Path: addrs.RootModuleInstance}
{
{
tf := &ConfigTransformer{Config: mod}
if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err)
}
}
{
transform := &AttachResourceConfigTransformer{Config: mod}
if err := transform.Transform(&g); err != nil {
t.Fatalf("err: %s", err)
}
}
tf := &ImportStateTransformer{
Config: mod,
Targets: []*ImportTarget{
&ImportTarget{
Addr: addrs.RootModuleInstance.
Child("moo", addrs.NoKey).
Child("child", addrs.NoKey).
ResourceInstance(
addrs.ManagedResourceMode,
"foo_instance",
"qux",
"aws_instance",
"foo",
addrs.NoKey,
),
ProviderAddr: addrs.RootModuleInstance.
Child("moo", addrs.NoKey).
ProviderConfigDefault(addrs.NewLegacyProvider("foo")),
ID: "bar",
},
},
}
if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err)
}
@ -91,7 +106,7 @@ func TestProviderTransformer_moduleChild(t *testing.T) {
}
actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testTransformProviderModuleChildStr)
expected := strings.TrimSpace(testTransformImportModuleChildStr)
if actual != expected {
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
}
@ -300,199 +315,6 @@ func TestMissingProviderTransformer_grandchildMissing(t *testing.T) {
}
}
func TestMissingProviderTransformer_moduleChild(t *testing.T) {
g := Graph{Path: addrs.RootModuleInstance}
// We use the import state transformer since at the time of writing
// this test it is the first and only transformer that will introduce
// multiple module-path nodes at a single go.
{
tf := &ImportStateTransformer{
Targets: []*ImportTarget{
&ImportTarget{
Addr: addrs.RootModuleInstance.
Child("moo", addrs.NoKey).
ResourceInstance(
addrs.ManagedResourceMode,
"foo_instance",
"qux",
addrs.NoKey,
),
ProviderAddr: addrs.RootModuleInstance.
Child("moo", addrs.NoKey).
ProviderConfigDefault(addrs.NewLegacyProvider("foo")),
ID: "bar",
},
},
}
if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err)
}
}
{
tf := &MissingProviderTransformer{Providers: []string{"foo", "bar"}}
if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err)
}
}
actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testTransformMissingProviderModuleChildStr)
if actual != expected {
t.Fatalf("bad:\n\n%s", actual)
}
}
func TestMissingProviderTransformer_moduleGrandchild(t *testing.T) {
g := Graph{Path: addrs.RootModuleInstance}
// We use the import state transformer since at the time of writing
// this test it is the first and only transformer that will introduce
// multiple module-path nodes at a single go.
{
tf := &ImportStateTransformer{
Targets: []*ImportTarget{
&ImportTarget{
Addr: addrs.RootModuleInstance.
Child("a", addrs.NoKey).
Child("b", addrs.NoKey).
ResourceInstance(
addrs.ManagedResourceMode,
"foo_instance",
"qux",
addrs.NoKey,
),
ProviderAddr: addrs.RootModuleInstance.
Child("moo", addrs.NoKey).
ProviderConfigDefault(addrs.NewLegacyProvider("foo")),
ID: "bar",
},
},
}
if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err)
}
}
{
tf := &MissingProviderTransformer{Providers: []string{"foo", "bar"}}
if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err)
}
}
actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testTransformMissingProviderModuleGrandchildStr)
if actual != expected {
t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
}
}
func TestParentProviderTransformer(t *testing.T) {
g := Graph{Path: addrs.RootModuleInstance}
// Introduce a cihld module
{
tf := &ImportStateTransformer{
Targets: []*ImportTarget{
&ImportTarget{
Addr: addrs.RootModuleInstance.
Child("moo", addrs.NoKey).
ResourceInstance(
addrs.ManagedResourceMode,
"foo_instance",
"qux",
addrs.NoKey,
),
ProviderAddr: addrs.RootModuleInstance.
Child("moo", addrs.NoKey).
ProviderConfigDefault(addrs.NewLegacyProvider("foo")),
ID: "bar",
},
},
}
if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err)
}
}
// Add the missing modules
{
tf := &MissingProviderTransformer{Providers: []string{"foo", "bar"}}
if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err)
}
}
// Connect parents
{
tf := &ParentProviderTransformer{}
if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err)
}
}
actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testTransformParentProviderStr)
if actual != expected {
t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
}
}
func TestParentProviderTransformer_moduleGrandchild(t *testing.T) {
g := Graph{Path: addrs.RootModuleInstance}
// We use the import state transformer since at the time of writing
// this test it is the first and only transformer that will introduce
// multiple module-path nodes at a single go.
{
tf := &ImportStateTransformer{
Targets: []*ImportTarget{
&ImportTarget{
Addr: addrs.RootModuleInstance.
Child("a", addrs.NoKey).
Child("b", addrs.NoKey).
ResourceInstance(
addrs.ManagedResourceMode,
"foo_instance",
"qux",
addrs.NoKey,
),
ProviderAddr: addrs.RootModuleInstance.
Child("moo", addrs.NoKey).
ProviderConfigDefault(addrs.NewLegacyProvider("foo")),
ID: "bar",
},
},
}
if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err)
}
}
{
tf := &MissingProviderTransformer{Providers: []string{"foo", "bar"}}
if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err)
}
}
// Connect parents
{
tf := &ParentProviderTransformer{}
if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err)
}
}
actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testTransformParentProviderModuleGrandchildStr)
if actual != expected {
t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
}
}
func TestPruneProviderTransformer(t *testing.T) {
mod := testModule(t, "transform-provider-prune")
@ -714,32 +536,6 @@ module.sub.provider["registry.terraform.io/-/foo"]
provider["registry.terraform.io/-/bar"]
`
const testTransformMissingProviderModuleChildStr = `
module.moo.foo_instance.qux (import id "bar")
provider["registry.terraform.io/-/foo"]
`
const testTransformMissingProviderModuleGrandchildStr = `
module.a.module.b.foo_instance.qux (import id "bar")
provider["registry.terraform.io/-/foo"]
`
const testTransformParentProviderStr = `
module.moo.foo_instance.qux (import id "bar")
provider["registry.terraform.io/-/foo"]
`
const testTransformParentProviderModuleGrandchildStr = `
module.a.module.b.foo_instance.qux (import id "bar")
provider["registry.terraform.io/-/foo"]
`
const testTransformProviderModuleChildStr = `
module.moo.foo_instance.qux (import id "bar")
provider["registry.terraform.io/-/foo"]
provider["registry.terraform.io/-/foo"]
`
const testTransformPruneProviderBasicStr = `
foo_instance.web
provider["registry.terraform.io/-/foo"]
@ -785,3 +581,12 @@ module.child.module.grandchild.aws_instance.baz
provider["registry.terraform.io/-/aws"].foo
provider["registry.terraform.io/-/aws"].foo
`
const testTransformImportModuleChildStr = `
module.child.aws_instance.foo
provider["registry.terraform.io/-/aws"]
module.child.aws_instance.foo (import id "bar")
provider["registry.terraform.io/-/aws"]
module.child.module.nested.aws_instance.foo
provider["registry.terraform.io/-/aws"]
provider["registry.terraform.io/-/aws"]`