github.com/skyscape-cloud-services/terraform@v0.9.2-0.20170609144644-7ece028a1747/terraform/graph_walk_context.go (about)

     1  package terraform
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log"
     7  	"sync"
     8  
     9  	"github.com/hashicorp/errwrap"
    10  	"github.com/hashicorp/terraform/dag"
    11  )
    12  
    13  // ContextGraphWalker is the GraphWalker implementation used with the
    14  // Context struct to walk and evaluate the graph.
    15  type ContextGraphWalker struct {
    16  	NullGraphWalker
    17  
    18  	// Configurable values
    19  	Context     *Context
    20  	Operation   walkOperation
    21  	StopContext context.Context
    22  
    23  	// Outputs, do not set these. Do not read these while the graph
    24  	// is being walked.
    25  	ValidationWarnings []string
    26  	ValidationErrors   []error
    27  
    28  	errorLock           sync.Mutex
    29  	once                sync.Once
    30  	contexts            map[string]*BuiltinEvalContext
    31  	contextLock         sync.Mutex
    32  	interpolaterVars    map[string]map[string]interface{}
    33  	interpolaterVarLock sync.Mutex
    34  	providerCache       map[string]ResourceProvider
    35  	providerConfigCache map[string]*ResourceConfig
    36  	providerLock        sync.Mutex
    37  	provisionerCache    map[string]ResourceProvisioner
    38  	provisionerLock     sync.Mutex
    39  }
    40  
    41  func (w *ContextGraphWalker) EnterPath(path []string) EvalContext {
    42  	w.once.Do(w.init)
    43  
    44  	w.contextLock.Lock()
    45  	defer w.contextLock.Unlock()
    46  
    47  	// If we already have a context for this path cached, use that
    48  	key := PathCacheKey(path)
    49  	if ctx, ok := w.contexts[key]; ok {
    50  		return ctx
    51  	}
    52  
    53  	// Setup the variables for this interpolater
    54  	variables := make(map[string]interface{})
    55  	if len(path) <= 1 {
    56  		for k, v := range w.Context.variables {
    57  			variables[k] = v
    58  		}
    59  	}
    60  	w.interpolaterVarLock.Lock()
    61  	if m, ok := w.interpolaterVars[key]; ok {
    62  		for k, v := range m {
    63  			variables[k] = v
    64  		}
    65  	}
    66  	w.interpolaterVars[key] = variables
    67  	w.interpolaterVarLock.Unlock()
    68  
    69  	ctx := &BuiltinEvalContext{
    70  		StopContext:         w.StopContext,
    71  		PathValue:           path,
    72  		Hooks:               w.Context.hooks,
    73  		InputValue:          w.Context.uiInput,
    74  		Components:          w.Context.components,
    75  		ProviderCache:       w.providerCache,
    76  		ProviderConfigCache: w.providerConfigCache,
    77  		ProviderInputConfig: w.Context.providerInputConfig,
    78  		ProviderLock:        &w.providerLock,
    79  		ProvisionerCache:    w.provisionerCache,
    80  		ProvisionerLock:     &w.provisionerLock,
    81  		DiffValue:           w.Context.diff,
    82  		DiffLock:            &w.Context.diffLock,
    83  		StateValue:          w.Context.state,
    84  		StateLock:           &w.Context.stateLock,
    85  		Interpolater: &Interpolater{
    86  			Operation:          w.Operation,
    87  			Meta:               w.Context.meta,
    88  			Module:             w.Context.module,
    89  			State:              w.Context.state,
    90  			StateLock:          &w.Context.stateLock,
    91  			VariableValues:     variables,
    92  			VariableValuesLock: &w.interpolaterVarLock,
    93  		},
    94  		InterpolaterVars:    w.interpolaterVars,
    95  		InterpolaterVarLock: &w.interpolaterVarLock,
    96  	}
    97  
    98  	w.contexts[key] = ctx
    99  	return ctx
   100  }
   101  
   102  func (w *ContextGraphWalker) EnterEvalTree(v dag.Vertex, n EvalNode) EvalNode {
   103  	log.Printf("[TRACE] [%s] Entering eval tree: %s",
   104  		w.Operation, dag.VertexName(v))
   105  
   106  	// Acquire a lock on the semaphore
   107  	w.Context.parallelSem.Acquire()
   108  
   109  	// We want to filter the evaluation tree to only include operations
   110  	// that belong in this operation.
   111  	return EvalFilter(n, EvalNodeFilterOp(w.Operation))
   112  }
   113  
   114  func (w *ContextGraphWalker) ExitEvalTree(
   115  	v dag.Vertex, output interface{}, err error) error {
   116  	log.Printf("[TRACE] [%s] Exiting eval tree: %s",
   117  		w.Operation, dag.VertexName(v))
   118  
   119  	// Release the semaphore
   120  	w.Context.parallelSem.Release()
   121  
   122  	if err == nil {
   123  		return nil
   124  	}
   125  
   126  	// Acquire the lock because anything is going to require a lock.
   127  	w.errorLock.Lock()
   128  	defer w.errorLock.Unlock()
   129  
   130  	// Try to get a validation error out of it. If its not a validation
   131  	// error, then just record the normal error.
   132  	verr, ok := err.(*EvalValidateError)
   133  	if !ok {
   134  		return err
   135  	}
   136  
   137  	for _, msg := range verr.Warnings {
   138  		w.ValidationWarnings = append(
   139  			w.ValidationWarnings,
   140  			fmt.Sprintf("%s: %s", dag.VertexName(v), msg))
   141  	}
   142  	for _, e := range verr.Errors {
   143  		w.ValidationErrors = append(
   144  			w.ValidationErrors,
   145  			errwrap.Wrapf(fmt.Sprintf("%s: {{err}}", dag.VertexName(v)), e))
   146  	}
   147  
   148  	return nil
   149  }
   150  
   151  func (w *ContextGraphWalker) init() {
   152  	w.contexts = make(map[string]*BuiltinEvalContext, 5)
   153  	w.providerCache = make(map[string]ResourceProvider, 5)
   154  	w.providerConfigCache = make(map[string]*ResourceConfig, 5)
   155  	w.provisionerCache = make(map[string]ResourceProvisioner, 5)
   156  	w.interpolaterVars = make(map[string]map[string]interface{}, 5)
   157  }