terraform/terraform/eval_provider.go

158 lines
4.7 KiB
Go

package terraform
import (
"fmt"
"log"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/providers"
"github.com/hashicorp/terraform/tfdiags"
)
func buildProviderConfig(ctx EvalContext, addr addrs.AbsProviderConfig, config *configs.Provider) hcl.Body {
var configBody hcl.Body
if config != nil {
configBody = config.Config
}
var inputBody hcl.Body
inputConfig := ctx.ProviderInput(addr)
if len(inputConfig) > 0 {
inputBody = configs.SynthBody("<input-prompt>", inputConfig)
}
switch {
case configBody != nil && inputBody != nil:
log.Printf("[TRACE] buildProviderConfig for %s: merging explicit config and input", addr)
// Note that the inputBody is the _base_ here, because configs.MergeBodies
// expects the base have all of the required fields, while these are
// forced to be optional for the override. The input process should
// guarantee that we have a value for each of the required arguments and
// that in practice the sets of attributes in each body will be
// disjoint.
return configs.MergeBodies(inputBody, configBody)
case configBody != nil:
log.Printf("[TRACE] buildProviderConfig for %s: using explicit config only", addr)
return configBody
case inputBody != nil:
log.Printf("[TRACE] buildProviderConfig for %s: using input only", addr)
return inputBody
default:
log.Printf("[TRACE] buildProviderConfig for %s: no configuration at all", addr)
return hcl.EmptyBody()
}
}
// 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
// initialized provider instance for the given name.
//
// Unlike most eval nodes, this takes an _absolute_ provider configuration,
// because providers can be passed into and inherited between modules.
// Resource nodes must therefore know the absolute path of the provider they
// will use, which is usually accomplished by implementing
// interface GraphNodeProviderConsumer.
type EvalGetProvider struct {
Addr addrs.AbsProviderConfig
Output *providers.Interface
// If non-nil, Schema will be updated after eval to refer to the
// schema of the provider.
Schema **ProviderSchema
}
func (n *EvalGetProvider) Eval(ctx EvalContext) (interface{}, error) {
if n.Addr.Provider.Type == "" {
// Should never happen
panic("EvalGetProvider used with uninitialized provider configuration address")
}
result := ctx.Provider(n.Addr)
if result == nil {
return nil, fmt.Errorf("provider %s not initialized", n.Addr)
}
if n.Output != nil {
*n.Output = result
}
if n.Schema != nil {
*n.Schema = ctx.ProviderSchema(n.Addr)
}
return nil, nil
}