github.com/opentofu/opentofu@v1.7.1/internal/tofu/context_eval.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/lang" 14 "github.com/opentofu/opentofu/internal/states" 15 "github.com/opentofu/opentofu/internal/tfdiags" 16 ) 17 18 type EvalOpts struct { 19 SetVariables InputValues 20 } 21 22 // Eval produces a scope in which expressions can be evaluated for 23 // the given module path. 24 // 25 // This method must first evaluate any ephemeral values (input variables, local 26 // values, and output values) in the configuration. These ephemeral values are 27 // not included in the persisted state, so they must be re-computed using other 28 // values in the state before they can be properly evaluated. The updated 29 // values are retained in the main state associated with the receiving context. 30 // 31 // This function takes no action against remote APIs but it does need access 32 // to all provider and provisioner instances in order to obtain their schemas 33 // for type checking. 34 // 35 // The result is an evaluation scope that can be used to resolve references 36 // against the root module. If the returned diagnostics contains errors then 37 // the returned scope may be nil. If it is not nil then it may still be used 38 // to attempt expression evaluation or other analysis, but some expressions 39 // may not behave as expected. 40 func (c *Context) Eval(config *configs.Config, state *states.State, moduleAddr addrs.ModuleInstance, opts *EvalOpts) (*lang.Scope, tfdiags.Diagnostics) { 41 // This is intended for external callers such as the "tofu console" 42 // command. Internally, we create an evaluator in c.walk before walking 43 // the graph, and create scopes in ContextGraphWalker. 44 45 var diags tfdiags.Diagnostics 46 defer c.acquireRun("eval")() 47 48 // Start with a copy of state so that we don't affect the instance that 49 // the caller is holding. 50 state = state.DeepCopy() 51 var walker *ContextGraphWalker 52 53 variables := opts.SetVariables 54 55 // By the time we get here, we should have values defined for all of 56 // the root module variables, even if some of them are "unknown". It's the 57 // caller's responsibility to have already handled the decoding of these 58 // from the various ways the CLI allows them to be set and to produce 59 // user-friendly error messages if they are not all present, and so 60 // the error message from checkInputVariables should never be seen and 61 // includes language asking the user to report a bug. 62 varDiags := checkInputVariables(config.Module.Variables, variables) 63 diags = diags.Append(varDiags) 64 65 log.Printf("[DEBUG] Building and walking 'eval' graph") 66 67 graph, moreDiags := (&EvalGraphBuilder{ 68 Config: config, 69 State: state, 70 RootVariableValues: variables, 71 Plugins: c.plugins, 72 }).Build(addrs.RootModuleInstance) 73 diags = diags.Append(moreDiags) 74 if moreDiags.HasErrors() { 75 return nil, diags 76 } 77 78 walkOpts := &graphWalkOpts{ 79 InputState: state, 80 Config: config, 81 } 82 83 walker, moreDiags = c.walk(graph, walkEval, walkOpts) 84 diags = diags.Append(moreDiags) 85 if walker != nil { 86 diags = diags.Append(walker.NonFatalDiagnostics) 87 } else { 88 // If we skipped walking the graph (due to errors) then we'll just 89 // use a placeholder graph walker here, which'll refer to the 90 // unmodified state. 91 walker = c.graphWalker(walkEval, walkOpts) 92 } 93 94 // This is a bit weird since we don't normally evaluate outside of 95 // the context of a walk, but we'll "re-enter" our desired path here 96 // just to get hold of an EvalContext for it. ContextGraphWalker 97 // caches its contexts, so we should get hold of the context that was 98 // previously used for evaluation here, unless we skipped walking. 99 evalCtx := walker.EnterPath(moduleAddr) 100 return evalCtx.EvaluationScope(nil, nil, EvalDataForNoInstanceKey), diags 101 }