From 29264df7c005e3c351ce02031bb372acd652c5e2 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Tue, 14 Nov 2017 15:22:15 -0500 Subject: [PATCH] normalize missing provider names The provider name coming from ProvidedBy may be resolved if it only exists in the state. Make sure to strip the module and provider prefixes for the provider name when adding missing providers. --- terraform/context_apply_test.go | 114 ++++++++++++++++++++++++++++++++ terraform/transform_provider.go | 9 ++- 2 files changed, 122 insertions(+), 1 deletion(-) diff --git a/terraform/context_apply_test.go b/terraform/context_apply_test.go index 4c9ec48f6..656ac9438 100644 --- a/terraform/context_apply_test.go +++ b/terraform/context_apply_test.go @@ -9099,3 +9099,117 @@ func TestContext2Apply_destroyWithProviders(t *testing.T) { t.Fatalf("wrong final state\ngot:\n%s\nwant:\n%s", got, want) } } + +func TestContext2Apply_providersFromState(t *testing.T) { + m := module.NewEmptyTree() + p := testProvider("aws") + p.DiffFn = testDiffFn + + for _, tc := range []struct { + name string + state *State + output string + err bool + }{ + { + name: "add implicit provider", + state: &State{ + Modules: []*ModuleState{ + &ModuleState{ + Path: []string{"root"}, + Resources: map[string]*ResourceState{ + "aws_instance.a": &ResourceState{ + Type: "aws_instance", + Primary: &InstanceState{ + ID: "bar", + }, + Provider: "provider.aws", + }, + }, + }, + }, + }, + err: false, + output: "", + }, + + // an aliased provider must be in the config to remove a resource + { + name: "add aliased provider", + state: &State{ + Modules: []*ModuleState{ + &ModuleState{ + Path: []string{"root"}, + Resources: map[string]*ResourceState{ + "aws_instance.a": &ResourceState{ + Type: "aws_instance", + Primary: &InstanceState{ + ID: "bar", + }, + Provider: "provider.aws.bar", + }, + }, + }, + }, + }, + err: true, + }, + + // a provider in a module implies some sort of config, so this isn't + // allowed even without an alias + { + name: "add unaliased module provider", + state: &State{ + Modules: []*ModuleState{ + &ModuleState{ + Path: []string{"root", "child"}, + Resources: map[string]*ResourceState{ + "aws_instance.a": &ResourceState{ + Type: "aws_instance", + Primary: &InstanceState{ + ID: "bar", + }, + Provider: "module.child.provider.aws", + }, + }, + }, + }, + }, + err: true, + }, + } { + + t.Run(tc.name, func(t *testing.T) { + ctx := testContext2(t, &ContextOpts{ + Module: m, + ProviderResolver: ResourceProviderResolverFixed( + map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + ), + State: tc.state, + }) + + _, err := ctx.Plan() + if tc.err { + if err == nil { + t.Fatal("expected error") + } else { + return + } + } + if !tc.err && err != nil { + t.Fatal(err) + } + + state, err := ctx.Apply() + if err != nil { + t.Fatalf("err: %s", err) + } + + checkStateString(t, state, "") + + }) + } + +} diff --git a/terraform/transform_provider.go b/terraform/transform_provider.go index 31aeb3ca5..e95864210 100644 --- a/terraform/transform_provider.go +++ b/terraform/transform_provider.go @@ -193,6 +193,11 @@ func (t *MissingProviderTransformer) Transform(g *Graph) error { } p := pv.ProvidedBy() + // this may be the resolved provider from the state, so we need to get + // the base provider name. + parts := strings.SplitAfter(p, "provider.") + p = parts[len(parts)-1] + key := ResolveProviderName(p, nil) provider := m[key] @@ -203,10 +208,12 @@ func (t *MissingProviderTransformer) Transform(g *Graph) error { // we don't implicitly create aliased providers if strings.Contains(p, ".") { - log.Println("[DEBUG] not adding missing provider alias", p) + log.Println("[DEBUG] not adding missing provider alias:", p) continue } + log.Println("[DEBUG] adding missing provider:", p) + // create the misisng top-level provider provider = t.Concrete(&NodeAbstractProvider{ NameValue: p,