github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/graph_walk_context.go (about)

     1  package terraform
     2  
     3  import (
     4  	"context"
     5  	"log"
     6  	"sync"
     7  
     8  	"github.com/zclconf/go-cty/cty"
     9  
    10  	"github.com/hashicorp/terraform-plugin-sdk/internal/addrs"
    11  	"github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema"
    12  	"github.com/hashicorp/terraform-plugin-sdk/internal/dag"
    13  	"github.com/hashicorp/terraform-plugin-sdk/internal/plans"
    14  	"github.com/hashicorp/terraform-plugin-sdk/internal/providers"
    15  	"github.com/hashicorp/terraform-plugin-sdk/internal/provisioners"
    16  	"github.com/hashicorp/terraform-plugin-sdk/internal/states"
    17  	"github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags"
    18  )
    19  
    20  // ContextGraphWalker is the GraphWalker implementation used with the
    21  // Context struct to walk and evaluate the graph.
    22  type ContextGraphWalker struct {
    23  	NullGraphWalker
    24  
    25  	// Configurable values
    26  	Context            *Context
    27  	State              *states.SyncState  // Used for safe concurrent access to state
    28  	Changes            *plans.ChangesSync // Used for safe concurrent writes to changes
    29  	Operation          walkOperation
    30  	StopContext        context.Context
    31  	RootVariableValues InputValues
    32  
    33  	// This is an output. Do not set this, nor read it while a graph walk
    34  	// is in progress.
    35  	NonFatalDiagnostics tfdiags.Diagnostics
    36  
    37  	errorLock          sync.Mutex
    38  	once               sync.Once
    39  	contexts           map[string]*BuiltinEvalContext
    40  	contextLock        sync.Mutex
    41  	variableValues     map[string]map[string]cty.Value
    42  	variableValuesLock sync.Mutex
    43  	providerCache      map[string]providers.Interface
    44  	providerSchemas    map[string]*ProviderSchema
    45  	providerLock       sync.Mutex
    46  	provisionerCache   map[string]provisioners.Interface
    47  	provisionerSchemas map[string]*configschema.Block
    48  	provisionerLock    sync.Mutex
    49  }
    50  
    51  func (w *ContextGraphWalker) EnterPath(path addrs.ModuleInstance) EvalContext {
    52  	w.once.Do(w.init)
    53  
    54  	w.contextLock.Lock()
    55  	defer w.contextLock.Unlock()
    56  
    57  	// If we already have a context for this path cached, use that
    58  	key := path.String()
    59  	if ctx, ok := w.contexts[key]; ok {
    60  		return ctx
    61  	}
    62  
    63  	// Our evaluator shares some locks with the main context and the walker
    64  	// so that we can safely run multiple evaluations at once across
    65  	// different modules.
    66  	evaluator := &Evaluator{
    67  		Meta:               w.Context.meta,
    68  		Config:             w.Context.config,
    69  		Operation:          w.Operation,
    70  		State:              w.State,
    71  		Changes:            w.Changes,
    72  		Schemas:            w.Context.schemas,
    73  		VariableValues:     w.variableValues,
    74  		VariableValuesLock: &w.variableValuesLock,
    75  	}
    76  
    77  	ctx := &BuiltinEvalContext{
    78  		StopContext:         w.StopContext,
    79  		PathValue:           path,
    80  		Hooks:               w.Context.hooks,
    81  		InputValue:          w.Context.uiInput,
    82  		Components:          w.Context.components,
    83  		Schemas:             w.Context.schemas,
    84  		ProviderCache:       w.providerCache,
    85  		ProviderInputConfig: w.Context.providerInputConfig,
    86  		ProviderLock:        &w.providerLock,
    87  		ProvisionerCache:    w.provisionerCache,
    88  		ProvisionerLock:     &w.provisionerLock,
    89  		ChangesValue:        w.Changes,
    90  		StateValue:          w.State,
    91  		Evaluator:           evaluator,
    92  		VariableValues:      w.variableValues,
    93  		VariableValuesLock:  &w.variableValuesLock,
    94  	}
    95  
    96  	w.contexts[key] = ctx
    97  	return ctx
    98  }
    99  
   100  func (w *ContextGraphWalker) EnterEvalTree(v dag.Vertex, n EvalNode) EvalNode {
   101  	log.Printf("[TRACE] [%s] Entering eval tree: %s", w.Operation, dag.VertexName(v))
   102  
   103  	// Acquire a lock on the semaphore
   104  	w.Context.parallelSem.Acquire()
   105  
   106  	// We want to filter the evaluation tree to only include operations
   107  	// that belong in this operation.
   108  	return EvalFilter(n, EvalNodeFilterOp(w.Operation))
   109  }
   110  
   111  func (w *ContextGraphWalker) ExitEvalTree(v dag.Vertex, output interface{}, err error) tfdiags.Diagnostics {
   112  	log.Printf("[TRACE] [%s] Exiting eval tree: %s", w.Operation, dag.VertexName(v))
   113  
   114  	// Release the semaphore
   115  	w.Context.parallelSem.Release()
   116  
   117  	if err == nil {
   118  		return nil
   119  	}
   120  
   121  	// Acquire the lock because anything is going to require a lock.
   122  	w.errorLock.Lock()
   123  	defer w.errorLock.Unlock()
   124  
   125  	// If the error is non-fatal then we'll accumulate its diagnostics in our
   126  	// non-fatal list, rather than returning it directly, so that the graph
   127  	// walk can continue.
   128  	if nferr, ok := err.(tfdiags.NonFatalError); ok {
   129  		log.Printf("[WARN] %s: %s", dag.VertexName(v), nferr)
   130  		w.NonFatalDiagnostics = w.NonFatalDiagnostics.Append(nferr.Diagnostics)
   131  		return nil
   132  	}
   133  
   134  	// Otherwise, we'll let our usual diagnostics machinery figure out how to
   135  	// unpack this as one or more diagnostic messages and return that. If we
   136  	// get down here then the returned diagnostics will contain at least one
   137  	// error, causing the graph walk to halt.
   138  	var diags tfdiags.Diagnostics
   139  	diags = diags.Append(err)
   140  	return diags
   141  }
   142  
   143  func (w *ContextGraphWalker) init() {
   144  	w.contexts = make(map[string]*BuiltinEvalContext)
   145  	w.providerCache = make(map[string]providers.Interface)
   146  	w.providerSchemas = make(map[string]*ProviderSchema)
   147  	w.provisionerCache = make(map[string]provisioners.Interface)
   148  	w.provisionerSchemas = make(map[string]*configschema.Block)
   149  	w.variableValues = make(map[string]map[string]cty.Value)
   150  
   151  	// Populate root module variable values. Other modules will be populated
   152  	// during the graph walk.
   153  	w.variableValues[""] = make(map[string]cty.Value)
   154  	for k, iv := range w.RootVariableValues {
   155  		w.variableValues[""][k] = iv.Value
   156  	}
   157  }