github.com/opentofu/opentofu@v1.7.1/internal/tofu/context_validate.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 "log" 10 11 "github.com/opentofu/opentofu/internal/addrs" 12 "github.com/opentofu/opentofu/internal/configs" 13 "github.com/opentofu/opentofu/internal/states" 14 "github.com/opentofu/opentofu/internal/tfdiags" 15 "github.com/zclconf/go-cty/cty" 16 ) 17 18 // Validate performs semantic validation of a configuration, and returns 19 // any warnings or errors. 20 // 21 // Syntax and structural checks are performed by the configuration loader, 22 // and so are not repeated here. 23 // 24 // Validate considers only the configuration and so it won't catch any 25 // errors caused by current values in the state, or other external information 26 // such as root module input variables. However, the Plan function includes 27 // all of the same checks as Validate, in addition to the other work it does 28 // to consider the previous run state and the planning options. 29 func (c *Context) Validate(config *configs.Config) tfdiags.Diagnostics { 30 defer c.acquireRun("validate")() 31 32 var diags tfdiags.Diagnostics 33 34 moreDiags := c.checkConfigDependencies(config) 35 diags = diags.Append(moreDiags) 36 // If required dependencies are not available then we'll bail early since 37 // otherwise we're likely to just see a bunch of other errors related to 38 // incompatibilities, which could be overwhelming for the user. 39 if diags.HasErrors() { 40 return diags 41 } 42 43 log.Printf("[DEBUG] Building and walking validate graph") 44 45 // Validate is to check if the given module is valid regardless of 46 // input values, current state, etc. Therefore we populate all of the 47 // input values with unknown values of the expected type, allowing us 48 // to perform a type check without assuming any particular values. 49 varValues := make(InputValues) 50 for name, variable := range config.Module.Variables { 51 ty := variable.Type 52 if ty == cty.NilType { 53 // Can't predict the type at all, so we'll just mark it as 54 // cty.DynamicVal (unknown value of cty.DynamicPseudoType). 55 ty = cty.DynamicPseudoType 56 } 57 varValues[name] = &InputValue{ 58 Value: cty.UnknownVal(ty), 59 SourceType: ValueFromUnknown, 60 } 61 } 62 63 graph, moreDiags := (&PlanGraphBuilder{ 64 Config: config, 65 Plugins: c.plugins, 66 State: states.NewState(), 67 RootVariableValues: varValues, 68 Operation: walkValidate, 69 }).Build(addrs.RootModuleInstance) 70 diags = diags.Append(moreDiags) 71 if moreDiags.HasErrors() { 72 return diags 73 } 74 75 walker, walkDiags := c.walk(graph, walkValidate, &graphWalkOpts{ 76 Config: config, 77 }) 78 diags = diags.Append(walker.NonFatalDiagnostics) 79 diags = diags.Append(walkDiags) 80 if walkDiags.HasErrors() { 81 return diags 82 } 83 84 return diags 85 }