github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/terraform/node_provider.go (about)

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