From 804a5bd3c5b6418fca83f3a92717c267e4c49abc Mon Sep 17 00:00:00 2001 From: Daniel Spangenberg Date: Wed, 23 Nov 2016 10:44:52 +0100 Subject: [PATCH] Implements import with specified provider This allows the import with a specified provider. So far it was not possible to get resources imported from a different provider than the default. --- command/import.go | 8 +++- command/import_test.go | 45 +++++++++++++++++++++ command/meta.go | 3 ++ terraform/context_import.go | 3 ++ terraform/context_import_test.go | 43 +++++++++++++++++++- terraform/transform_import_state.go | 30 ++++++++------ website/source/docs/commands/import.html.md | 3 ++ 7 files changed, 119 insertions(+), 16 deletions(-) diff --git a/command/import.go b/command/import.go index 37cf708f4..46d0324ef 100644 --- a/command/import.go +++ b/command/import.go @@ -32,6 +32,7 @@ func (c *ImportCommand) Run(args []string) int { cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path") cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path") cmdFlags.StringVar(&configPath, "config", pwd, "path") + cmdFlags.StringVar(&c.Meta.provider, "provider", "", "provider") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { return 1 @@ -62,8 +63,9 @@ func (c *ImportCommand) Run(args []string) int { newState, err := ctx.Import(&terraform.ImportOpts{ Targets: []*terraform.ImportTarget{ &terraform.ImportTarget{ - Addr: args[0], - ID: args[1], + Addr: args[0], + ID: args[1], + Provider: c.Meta.provider, }, }, }) @@ -138,6 +140,8 @@ Options: -state-out=path Path to write updated state file. By default, the "-state" path will be used. + -provider=provider Provider used for import. Defaults to: "" + ` return strings.TrimSpace(helpText) } diff --git a/command/import_test.go b/command/import_test.go index 9e5129b54..a6a4779c4 100644 --- a/command/import_test.go +++ b/command/import_test.go @@ -844,8 +844,53 @@ func TestRefresh_displaysOutputs(t *testing.T) { } */ +func TestImport_customProvider(t *testing.T) { + statePath := testTempFile(t) + + p := testProvider() + ui := new(cli.MockUi) + c := &ImportCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(p), + Ui: ui, + }, + } + + p.ImportStateFn = nil + p.ImportStateReturn = []*terraform.InstanceState{ + &terraform.InstanceState{ + ID: "yay", + Ephemeral: terraform.EphemeralState{ + Type: "test_instance", + }, + }, + } + + args := []string{ + "-provider", "test.alias", + "-state", statePath, + "test_instance.foo", + "bar", + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + if !p.ImportStateCalled { + t.Fatal("ImportState should be called") + } + + testStateOutput(t, statePath, testImportCustomProviderStr) +} + const testImportStr = ` test_instance.foo: ID = yay provider = test ` + +const testImportCustomProviderStr = ` +test_instance.foo: + ID = yay + provider = test.alias +` diff --git a/command/meta.go b/command/meta.go index 2d68eff98..07ac45627 100644 --- a/command/meta.go +++ b/command/meta.go @@ -72,11 +72,14 @@ type Meta struct { // allowed when walking the graph // // shadow is used to enable/disable the shadow graph + // + // provider is to specify specific resource providers statePath string stateOutPath string backupPath string parallelism int shadow bool + provider string } // initStatePaths is used to initialize the default values for diff --git a/terraform/context_import.go b/terraform/context_import.go index 382fc8e00..afc9a43c0 100644 --- a/terraform/context_import.go +++ b/terraform/context_import.go @@ -23,6 +23,9 @@ type ImportTarget struct { // ID is the ID of the resource to import. This is resource-specific. ID string + + // Provider string + Provider string } // Import takes already-created external resources and brings them diff --git a/terraform/context_import_test.go b/terraform/context_import_test.go index 221866cdc..5ad2273b4 100644 --- a/terraform/context_import_test.go +++ b/terraform/context_import_test.go @@ -1,3 +1,4 @@ +// TODO package terraform import ( @@ -32,7 +33,6 @@ func TestContextImport_basic(t *testing.T) { if err != nil { t.Fatalf("err: %s", err) } - actual := strings.TrimSpace(state.String()) expected := strings.TrimSpace(testImportStr) if actual != expected { @@ -621,6 +621,41 @@ func TestContextImport_multiStateSame(t *testing.T) { } } +func TestContextImport_customProvider(t *testing.T) { + p := testProvider("aws") + ctx := testContext2(t, &ContextOpts{ + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + }) + + p.ImportStateReturn = []*InstanceState{ + &InstanceState{ + ID: "foo", + Ephemeral: EphemeralState{Type: "aws_instance"}, + }, + } + + state, err := ctx.Import(&ImportOpts{ + Targets: []*ImportTarget{ + &ImportTarget{ + Addr: "aws_instance.foo", + ID: "bar", + Provider: "aws.alias", + }, + }, + }) + if err != nil { + t.Fatalf("err: %s", 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 @@ -700,3 +735,9 @@ aws_instance.foo: provider = aws foo = bar ` + +const testImportCustomProviderStr = ` +aws_instance.foo: + ID = foo + provider = aws.alias +` diff --git a/terraform/transform_import_state.go b/terraform/transform_import_state.go index 389c0464e..081df2f84 100644 --- a/terraform/transform_import_state.go +++ b/terraform/transform_import_state.go @@ -21,8 +21,9 @@ func (t *ImportStateTransformer) Transform(g *Graph) error { } nodes = append(nodes, &graphNodeImportState{ - Addr: addr, - ID: target.ID, + Addr: addr, + ID: target.ID, + Provider: target.Provider, }) } @@ -35,8 +36,9 @@ func (t *ImportStateTransformer) Transform(g *Graph) error { } type graphNodeImportState struct { - Addr *ResourceAddress // Addr is the resource address to import to - ID string // ID is the ID to import as + Addr *ResourceAddress // Addr is the resource address to import to + ID string // ID is the ID to import as + Provider string // Provider string states []*InstanceState } @@ -46,7 +48,7 @@ func (n *graphNodeImportState) Name() string { } func (n *graphNodeImportState) ProvidedBy() []string { - return []string{resourceProvider(n.Addr.Type, "")} + return []string{resourceProvider(n.Addr.Type, n.Provider)} } // GraphNodeSubPath @@ -147,9 +149,10 @@ func (n *graphNodeImportState) DynamicExpand(ctx EvalContext) (*Graph, error) { // is safe. for i, state := range n.states { g.Add(&graphNodeImportStateSub{ - Target: addrs[i], - Path_: n.Path(), - State: state, + Target: addrs[i], + Path_: n.Path(), + State: state, + Provider: n.Provider, }) } @@ -167,9 +170,10 @@ func (n *graphNodeImportState) DynamicExpand(ctx EvalContext) (*Graph, error) { // and is part of the subgraph. This node is responsible for refreshing // and adding a resource to the state once it is imported. type graphNodeImportStateSub struct { - Target *ResourceAddress - State *InstanceState - Path_ []string + Target *ResourceAddress + State *InstanceState + Path_ []string + Provider string } func (n *graphNodeImportStateSub) Name() string { @@ -212,7 +216,7 @@ func (n *graphNodeImportStateSub) EvalTree() EvalNode { return &EvalSequence{ Nodes: []EvalNode{ &EvalGetProvider{ - Name: resourceProvider(info.Type, ""), + Name: resourceProvider(info.Type, n.Provider), Output: &provider, }, &EvalRefresh{ @@ -229,7 +233,7 @@ func (n *graphNodeImportStateSub) EvalTree() EvalNode { &EvalWriteState{ Name: key.String(), ResourceType: info.Type, - Provider: resourceProvider(info.Type, ""), + Provider: resourceProvider(info.Type, n.Provider), State: &state, }, }, diff --git a/website/source/docs/commands/import.html.md b/website/source/docs/commands/import.html.md index 4f717b965..4ae785b64 100644 --- a/website/source/docs/commands/import.html.md +++ b/website/source/docs/commands/import.html.md @@ -49,6 +49,9 @@ The command-line flags are all optional. The list of available flags are: the state path. Ignored when [remote state](/docs/state/remote/index.html) is used. +* `-provider=provider` - Provider used for import. Defaults to the default + provider of the resource to import. + ## Provider Configuration Terraform will attempt to load configuration files that configure the