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.
This commit is contained in:
Daniel Spangenberg 2016-11-23 10:44:52 +01:00
parent b335418d0d
commit 804a5bd3c5
7 changed files with 119 additions and 16 deletions

View File

@ -32,6 +32,7 @@ func (c *ImportCommand) Run(args []string) int {
cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path") cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path")
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path") cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
cmdFlags.StringVar(&configPath, "config", pwd, "path") cmdFlags.StringVar(&configPath, "config", pwd, "path")
cmdFlags.StringVar(&c.Meta.provider, "provider", "", "provider")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil { if err := cmdFlags.Parse(args); err != nil {
return 1 return 1
@ -62,8 +63,9 @@ func (c *ImportCommand) Run(args []string) int {
newState, err := ctx.Import(&terraform.ImportOpts{ newState, err := ctx.Import(&terraform.ImportOpts{
Targets: []*terraform.ImportTarget{ Targets: []*terraform.ImportTarget{
&terraform.ImportTarget{ &terraform.ImportTarget{
Addr: args[0], Addr: args[0],
ID: args[1], 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-out=path Path to write updated state file. By default, the
"-state" path will be used. "-state" path will be used.
-provider=provider Provider used for import. Defaults to: ""
` `
return strings.TrimSpace(helpText) return strings.TrimSpace(helpText)
} }

View File

@ -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 = ` const testImportStr = `
test_instance.foo: test_instance.foo:
ID = yay ID = yay
provider = test provider = test
` `
const testImportCustomProviderStr = `
test_instance.foo:
ID = yay
provider = test.alias
`

View File

@ -72,11 +72,14 @@ type Meta struct {
// allowed when walking the graph // allowed when walking the graph
// //
// shadow is used to enable/disable the shadow graph // shadow is used to enable/disable the shadow graph
//
// provider is to specify specific resource providers
statePath string statePath string
stateOutPath string stateOutPath string
backupPath string backupPath string
parallelism int parallelism int
shadow bool shadow bool
provider string
} }
// initStatePaths is used to initialize the default values for // initStatePaths is used to initialize the default values for

View File

@ -23,6 +23,9 @@ type ImportTarget struct {
// ID is the ID of the resource to import. This is resource-specific. // ID is the ID of the resource to import. This is resource-specific.
ID string ID string
// Provider string
Provider string
} }
// Import takes already-created external resources and brings them // Import takes already-created external resources and brings them

View File

@ -1,3 +1,4 @@
// TODO
package terraform package terraform
import ( import (
@ -32,7 +33,6 @@ func TestContextImport_basic(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
actual := strings.TrimSpace(state.String()) actual := strings.TrimSpace(state.String())
expected := strings.TrimSpace(testImportStr) expected := strings.TrimSpace(testImportStr)
if actual != expected { 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 = ` const testImportStr = `
aws_instance.foo: aws_instance.foo:
ID = foo ID = foo
@ -700,3 +735,9 @@ aws_instance.foo:
provider = aws provider = aws
foo = bar foo = bar
` `
const testImportCustomProviderStr = `
aws_instance.foo:
ID = foo
provider = aws.alias
`

View File

@ -21,8 +21,9 @@ func (t *ImportStateTransformer) Transform(g *Graph) error {
} }
nodes = append(nodes, &graphNodeImportState{ nodes = append(nodes, &graphNodeImportState{
Addr: addr, Addr: addr,
ID: target.ID, ID: target.ID,
Provider: target.Provider,
}) })
} }
@ -35,8 +36,9 @@ func (t *ImportStateTransformer) Transform(g *Graph) error {
} }
type graphNodeImportState struct { type graphNodeImportState struct {
Addr *ResourceAddress // Addr is the resource address to import to Addr *ResourceAddress // Addr is the resource address to import to
ID string // ID is the ID to import as ID string // ID is the ID to import as
Provider string // Provider string
states []*InstanceState states []*InstanceState
} }
@ -46,7 +48,7 @@ func (n *graphNodeImportState) Name() string {
} }
func (n *graphNodeImportState) ProvidedBy() []string { func (n *graphNodeImportState) ProvidedBy() []string {
return []string{resourceProvider(n.Addr.Type, "")} return []string{resourceProvider(n.Addr.Type, n.Provider)}
} }
// GraphNodeSubPath // GraphNodeSubPath
@ -147,9 +149,10 @@ func (n *graphNodeImportState) DynamicExpand(ctx EvalContext) (*Graph, error) {
// is safe. // is safe.
for i, state := range n.states { for i, state := range n.states {
g.Add(&graphNodeImportStateSub{ g.Add(&graphNodeImportStateSub{
Target: addrs[i], Target: addrs[i],
Path_: n.Path(), Path_: n.Path(),
State: state, 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 is part of the subgraph. This node is responsible for refreshing
// and adding a resource to the state once it is imported. // and adding a resource to the state once it is imported.
type graphNodeImportStateSub struct { type graphNodeImportStateSub struct {
Target *ResourceAddress Target *ResourceAddress
State *InstanceState State *InstanceState
Path_ []string Path_ []string
Provider string
} }
func (n *graphNodeImportStateSub) Name() string { func (n *graphNodeImportStateSub) Name() string {
@ -212,7 +216,7 @@ func (n *graphNodeImportStateSub) EvalTree() EvalNode {
return &EvalSequence{ return &EvalSequence{
Nodes: []EvalNode{ Nodes: []EvalNode{
&EvalGetProvider{ &EvalGetProvider{
Name: resourceProvider(info.Type, ""), Name: resourceProvider(info.Type, n.Provider),
Output: &provider, Output: &provider,
}, },
&EvalRefresh{ &EvalRefresh{
@ -229,7 +233,7 @@ func (n *graphNodeImportStateSub) EvalTree() EvalNode {
&EvalWriteState{ &EvalWriteState{
Name: key.String(), Name: key.String(),
ResourceType: info.Type, ResourceType: info.Type,
Provider: resourceProvider(info.Type, ""), Provider: resourceProvider(info.Type, n.Provider),
State: &state, State: &state,
}, },
}, },

View File

@ -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 the state path. Ignored when [remote state](/docs/state/remote/index.html) is
used. used.
* `-provider=provider` - Provider used for import. Defaults to the default
provider of the resource to import.
## Provider Configuration ## Provider Configuration
Terraform will attempt to load configuration files that configure the Terraform will attempt to load configuration files that configure the