github.com/opentofu/opentofu@v1.7.1/internal/tofu/graph_walk_context.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  	"context"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/zclconf/go-cty/cty"
    14  
    15  	"github.com/opentofu/opentofu/internal/addrs"
    16  	"github.com/opentofu/opentofu/internal/checks"
    17  	"github.com/opentofu/opentofu/internal/configs"
    18  	"github.com/opentofu/opentofu/internal/encryption"
    19  	"github.com/opentofu/opentofu/internal/instances"
    20  	"github.com/opentofu/opentofu/internal/plans"
    21  	"github.com/opentofu/opentofu/internal/providers"
    22  	"github.com/opentofu/opentofu/internal/provisioners"
    23  	"github.com/opentofu/opentofu/internal/refactoring"
    24  	"github.com/opentofu/opentofu/internal/states"
    25  	"github.com/opentofu/opentofu/internal/tfdiags"
    26  )
    27  
    28  // ContextGraphWalker is the GraphWalker implementation used with the
    29  // Context struct to walk and evaluate the graph.
    30  type ContextGraphWalker struct {
    31  	NullGraphWalker
    32  
    33  	// Configurable values
    34  	Context            *Context
    35  	State              *states.SyncState       // Used for safe concurrent access to state
    36  	RefreshState       *states.SyncState       // Used for safe concurrent access to state
    37  	PrevRunState       *states.SyncState       // Used for safe concurrent access to state
    38  	Changes            *plans.ChangesSync      // Used for safe concurrent writes to changes
    39  	Checks             *checks.State           // Used for safe concurrent writes of checkable objects and their check results
    40  	InstanceExpander   *instances.Expander     // Tracks our gradual expansion of module and resource instances
    41  	ImportResolver     *ImportResolver         // Tracks import targets as they are being resolved
    42  	MoveResults        refactoring.MoveResults // Read-only record of earlier processing of move statements
    43  	Operation          walkOperation
    44  	StopContext        context.Context
    45  	RootVariableValues InputValues
    46  	Config             *configs.Config
    47  	PlanTimestamp      time.Time
    48  	Encryption         encryption.Encryption
    49  
    50  	// This is an output. Do not set this, nor read it while a graph walk
    51  	// is in progress.
    52  	NonFatalDiagnostics tfdiags.Diagnostics
    53  
    54  	once               sync.Once
    55  	contexts           map[string]*BuiltinEvalContext
    56  	contextLock        sync.Mutex
    57  	variableValues     map[string]map[string]cty.Value
    58  	variableValuesLock sync.Mutex
    59  	providerCache      map[string]providers.Interface
    60  	providerLock       sync.Mutex
    61  	provisionerCache   map[string]provisioners.Interface
    62  	provisionerLock    sync.Mutex
    63  }
    64  
    65  func (w *ContextGraphWalker) EnterPath(path addrs.ModuleInstance) EvalContext {
    66  	w.contextLock.Lock()
    67  	defer w.contextLock.Unlock()
    68  
    69  	// If we already have a context for this path cached, use that
    70  	key := path.String()
    71  	if ctx, ok := w.contexts[key]; ok {
    72  		return ctx
    73  	}
    74  
    75  	ctx := w.EvalContext().WithPath(path)
    76  	w.contexts[key] = ctx.(*BuiltinEvalContext)
    77  	return ctx
    78  }
    79  
    80  func (w *ContextGraphWalker) EvalContext() EvalContext {
    81  	w.once.Do(w.init)
    82  
    83  	// Our evaluator shares some locks with the main context and the walker
    84  	// so that we can safely run multiple evaluations at once across
    85  	// different modules.
    86  	evaluator := &Evaluator{
    87  		Meta:               w.Context.meta,
    88  		Config:             w.Config,
    89  		Operation:          w.Operation,
    90  		State:              w.State,
    91  		Changes:            w.Changes,
    92  		Plugins:            w.Context.plugins,
    93  		VariableValues:     w.variableValues,
    94  		VariableValuesLock: &w.variableValuesLock,
    95  		PlanTimestamp:      w.PlanTimestamp,
    96  	}
    97  
    98  	ctx := &BuiltinEvalContext{
    99  		StopContext:           w.StopContext,
   100  		Hooks:                 w.Context.hooks,
   101  		InputValue:            w.Context.uiInput,
   102  		InstanceExpanderValue: w.InstanceExpander,
   103  		Plugins:               w.Context.plugins,
   104  		MoveResultsValue:      w.MoveResults,
   105  		ImportResolverValue:   w.ImportResolver,
   106  		ProviderCache:         w.providerCache,
   107  		ProviderInputConfig:   w.Context.providerInputConfig,
   108  		ProviderLock:          &w.providerLock,
   109  		ProvisionerCache:      w.provisionerCache,
   110  		ProvisionerLock:       &w.provisionerLock,
   111  		ChangesValue:          w.Changes,
   112  		ChecksValue:           w.Checks,
   113  		StateValue:            w.State,
   114  		RefreshStateValue:     w.RefreshState,
   115  		PrevRunStateValue:     w.PrevRunState,
   116  		Evaluator:             evaluator,
   117  		VariableValues:        w.variableValues,
   118  		VariableValuesLock:    &w.variableValuesLock,
   119  		Encryption:            w.Encryption,
   120  	}
   121  
   122  	return ctx
   123  }
   124  
   125  func (w *ContextGraphWalker) init() {
   126  	w.contexts = make(map[string]*BuiltinEvalContext)
   127  	w.providerCache = make(map[string]providers.Interface)
   128  	w.provisionerCache = make(map[string]provisioners.Interface)
   129  	w.variableValues = make(map[string]map[string]cty.Value)
   130  
   131  	// Populate root module variable values. Other modules will be populated
   132  	// during the graph walk.
   133  	w.variableValues[""] = make(map[string]cty.Value)
   134  	for k, iv := range w.RootVariableValues {
   135  		w.variableValues[""][k] = iv.Value
   136  	}
   137  }
   138  
   139  func (w *ContextGraphWalker) Execute(ctx EvalContext, n GraphNodeExecutable) tfdiags.Diagnostics {
   140  	// Acquire a lock on the semaphore
   141  	w.Context.parallelSem.Acquire()
   142  	defer w.Context.parallelSem.Release()
   143  
   144  	return n.Execute(ctx, w.Operation)
   145  }