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