Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 73 additions & 58 deletions terraform/context_import_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,73 +290,85 @@ func TestContextImport_providerModule(t *testing.T) {

// Test that import will interpolate provider configuration and use
// that configuration for import.
func TestContextImport_providerVarConfig(t *testing.T) {
p := testProvider("aws")
m := testModule(t, "import-provider-vars")
ctx := testContext2(t, &ContextOpts{
Config: m,
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
},
Variables: InputValues{
"foo": &InputValue{
Value: cty.StringVal("bar"),
SourceType: ValueFromCaller,
},
},
})

configured := false
p.ConfigureFn = func(c *ResourceConfig) error {
configured = true

if v, ok := c.Get("foo"); !ok || v.(string) != "bar" {
return fmt.Errorf("bad value %#v; want %#v", v, "bar")
}

return nil
}

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",
},
},
})
if diags.HasErrors() {
t.Fatalf("unexpected errors: %s", diags.Err())
}

if !configured {
t.Fatal("didn't configure provider")
}
func TestContextImport_providerConfig(t *testing.T) {
testCases := map[string]struct {
module string
value string
}{
"variables": {
module: "import-provider-vars",
value: "bar",
},
"locals": {
module: "import-provider-locals",
value: "baz-bar",
},
}
for name, test := range testCases {
t.Run(name, func(t *testing.T) {
p := testProvider("aws")
m := testModule(t, test.module)
ctx := testContext2(t, &ContextOpts{
Config: m,
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
},
Variables: InputValues{
"foo": &InputValue{
Value: cty.StringVal("bar"),
SourceType: ValueFromCaller,
},
},
})

actual := strings.TrimSpace(state.String())
expected := strings.TrimSpace(testImportStr)
if actual != expected {
t.Fatalf("bad: \n%s", actual)
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",
},
},
})
if diags.HasErrors() {
t.Fatalf("unexpected errors: %s", diags.Err())
}

if !p.ConfigureCalled {
t.Fatal("didn't configure provider")
}

if foo := p.ConfigureRequest.Config.GetAttr("foo").AsString(); foo != test.value {
t.Fatalf("bad value %#v; want %#v", foo, test.value)
}

actual := strings.TrimSpace(state.String())
expected := strings.TrimSpace(testImportStr)
if actual != expected {
t.Fatalf("bad: \n%s", actual)
}
})
}
}

// Test that provider configs can't reference resources.
func TestContextImport_providerNonVarConfig(t *testing.T) {
func TestContextImport_providerConfigResources(t *testing.T) {
p := testProvider("aws")
m := testModule(t, "import-provider-non-vars")
pTest := testProvider("test")
m := testModule(t, "import-provider-resources")
ctx := testContext2(t, &ContextOpts{
Config: m,
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
addrs.NewDefaultProvider("test"): testProviderFuncFixed(pTest),
},
})

Expand All @@ -380,6 +392,9 @@ func TestContextImport_providerNonVarConfig(t *testing.T) {
if !diags.HasErrors() {
t.Fatal("should error")
}
if got, want := diags.Err().Error(), `The configuration for provider["registry.terraform.io/hashicorp/aws"] depends on values that cannot be determined until apply.`; !strings.Contains(got, want) {
t.Errorf("wrong error\n got: %s\nwant: %s", got, want)
}
}

func TestContextImport_refresh(t *testing.T) {
Expand Down
17 changes: 14 additions & 3 deletions terraform/eval_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,10 @@ func buildProviderConfig(ctx EvalContext, addr addrs.AbsProviderConfig, config *
// EvalConfigProvider is an EvalNode implementation that configures
// a provider that is already initialized and retrieved.
type EvalConfigProvider struct {
Addr addrs.AbsProviderConfig
Provider *providers.Interface
Config *configs.Provider
Addr addrs.AbsProviderConfig
Provider *providers.Interface
Config *configs.Provider
VerifyConfigIsKnown bool
}

func (n *EvalConfigProvider) Eval(ctx EvalContext) (interface{}, error) {
Expand All @@ -78,6 +79,16 @@ func (n *EvalConfigProvider) Eval(ctx EvalContext) (interface{}, error) {
return nil, diags.NonFatalErr()
}

if n.VerifyConfigIsKnown && !configVal.IsWhollyKnown() {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid provider configuration",
Detail: fmt.Sprintf("The configuration for %s depends on values that cannot be determined until apply.", n.Addr),
Subject: &config.DeclRange,
})
return nil, diags.NonFatalErr()
}

configDiags := ctx.ConfigureProvider(n.Addr, configVal)
configDiags = configDiags.InConfigBody(configBody)

Expand Down
94 changes: 94 additions & 0 deletions terraform/eval_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/providers"
"github.com/hashicorp/terraform/tfdiags"
)

func TestBuildProviderConfig(t *testing.T) {
Expand Down Expand Up @@ -97,6 +98,99 @@ func TestEvalConfigProvider(t *testing.T) {
}
}

func TestEvalConfigProvider_unknownImport(t *testing.T) {
config := &configs.Provider{
Name: "foo",
Config: configs.SynthBody("", map[string]cty.Value{
"test_string": cty.UnknownVal(cty.String),
}),
}
provider := mockProviderWithConfigSchema(simpleTestSchema())
rp := providers.Interface(provider)
providerAddr := addrs.AbsProviderConfig{
Module: addrs.RootModule,
Provider: addrs.NewDefaultProvider("foo"),
}
n := &EvalConfigProvider{
Addr: providerAddr,
Config: config,
Provider: &rp,
VerifyConfigIsKnown: true,
}

ctx := &MockEvalContext{ProviderProvider: provider}
ctx.installSimpleEval()

_, err := n.Eval(ctx)

var diags tfdiags.Diagnostics
switch e := err.(type) {
case tfdiags.NonFatalError:
diags = e.Diagnostics
default:
t.Fatalf("expected err to be NonFatalError, was %T", err)
}

if len(diags) != 1 {
t.Fatalf("expected 1 diagnostic, got %d", len(diags))
}

if got, want := diags[0].Severity(), tfdiags.Error; got != want {
t.Errorf("wrong diagnostic severity %#v; want %#v", got, want)
}
if got, want := diags[0].Description().Summary, "Invalid provider configuration"; got != want {
t.Errorf("wrong diagnostic summary %#v; want %#v", got, want)
}
detail := `The configuration for provider["registry.terraform.io/hashicorp/foo"] depends on values that cannot be determined until apply.`
if got, want := diags[0].Description().Detail, detail; got != want {
t.Errorf("wrong diagnostic detail\n got: %q\nwant: %q", got, want)
}

if ctx.ConfigureProviderCalled {
t.Fatal("should not be called")
}
}

func TestEvalConfigProvider_unknownApply(t *testing.T) {
config := &configs.Provider{
Name: "foo",
Config: configs.SynthBody("", map[string]cty.Value{
"test_string": cty.UnknownVal(cty.String),
}),
}
provider := mockProviderWithConfigSchema(simpleTestSchema())
rp := providers.Interface(provider)
providerAddr := addrs.AbsProviderConfig{
Module: addrs.RootModule,
Provider: addrs.NewDefaultProvider("foo"),
}
n := &EvalConfigProvider{
Addr: providerAddr,
Config: config,
Provider: &rp,
VerifyConfigIsKnown: false,
}

ctx := &MockEvalContext{ProviderProvider: provider}
ctx.installSimpleEval()

if _, err := n.Eval(ctx); err != nil {
t.Fatalf("err: %s", err)
}

if !ctx.ConfigureProviderCalled {
t.Fatal("should be called")
}

gotObj := ctx.ConfigureProviderConfig
if !gotObj.Type().HasAttribute("test_string") {
t.Fatal("configuration object does not have \"test_string\" attribute")
}
if got, want := gotObj.GetAttr("test_string"), cty.UnknownVal(cty.String); !got.RawEquals(want) {
t.Errorf("wrong configuration value\ngot: %#v\nwant: %#v", got, want)
}
}

func TestEvalInitProvider_impl(t *testing.T) {
var _ EvalNode = new(EvalInitProvider)
}
Expand Down
29 changes: 14 additions & 15 deletions terraform/evaltree_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,6 @@ func ProviderEvalTree(n *NodeApplyableProvider, config *configs.Provider) EvalNo
Addr: addr,
})

