mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-28 18:01:01 -06:00
e7b2d98ca3
Core is only using the PrepareProviderConfig call for the validation part of the method, but we should be re-validating the final config immediately before Configure. This change elects to not start using the PreparedConfig here, since there is no useful reason for it at this point, and it would introduce a functional difference between terraform releases that can be avoided.
138 lines
4.2 KiB
Go
138 lines
4.2 KiB
Go
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"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
// NodeApplyableProvider represents a provider during an apply.
|
|
type NodeApplyableProvider struct {
|
|
*NodeAbstractProvider
|
|
}
|
|
|
|
var (
|
|
_ GraphNodeExecutable = (*NodeApplyableProvider)(nil)
|
|
)
|
|
|
|
// GraphNodeExecutable
|
|
func (n *NodeApplyableProvider) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) {
|
|
_, err := ctx.InitProvider(n.Addr)
|
|
diags = diags.Append(err)
|
|
if diags.HasErrors() {
|
|
return diags
|
|
}
|
|
provider, _, err := GetProvider(ctx, n.Addr)
|
|
diags = diags.Append(err)
|
|
if diags.HasErrors() {
|
|
return diags
|
|
}
|
|
|
|
switch op {
|
|
case walkValidate:
|
|
return diags.Append(n.ValidateProvider(ctx, provider))
|
|
case walkPlan, walkApply, walkDestroy:
|
|
return diags.Append(n.ConfigureProvider(ctx, provider, false))
|
|
case walkImport:
|
|
return diags.Append(n.ConfigureProvider(ctx, provider, true))
|
|
}
|
|
return diags
|
|
}
|
|
|
|
func (n *NodeApplyableProvider) ValidateProvider(ctx EvalContext, provider providers.Interface) (diags tfdiags.Diagnostics) {
|
|
|
|
configBody := buildProviderConfig(ctx, n.Addr, n.ProviderConfig())
|
|
|
|
resp := provider.GetSchema()
|
|
diags = diags.Append(resp.Diagnostics)
|
|
if diags.HasErrors() {
|
|
return diags
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
req := providers.PrepareProviderConfigRequest{
|
|
Config: configVal,
|
|
}
|
|
|
|
validateResp := provider.PrepareProviderConfig(req)
|
|
diags = diags.Append(validateResp.Diagnostics)
|
|
|
|
return diags
|
|
}
|
|
|
|
// 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) (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
|
|
}
|
|
|
|
configSchema := resp.Provider.Block
|
|
configVal, configBody, evalDiags := ctx.EvaluateBlock(configBody, configSchema, nil, EvalDataForNoInstanceKey)
|
|
diags = diags.Append(evalDiags)
|
|
if evalDiags.HasErrors() {
|
|
return diags
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// Allow the provider to validate and insert any defaults into the full
|
|
// configuration.
|
|
req := providers.PrepareProviderConfigRequest{
|
|
Config: configVal,
|
|
}
|
|
|
|
// PrepareProviderConfig is only used for validation. We are intentionally
|
|
// ignoring the PreparedConfig field to maintain existing behavior.
|
|
prepareResp := provider.PrepareProviderConfig(req)
|
|
diags = diags.Append(prepareResp.Diagnostics)
|
|
if diags.HasErrors() {
|
|
return diags
|
|
}
|
|
|
|
// If the provider returns something different, log a warning to help
|
|
// indicate to provider developers that the value is not used.
|
|
preparedCfg := prepareResp.PreparedConfig
|
|
if preparedCfg != cty.NilVal && !preparedCfg.IsNull() && !preparedCfg.RawEquals(configVal) {
|
|
log.Printf("[WARN] PrepareProviderConfig from %q changed the config value, but that value is unused", n.Addr)
|
|
}
|
|
|
|
configDiags := ctx.ConfigureProvider(n.Addr, configVal)
|
|
diags = diags.Append(configDiags.InConfigBody(configBody))
|
|
|
|
return diags
|
|
}
|