kubeform.dev/terraform-backend-sdk@v0.0.0-20220310143633-45f07fe731c5/terraform/node_provider.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 7 "github.com/hashicorp/hcl/v2" 8 "kubeform.dev/terraform-backend-sdk/configs/configschema" 9 "kubeform.dev/terraform-backend-sdk/providers" 10 "kubeform.dev/terraform-backend-sdk/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 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 `