// Input stuff
seq = append(seq, &EvalOpFilter{
Ops: []walkOperation{walkImport},
Node: &EvalSequence{
Nodes: []EvalNode{
&EvalGetProvider{
Addr: addr,
Output: &provider,
},
},
},
})

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this step was redundant with the "everything except validate" one a few lines below.

seq = append(seq, &EvalOpFilter{
Ops: []walkOperation{walkValidate},
Node: &EvalSequence{
Expand All @@ -48,7 +35,6 @@ func ProviderEvalTree(n *NodeApplyableProvider, config *configs.Provider) EvalNo
},
})

// Apply stuff
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment (and the one above) did not seem to mean anything. I'd happily add more comments here if I knew what to write…

seq = append(seq, &EvalOpFilter{
Ops: []walkOperation{walkRefresh, walkPlan, walkApply, walkDestroy, walkImport},
Node: &EvalSequence{
Expand All @@ -64,7 +50,7 @@ func ProviderEvalTree(n *NodeApplyableProvider, config *configs.Provider) EvalNo
// We configure on everything but validate, since validate may
// not have access to all the variables.
seq = append(seq, &EvalOpFilter{
Ops: []walkOperation{walkRefresh, walkPlan, walkApply, walkDestroy, walkImport},
Ops: []walkOperation{walkRefresh, walkPlan, walkApply, walkDestroy},
Node: &EvalSequence{
Nodes: []EvalNode{
&EvalConfigProvider{
Expand All @@ -75,6 +61,19 @@ func ProviderEvalTree(n *NodeApplyableProvider, config *configs.Provider) EvalNo
},
},
})
seq = append(seq, &EvalOpFilter{
Ops: []walkOperation{walkImport},
Node: &EvalSequence{
Nodes: []EvalNode{
&EvalConfigProvider{
Addr: addr,
Provider: &provider,
Config: config,
VerifyConfigIsKnown: true,
},
},
},
})

return &EvalSequence{Nodes: seq}
}
Expand Down
3 changes: 0 additions & 3 deletions terraform/graph_builder_import.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,6 @@ func (b *ImportGraphBuilder) Steps() []GraphTransformer {
// configuration
&attachDataResourceDependenciesTransformer{},

// This validates that the providers only depend on variables
&ImportProviderValidateTransformer{},

// Close opened plugin connections
&CloseProviderTransformer{},

Expand Down
13 changes: 13 additions & 0 deletions terraform/testdata/import-provider-locals/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
variable "foo" {}

locals {
baz = "baz-${var.foo}"
}

provider "aws" {
foo = "${local.baz}"
}

resource "aws_instance" "foo" {
id = "bar"
}
7 changes: 0 additions & 7 deletions terraform/testdata/import-provider-non-vars/main.tf

This file was deleted.

7 changes: 0 additions & 7 deletions terraform/testdata/import-provider-resource/main.tf

This file was deleted.

Loading