github.com/opentofu/opentofu@v1.7.1/internal/tofu/node_provider.go (about)

     1  // Copyright (c) The OpenTofu Authors
     2  // SPDX-License-Identifier: MPL-2.0
     3  // Copyright (c) 2023 HashiCorp, Inc.
     4  // SPDX-License-Identifier: MPL-2.0
     5  
     6  package tofu
     7  
     8  import (
     9  	"fmt"
    10  	"log"
    11  
    12  	"github.com/hashicorp/hcl/v2"
    13  	"github.com/opentofu/opentofu/internal/configs/configschema"
    14  	"github.com/opentofu/opentofu/internal/providers"
    15  	"github.com/opentofu/opentofu/internal/tfdiags"
    16  	"github.com/zclconf/go-cty/cty"
    17  )
    18  
    19  // NodeApplyableProvider represents a provider during an apply.
    20  type NodeApplyableProvider struct {
    21  	*NodeAbstractProvider
    22  }
    23  
    24  var (
    25  	_ GraphNodeExecutable = (*NodeApplyableProvider)(nil)
    26  )
    27  
    28  // GraphNodeExecutable
    29  func (n *NodeApplyableProvider) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) {
    30  	_, err := ctx.InitProvider(n.Addr)
    31  	diags = diags.Append(err)
    32  	if diags.HasErrors() {
    33  		return diags
    34  	}
    35  	provider, _, err := getProvider(ctx, n.Addr)
    36  	diags = diags.Append(err)
    37  	if diags.HasErrors() {
    38  		return diags
    39  	}
    40  
    41  	switch op {
    42  	case walkValidate:
    43  		log.Printf("[TRACE] NodeApplyableProvider: validating configuration for %s", n.Addr)
    44  		return diags.Append(n.ValidateProvider(ctx, provider))
    45  	case walkPlan, walkPlanDestroy, walkApply, walkDestroy:
    46  		log.Printf("[TRACE] NodeApplyableProvider: configuring %s", n.Addr)
    47  		return diags.Append(n.ConfigureProvider(ctx, provider, false))
    48  	case walkImport:
    49  		log.Printf("[TRACE] NodeApplyableProvider: configuring %s (requiring that configuration is wholly known)", n.Addr)
    50  		return diags.Append(n.ConfigureProvider(ctx, provider, true))
    51  	}
    52  	return diags
    53  }
    54  
    55  func (n *NodeApplyableProvider) ValidateProvider(ctx EvalContext, provider providers.Interface) (diags tfdiags.Diagnostics) {
    56  
    57  	configBody := buildProviderConfig(ctx, n.Addr, n.ProviderConfig())
    58  
    59  	// if a provider config is empty (only an alias), return early and don't continue
    60  	// validation. validate doesn't need to fully configure the provider itself, so
    61  	// skipping a provider with an implied configuration won't prevent other validation from completing.
    62  	_, noConfigDiags := configBody.Content(&hcl.BodySchema{})
    63  	if !noConfigDiags.HasErrors() {
    64  		return nil
    65  	}
    66  
    67  	schemaResp := provider.GetProviderSchema()
    68  	diags = diags.Append(schemaResp.Diagnostics.InConfigBody(configBody, n.Addr.String()))
    69  	if diags.HasErrors() {
    70  		return diags
    71  	}
    72  
    73  	configSchema := schemaResp.Provider.Block
    74  	if configSchema == nil {
    75  		// Should never happen in real code, but often comes up in tests where
    76  		// mock schemas are being used that tend to be incomplete.
    77  		log.Printf("[WARN] ValidateProvider: no config schema is available for %s, so using empty schema", n.Addr)
    78  		configSchema = &configschema.Block{}
    79  	}
    80  
    81  	configVal, _, evalDiags := ctx.EvaluateBlock(configBody, configSchema, nil, EvalDataForNoInstanceKey)
    82  	if evalDiags.HasErrors() {
    83  		return diags.Append(evalDiags)
    84  	}
    85  	diags = diags.Append(evalDiags)
    86  
    87  	// If our config value contains any marked values, ensure those are
    88  	// stripped out before sending this to the provider
    89  	unmarkedConfigVal, _ := configVal.UnmarkDeep()
    90  
    91  	req := providers.ValidateProviderConfigRequest{
    92  		Config: unmarkedConfigVal,
    93  	}
    94  
    95  	validateResp := provider.ValidateProviderConfig(req)
    96  	diags = diags.Append(validateResp.Diagnostics.InConfigBody(configBody, n.Addr.String()))
    97  
    98  	return diags
    99  }
   100  
   101  // ConfigureProvider configures a provider that is already initialized and retrieved.
   102  // If verifyConfigIsKnown is true, ConfigureProvider will return an error if the
   103  // provider configVal is not wholly known and is meant only for use during import.
   104  func (n *NodeApplyableProvider) ConfigureProvider(ctx EvalContext, provider providers.Interface, verifyConfigIsKnown bool) (diags tfdiags.Diagnostics) {
   105  	config := n.ProviderConfig()
   106  
   107  	configBody := buildProviderConfig(ctx, n.Addr, config)
   108  
   109  	resp := provider.GetProviderSchema()
   110  	diags = diags.Append(resp.Diagnostics.InConfigBody(configBody, n.Addr.String()))
   111  	if diags.HasErrors() {
   112  		return diags
   113  	}
   114  
   115  	configSchema := resp.Provider.Block
   116  	configVal, configBody, evalDiags := ctx.EvaluateBlock(configBody, configSchema, nil, EvalDataForNoInstanceKey)
   117  	diags = diags.Append(evalDiags)
   118  	if evalDiags.HasErrors() {
   119  		return diags
   120  	}
   121  
   122  	if verifyConfigIsKnown && !configVal.IsWhollyKnown() {
   123  		diags = diags.Append(&hcl.Diagnostic{
   124  			Severity: hcl.DiagError,
   125  			Summary:  "Invalid provider configuration",
   126  			Detail:   fmt.Sprintf("The configuration for %s depends on values that cannot be determined until apply.", n.Addr),
   127  			Subject:  &config.DeclRange,
   128  		})
   129  		return diags
   130  	}
   131  
   132  	// If our config value contains any marked values, ensure those are
   133  	// stripped out before sending this to the provider
   134  	unmarkedConfigVal, _ := configVal.UnmarkDeep()
   135  
   136  	// Allow the provider to validate and insert any defaults into the full
   137  	// configuration.
   138  	req := providers.ValidateProviderConfigRequest{
   139  		Config: unmarkedConfigVal,
   140  	}
   141  
   142  	// ValidateProviderConfig is only used for validation. We are intentionally
   143  	// ignoring the PreparedConfig field to maintain existing behavior.
   144  	validateResp := provider.ValidateProviderConfig(req)
   145  	diags = diags.Append(validateResp.Diagnostics.InConfigBody(configBody, n.Addr.String()))
   146  	if diags.HasErrors() && config == nil {
   147  		// If there isn't an explicit "provider" block in the configuration,
   148  		// this error message won't be very clear. Add some detail to the error
   149  		// message in this case.
   150  		diags = diags.Append(tfdiags.Sourceless(
   151  			tfdiags.Error,
   152  			"Invalid provider configuration",
   153  			fmt.Sprintf(providerConfigErr, n.Addr.Provider),
   154  		))
   155  	}
   156  
   157  	if diags.HasErrors() {
   158  		return diags
   159  	}
   160  
   161  	// If the provider returns something different, log a warning to help
   162  	// indicate to provider developers that the value is not used.
   163  	preparedCfg := validateResp.PreparedConfig
   164  	if preparedCfg != cty.NilVal && !preparedCfg.IsNull() && !preparedCfg.RawEquals(unmarkedConfigVal) {
   165  		log.Printf("[WARN] ValidateProviderConfig from %q changed the config value, but that value is unused", n.Addr)
   166  	}
   167  
   168  	configDiags := ctx.ConfigureProvider(n.Addr, unmarkedConfigVal)
   169  	diags = diags.Append(configDiags.InConfigBody(configBody, n.Addr.String()))
   170  	if diags.HasErrors() && config == nil {
   171  		// If there isn't an explicit "provider" block in the configuration,
   172  		// this error message won't be very clear. Add some detail to the error
   173  		// message in this case.
   174  		diags = diags.Append(tfdiags.Sourceless(
   175  			tfdiags.Error,
   176  			"Invalid provider configuration",
   177  			fmt.Sprintf(providerConfigErr, n.Addr.Provider),
   178  		))
   179  	}
   180  	return diags
   181  }
   182  
   183  const providerConfigErr = `Provider %q requires explicit configuration. Add a provider block to the root module and configure the provider's required arguments as described in the provider documentation.
   184  `