terraform: refactor ProviderEvalTree (#26236)
* remove leftover debug line * terraform: refactor ProviderEvalTree This PR refactors the ProviderEvalTree by folding the entire tree into straight-through code in NodeApplyableProvider Execute (formally EvalTree). The EvalConfigProvider functions were refactored into NodeApplyableProvider functions (since that was the only place they were used). I also removed the unused node_provider_disabled code. * revert removal of graphNodeCloseProvider EvalTree, replace with Execute
This commit is contained in:
parent
150702c100
commit
67d441b131
|
@ -574,7 +574,6 @@ The -target option is not for routine use, and is provided only for exceptional
|
||||||
diags = diags.Append(walker.NonFatalDiagnostics)
|
diags = diags.Append(walker.NonFatalDiagnostics)
|
||||||
diags = diags.Append(walkDiags)
|
diags = diags.Append(walkDiags)
|
||||||
if walkDiags.HasErrors() {
|
if walkDiags.HasErrors() {
|
||||||
fmt.Println("walkerr")
|
|
||||||
return nil, diags
|
return nil, diags
|
||||||
}
|
}
|
||||||
p.Changes = c.changes
|
p.Changes = c.changes
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"github.com/hashicorp/terraform/addrs"
|
"github.com/hashicorp/terraform/addrs"
|
||||||
"github.com/hashicorp/terraform/configs"
|
"github.com/hashicorp/terraform/configs"
|
||||||
"github.com/hashicorp/terraform/providers"
|
"github.com/hashicorp/terraform/providers"
|
||||||
"github.com/hashicorp/terraform/tfdiags"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func buildProviderConfig(ctx EvalContext, addr addrs.AbsProviderConfig, config *configs.Provider) hcl.Body {
|
func buildProviderConfig(ctx EvalContext, addr addrs.AbsProviderConfig, config *configs.Provider) hcl.Body {
|
||||||
|
@ -46,7 +45,7 @@ func buildProviderConfig(ctx EvalContext, addr addrs.AbsProviderConfig, config *
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetProvider returns the providers interface and schema for a given provider.
|
// GetProvider returns the providers.Interface and schema for a given provider.
|
||||||
func GetProvider(ctx EvalContext, addr addrs.AbsProviderConfig) (providers.Interface, *ProviderSchema, error) {
|
func GetProvider(ctx EvalContext, addr addrs.AbsProviderConfig) (providers.Interface, *ProviderSchema, error) {
|
||||||
if addr.Provider.Type == "" {
|
if addr.Provider.Type == "" {
|
||||||
// Should never happen
|
// Should never happen
|
||||||
|
@ -62,77 +61,6 @@ func GetProvider(ctx EvalContext, addr addrs.AbsProviderConfig) (providers.Inter
|
||||||
return provider, schema, nil
|
return provider, schema, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
|
||||||
VerifyConfigIsKnown bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *EvalConfigProvider) Eval(ctx EvalContext) (interface{}, error) {
|
|
||||||
if n.Provider == nil {
|
|
||||||
return nil, fmt.Errorf("EvalConfigProvider Provider is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
var diags tfdiags.Diagnostics
|
|
||||||
provider := *n.Provider
|
|
||||||
config := n.Config
|
|
||||||
|
|
||||||
configBody := buildProviderConfig(ctx, n.Addr, config)
|
|
||||||
|
|
||||||
resp := provider.GetSchema()
|
|
||||||
diags = diags.Append(resp.Diagnostics)
|
|
||||||
if diags.HasErrors() {
|
|
||||||
return nil, diags.NonFatalErr()
|
|
||||||
}
|
|
||||||
|
|
||||||
configSchema := resp.Provider.Block
|
|
||||||
configVal, configBody, evalDiags := ctx.EvaluateBlock(configBody, configSchema, nil, EvalDataForNoInstanceKey)
|
|
||||||
diags = diags.Append(evalDiags)
|
|
||||||
if evalDiags.HasErrors() {
|
|
||||||
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)
|
|
||||||
|
|
||||||
return nil, configDiags.ErrWithWarnings()
|
|
||||||
}
|
|
||||||
|
|
||||||
// EvalInitProvider is an EvalNode implementation that initializes a provider
|
|
||||||
// and returns nothing. The provider can be retrieved again with the
|
|
||||||
// EvalGetProvider node.
|
|
||||||
type EvalInitProvider struct {
|
|
||||||
Addr addrs.AbsProviderConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *EvalInitProvider) Eval(ctx EvalContext) (interface{}, error) {
|
|
||||||
return ctx.InitProvider(n.Addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EvalCloseProvider is an EvalNode implementation that closes provider
|
|
||||||
// connections that aren't needed anymore.
|
|
||||||
type EvalCloseProvider struct {
|
|
||||||
Addr addrs.AbsProviderConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *EvalCloseProvider) Eval(ctx EvalContext) (interface{}, error) {
|
|
||||||
ctx.CloseProvider(n.Addr)
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EvalGetProvider is an EvalNode implementation that retrieves an already
|
// EvalGetProvider is an EvalNode implementation that retrieves an already
|
||||||
// initialized provider instance for the given name.
|
// initialized provider instance for the given name.
|
||||||
//
|
//
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"github.com/hashicorp/terraform/configs"
|
"github.com/hashicorp/terraform/configs"
|
||||||
"github.com/hashicorp/terraform/configs/configschema"
|
"github.com/hashicorp/terraform/configs/configschema"
|
||||||
"github.com/hashicorp/terraform/providers"
|
"github.com/hashicorp/terraform/providers"
|
||||||
"github.com/hashicorp/terraform/tfdiags"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBuildProviderConfig(t *testing.T) {
|
func TestBuildProviderConfig(t *testing.T) {
|
||||||
|
@ -56,189 +55,6 @@ func TestBuildProviderConfig(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEvalConfigProvider_impl(t *testing.T) {
|
|
||||||
var _ EvalNode = new(EvalConfigProvider)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEvalConfigProvider(t *testing.T) {
|
|
||||||
config := &configs.Provider{
|
|
||||||
Name: "foo",
|
|
||||||
Config: configs.SynthBody("", map[string]cty.Value{
|
|
||||||
"test_string": cty.StringVal("hello"),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
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.StringVal("hello"); !got.RawEquals(want) {
|
|
||||||
t.Errorf("wrong configuration value\ngot: %#v\nwant: %#v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEvalInitProvider(t *testing.T) {
|
|
||||||
providerAddr := addrs.AbsProviderConfig{
|
|
||||||
Module: addrs.RootModule,
|
|
||||||
Provider: addrs.NewDefaultProvider("foo"),
|
|
||||||
}
|
|
||||||
n := &EvalInitProvider{
|
|
||||||
Addr: providerAddr,
|
|
||||||
}
|
|
||||||
provider := &MockProvider{}
|
|
||||||
ctx := &MockEvalContext{InitProviderProvider: provider}
|
|
||||||
if _, err := n.Eval(ctx); err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ctx.InitProviderCalled {
|
|
||||||
t.Fatal("should be called")
|
|
||||||
}
|
|
||||||
if ctx.InitProviderAddr.String() != `provider["registry.terraform.io/hashicorp/foo"]` {
|
|
||||||
t.Fatalf("wrong provider address %s", ctx.InitProviderAddr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEvalCloseProvider(t *testing.T) {
|
|
||||||
providerAddr := addrs.AbsProviderConfig{
|
|
||||||
Module: addrs.RootModule,
|
|
||||||
Provider: addrs.NewDefaultProvider("foo"),
|
|
||||||
}
|
|
||||||
n := &EvalCloseProvider{
|
|
||||||
Addr: providerAddr,
|
|
||||||
}
|
|
||||||
provider := &MockProvider{}
|
|
||||||
ctx := &MockEvalContext{CloseProviderProvider: provider}
|
|
||||||
if _, err := n.Eval(ctx); err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ctx.CloseProviderCalled {
|
|
||||||
t.Fatal("should be called")
|
|
||||||
}
|
|
||||||
if ctx.CloseProviderAddr.String() != `provider["registry.terraform.io/hashicorp/foo"]` {
|
|
||||||
t.Fatalf("wrong provider address %s", ctx.CloseProviderAddr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEvalGetProvider_impl(t *testing.T) {
|
func TestEvalGetProvider_impl(t *testing.T) {
|
||||||
var _ EvalNode = new(EvalGetProvider)
|
var _ EvalNode = new(EvalGetProvider)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package terraform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/hashicorp/hcl/v2"
|
"github.com/hashicorp/hcl/v2"
|
||||||
"github.com/hashicorp/terraform/addrs"
|
"github.com/hashicorp/terraform/addrs"
|
||||||
|
@ -64,50 +63,6 @@ RETURN:
|
||||||
return nil, diags.NonFatalErr()
|
return nil, diags.NonFatalErr()
|
||||||
}
|
}
|
||||||
|
|
||||||
// EvalValidateProvider is an EvalNode implementation that validates
|
|
||||||
// a provider configuration.
|
|
||||||
type EvalValidateProvider struct {
|
|
||||||
Addr addrs.AbsProviderConfig
|
|
||||||
Provider *providers.Interface
|
|
||||||
Config *configs.Provider
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *EvalValidateProvider) Eval(ctx EvalContext) (interface{}, error) {
|
|
||||||
var diags tfdiags.Diagnostics
|
|
||||||
provider := *n.Provider
|
|
||||||
|
|
||||||
configBody := buildProviderConfig(ctx, n.Addr, n.Config)
|
|
||||||
|
|
||||||
resp := provider.GetSchema()
|
|
||||||
diags = diags.Append(resp.Diagnostics)
|
|
||||||
if diags.HasErrors() {
|
|
||||||
return nil, diags.NonFatalErr()
|
|
||||||
}
|
|
||||||
|
|
||||||
configSchema := resp.Provider.Block
|
|
||||||
if configSchema == nil {
|
|
||||||
// Should never happen in real code, but often comes up in tests where
|
|
||||||
// mock schemas are being used that tend to be incomplete.
|
|
||||||
log.Printf("[WARN] EvalValidateProvider: no config schema is available for %s, so using empty schema", n.Addr)
|
|
||||||
configSchema = &configschema.Block{}
|
|
||||||
}
|
|
||||||
|
|
||||||
configVal, configBody, evalDiags := ctx.EvaluateBlock(configBody, configSchema, nil, EvalDataForNoInstanceKey)
|
|
||||||
diags = diags.Append(evalDiags)
|
|
||||||
if evalDiags.HasErrors() {
|
|
||||||
return nil, diags.NonFatalErr()
|
|
||||||
}
|
|
||||||
|
|
||||||
req := providers.PrepareProviderConfigRequest{
|
|
||||||
Config: configVal,
|
|
||||||
}
|
|
||||||
|
|
||||||
validateResp := provider.PrepareProviderConfig(req)
|
|
||||||
diags = diags.Append(validateResp.Diagnostics)
|
|
||||||
|
|
||||||
return nil, diags.NonFatalErr()
|
|
||||||
}
|
|
||||||
|
|
||||||
// EvalValidateProvisioner validates the configuration of a provisioner
|
// EvalValidateProvisioner validates the configuration of a provisioner
|
||||||
// belonging to a resource. The provisioner config is expected to contain the
|
// belonging to a resource. The provisioner config is expected to contain the
|
||||||
// merged connection configurations.
|
// merged connection configurations.
|
||||||
|
|
|
@ -1,85 +0,0 @@
|
||||||
package terraform
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/hashicorp/terraform/addrs"
|
|
||||||
"github.com/hashicorp/terraform/configs"
|
|
||||||
"github.com/hashicorp/terraform/providers"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ProviderEvalTree returns the evaluation tree for initializing and
|
|
||||||
// configuring providers.
|
|
||||||
func ProviderEvalTree(n *NodeApplyableProvider, config *configs.Provider) EvalNode {
|
|
||||||
var provider providers.Interface
|
|
||||||
|
|
||||||
addr := n.Addr
|
|
||||||
|
|
||||||
seq := make([]EvalNode, 0, 5)
|
|
||||||
seq = append(seq, &EvalInitProvider{
|
|
||||||
Addr: addr,
|
|
||||||
})
|
|
||||||
|
|
||||||
seq = append(seq, &EvalOpFilter{
|
|
||||||
Ops: []walkOperation{walkValidate},
|
|
||||||
Node: &EvalSequence{
|
|
||||||
Nodes: []EvalNode{
|
|
||||||
&EvalGetProvider{
|
|
||||||
Addr: addr,
|
|
||||||
Output: &provider,
|
|
||||||
},
|
|
||||||
&EvalValidateProvider{
|
|
||||||
Addr: addr,
|
|
||||||
Provider: &provider,
|
|
||||||
Config: config,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
seq = append(seq, &EvalOpFilter{
|
|
||||||
Ops: []walkOperation{walkRefresh, walkPlan, walkApply, walkDestroy, walkImport},
|
|
||||||
Node: &EvalSequence{
|
|
||||||
Nodes: []EvalNode{
|
|
||||||
&EvalGetProvider{
|
|
||||||
Addr: addr,
|
|
||||||
Output: &provider,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// 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},
|
|
||||||
Node: &EvalSequence{
|
|
||||||
Nodes: []EvalNode{
|
|
||||||
&EvalConfigProvider{
|
|
||||||
Addr: addr,
|
|
||||||
Provider: &provider,
|
|
||||||
Config: config,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
seq = append(seq, &EvalOpFilter{
|
|
||||||
Ops: []walkOperation{walkImport},
|
|
||||||
Node: &EvalSequence{
|
|
||||||
Nodes: []EvalNode{
|
|
||||||
&EvalConfigProvider{
|
|
||||||
Addr: addr,
|
|
||||||
Provider: &provider,
|
|
||||||
Config: config,
|
|
||||||
VerifyConfigIsKnown: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return &EvalSequence{Nodes: seq}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CloseProviderEvalTree returns the evaluation tree for closing
|
|
||||||
// provider connections that aren't needed anymore.
|
|
||||||
func CloseProviderEvalTree(addr addrs.AbsProviderConfig) EvalNode {
|
|
||||||
return &EvalCloseProvider{Addr: addr}
|
|
||||||
}
|
|
|
@ -1,11 +1,115 @@
|
||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/hashicorp/hcl/v2"
|
||||||
|
"github.com/hashicorp/terraform/configs/configschema"
|
||||||
|
"github.com/hashicorp/terraform/providers"
|
||||||
|
"github.com/hashicorp/terraform/tfdiags"
|
||||||
|
)
|
||||||
|
|
||||||
// NodeApplyableProvider represents a provider during an apply.
|
// NodeApplyableProvider represents a provider during an apply.
|
||||||
type NodeApplyableProvider struct {
|
type NodeApplyableProvider struct {
|
||||||
*NodeAbstractProvider
|
*NodeAbstractProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeEvalable
|
var (
|
||||||
func (n *NodeApplyableProvider) EvalTree() EvalNode {
|
_ GraphNodeExecutable = (*NodeApplyableProvider)(nil)
|
||||||
return ProviderEvalTree(n, n.ProviderConfig())
|
)
|
||||||
|
|
||||||
|
// GraphNodeExecutable
|
||||||
|
func (n *NodeApplyableProvider) Execute(ctx EvalContext, op walkOperation) error {
|
||||||
|
_, err := ctx.InitProvider(n.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
provider, _, err := GetProvider(ctx, n.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch op {
|
||||||
|
case walkValidate:
|
||||||
|
return n.ValidateProvider(ctx, provider)
|
||||||
|
case walkRefresh, walkPlan, walkApply, walkDestroy:
|
||||||
|
return n.ConfigureProvider(ctx, provider, false)
|
||||||
|
case walkImport:
|
||||||
|
return n.ConfigureProvider(ctx, provider, true)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NodeApplyableProvider) ValidateProvider(ctx EvalContext, provider providers.Interface) error {
|
||||||
|
var diags tfdiags.Diagnostics
|
||||||
|
|
||||||
|
configBody := buildProviderConfig(ctx, n.Addr, n.ProviderConfig())
|
||||||
|
|
||||||
|
resp := provider.GetSchema()
|
||||||
|
diags = diags.Append(resp.Diagnostics)
|
||||||
|
if diags.HasErrors() {
|
||||||
|
return diags.ErrWithWarnings()
|
||||||
|
}
|
||||||
|
|
||||||
|
configSchema := resp.Provider.Block
|
||||||
|
if configSchema == nil {
|
||||||
|
// Should never happen in real code, but often comes up in tests where
|
||||||
|
// mock schemas are being used that tend to be incomplete.
|
||||||
|
log.Printf("[WARN] ValidateProvider: no config schema is available for %s, so using empty schema", n.Addr)
|
||||||
|
configSchema = &configschema.Block{}
|
||||||
|
}
|
||||||
|
|
||||||
|
configVal, configBody, evalDiags := ctx.EvaluateBlock(configBody, configSchema, nil, EvalDataForNoInstanceKey)
|
||||||
|
diags = diags.Append(evalDiags)
|
||||||
|
if evalDiags.HasErrors() {
|
||||||
|
return diags.ErrWithWarnings()
|
||||||
|
}
|
||||||
|
|
||||||
|
req := providers.PrepareProviderConfigRequest{
|
||||||
|
Config: configVal,
|
||||||
|
}
|
||||||
|
|
||||||
|
validateResp := provider.PrepareProviderConfig(req)
|
||||||
|
diags = diags.Append(validateResp.Diagnostics)
|
||||||
|
|
||||||
|
return diags.ErrWithWarnings()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigureProvider configures a provider that is already initialized and retrieved.
|
||||||
|
// If verifyConfigIsKnown is true, ConfigureProvider will return an error if the
|
||||||
|
// provider configVal is not wholly known and is meant only for use during import.
|
||||||
|
func (n *NodeApplyableProvider) ConfigureProvider(ctx EvalContext, provider providers.Interface, verifyConfigIsKnown bool) error {
|
||||||
|
var diags tfdiags.Diagnostics
|
||||||
|
config := n.ProviderConfig()
|
||||||
|
|
||||||
|
configBody := buildProviderConfig(ctx, n.Addr, config)
|
||||||
|
|
||||||
|
resp := provider.GetSchema()
|
||||||
|
diags = diags.Append(resp.Diagnostics)
|
||||||
|
if diags.HasErrors() {
|
||||||
|
return diags.ErrWithWarnings()
|
||||||
|
}
|
||||||
|
|
||||||
|
configSchema := resp.Provider.Block
|
||||||
|
configVal, configBody, evalDiags := ctx.EvaluateBlock(configBody, configSchema, nil, EvalDataForNoInstanceKey)
|
||||||
|
diags = diags.Append(evalDiags)
|
||||||
|
if evalDiags.HasErrors() {
|
||||||
|
return diags.ErrWithWarnings()
|
||||||
|
}
|
||||||
|
|
||||||
|
if 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 diags.ErrWithWarnings()
|
||||||
|
}
|
||||||
|
|
||||||
|
configDiags := ctx.ConfigureProvider(n.Addr, configVal)
|
||||||
|
configDiags = configDiags.InConfigBody(configBody)
|
||||||
|
|
||||||
|
return configDiags.ErrWithWarnings()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
package terraform
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/dag"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NodeDisabledProvider represents a provider that is disabled. A disabled
|
|
||||||
// provider does nothing. It exists to properly set inheritance information
|
|
||||||
// for child providers.
|
|
||||||
type NodeDisabledProvider struct {
|
|
||||||
*NodeAbstractProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ GraphNodeModulePath = (*NodeDisabledProvider)(nil)
|
|
||||||
_ GraphNodeReferencer = (*NodeDisabledProvider)(nil)
|
|
||||||
_ GraphNodeProvider = (*NodeDisabledProvider)(nil)
|
|
||||||
_ GraphNodeAttachProvider = (*NodeDisabledProvider)(nil)
|
|
||||||
_ dag.GraphNodeDotter = (*NodeDisabledProvider)(nil)
|
|
||||||
)
|
|
||||||
|
|
||||||
func (n *NodeDisabledProvider) Name() string {
|
|
||||||
return fmt.Sprintf("%s (disabled)", n.NodeAbstractProvider.Name())
|
|
||||||
}
|
|
|
@ -8,11 +8,8 @@ type NodeEvalableProvider struct {
|
||||||
*NodeAbstractProvider
|
*NodeAbstractProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeEvalable
|
// GraphNodeExecutable
|
||||||
func (n *NodeEvalableProvider) EvalTree() EvalNode {
|
func (n *NodeEvalableProvider) Execute(ctx EvalContext, op walkOperation) error {
|
||||||
addr := n.Addr
|
_, err := ctx.InitProvider(n.Addr)
|
||||||
|
return err
|
||||||
return &EvalInitProvider{
|
|
||||||
Addr: addr,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/addrs"
|
||||||
|
"github.com/hashicorp/terraform/configs"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNodeApplyableProviderExecute(t *testing.T) {
|
||||||
|
config := &configs.Provider{
|
||||||
|
Name: "foo",
|
||||||
|
Config: configs.SynthBody("", map[string]cty.Value{
|
||||||
|
"test_string": cty.StringVal("hello"),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
provider := mockProviderWithConfigSchema(simpleTestSchema())
|
||||||
|
providerAddr := addrs.AbsProviderConfig{
|
||||||
|
Module: addrs.RootModule,
|
||||||
|
Provider: addrs.NewDefaultProvider("foo"),
|
||||||
|
}
|
||||||
|
|
||||||
|
n := &NodeApplyableProvider{&NodeAbstractProvider{
|
||||||
|
Addr: providerAddr,
|
||||||
|
Config: config,
|
||||||
|
}}
|
||||||
|
|
||||||
|
ctx := &MockEvalContext{ProviderProvider: provider}
|
||||||
|
ctx.installSimpleEval()
|
||||||
|
if err := n.Execute(ctx, walkApply); 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.StringVal("hello"); !got.RawEquals(want) {
|
||||||
|
t.Errorf("wrong configuration value\ngot: %#v\nwant: %#v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNodeApplyableProviderExecute_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())
|
||||||
|
providerAddr := addrs.AbsProviderConfig{
|
||||||
|
Module: addrs.RootModule,
|
||||||
|
Provider: addrs.NewDefaultProvider("foo"),
|
||||||
|
}
|
||||||
|
n := &NodeApplyableProvider{&NodeAbstractProvider{
|
||||||
|
Addr: providerAddr,
|
||||||
|
Config: config,
|
||||||
|
}}
|
||||||
|
|
||||||
|
ctx := &MockEvalContext{ProviderProvider: provider}
|
||||||
|
ctx.installSimpleEval()
|
||||||
|
|
||||||
|
err := n.Execute(ctx, walkImport)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error, got success")
|
||||||
|
}
|
||||||
|
|
||||||
|
detail := `Invalid provider configuration: The configuration for provider["registry.terraform.io/hashicorp/foo"] depends on values that cannot be determined until apply.`
|
||||||
|
if got, want := err.Error(), 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 TestNodeApplyableProviderExecute_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())
|
||||||
|
providerAddr := addrs.AbsProviderConfig{
|
||||||
|
Module: addrs.RootModule,
|
||||||
|
Provider: addrs.NewDefaultProvider("foo"),
|
||||||
|
}
|
||||||
|
n := &NodeApplyableProvider{&NodeAbstractProvider{
|
||||||
|
Addr: providerAddr,
|
||||||
|
Config: config,
|
||||||
|
}}
|
||||||
|
ctx := &MockEvalContext{ProviderProvider: provider}
|
||||||
|
ctx.installSimpleEval()
|
||||||
|
|
||||||
|
if err := n.Execute(ctx, walkApply); 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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -460,9 +460,9 @@ func (n *graphNodeCloseProvider) ModulePath() addrs.Module {
|
||||||
return n.Addr.Module
|
return n.Addr.Module
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeEvalable impl.
|
// GraphNodeExecutable impl.
|
||||||
func (n *graphNodeCloseProvider) EvalTree() EvalNode {
|
func (n *graphNodeCloseProvider) Execute(ctx EvalContext, op walkOperation) error {
|
||||||
return CloseProviderEvalTree(n.Addr)
|
return ctx.CloseProvider(n.Addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeDependable impl.
|
// GraphNodeDependable impl.
|
||||||
|
|
Loading…
Reference in New Issue