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  }