github.com/nathanielks/terraform@v0.6.1-0.20170509030759-13e1a62319dc/terraform/node_data_refresh.go (about)

     1  package terraform
     2  
     3  import (
     4  	"github.com/hashicorp/terraform/dag"
     5  )
     6  
     7  // NodeRefreshableDataResource represents a resource that is "plannable":
     8  // it is ready to be planned in order to create a diff.
     9  type NodeRefreshableDataResource struct {
    10  	*NodeAbstractCountResource
    11  }
    12  
    13  // GraphNodeDynamicExpandable
    14  func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
    15  	// Grab the state which we read
    16  	state, lock := ctx.State()
    17  	lock.RLock()
    18  	defer lock.RUnlock()
    19  
    20  	// Expand the resource count which must be available by now from EvalTree
    21  	count, err := n.Config.Count()
    22  	if err != nil {
    23  		return nil, err
    24  	}
    25  
    26  	// The concrete resource factory we'll use
    27  	concreteResource := func(a *NodeAbstractResource) dag.Vertex {
    28  		// Add the config and state since we don't do that via transforms
    29  		a.Config = n.Config
    30  
    31  		return &NodeRefreshableDataResourceInstance{
    32  			NodeAbstractResource: a,
    33  		}
    34  	}
    35  
    36  	// Start creating the steps
    37  	steps := []GraphTransformer{
    38  		// Expand the count.
    39  		&ResourceCountTransformer{
    40  			Concrete: concreteResource,
    41  			Count:    count,
    42  			Addr:     n.ResourceAddr(),
    43  		},
    44  
    45  		// Attach the state
    46  		&AttachStateTransformer{State: state},
    47  
    48  		// Targeting
    49  		&TargetsTransformer{ParsedTargets: n.Targets},
    50  
    51  		// Connect references so ordering is correct
    52  		&ReferenceTransformer{},
    53  
    54  		// Make sure there is a single root
    55  		&RootTransformer{},
    56  	}
    57  
    58  	// Build the graph
    59  	b := &BasicGraphBuilder{
    60  		Steps:    steps,
    61  		Validate: true,
    62  		Name:     "NodeRefreshableDataResource",
    63  	}
    64  
    65  	return b.Build(ctx.Path())
    66  }
    67  
    68  // NodeRefreshableDataResourceInstance represents a _single_ resource instance
    69  // that is refreshable.
    70  type NodeRefreshableDataResourceInstance struct {
    71  	*NodeAbstractResource
    72  }
    73  
    74  // GraphNodeEvalable
    75  func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode {
    76  	addr := n.NodeAbstractResource.Addr
    77  
    78  	// stateId is the ID to put into the state
    79  	stateId := addr.stateId()
    80  
    81  	// Build the instance info. More of this will be populated during eval
    82  	info := &InstanceInfo{
    83  		Id:   stateId,
    84  		Type: addr.Type,
    85  	}
    86  
    87  	// Get the state if we have it, if not we build it
    88  	rs := n.ResourceState
    89  	if rs == nil {
    90  		rs = &ResourceState{}
    91  	}
    92  
    93  	// If the config isn't empty we update the state
    94  	if n.Config != nil {
    95  		rs = &ResourceState{
    96  			Type:         n.Config.Type,
    97  			Provider:     n.Config.Provider,
    98  			Dependencies: n.StateReferences(),
    99  		}
   100  	}
   101  
   102  	// Build the resource for eval
   103  	resource := &Resource{
   104  		Name:       addr.Name,
   105  		Type:       addr.Type,
   106  		CountIndex: addr.Index,
   107  	}
   108  	if resource.CountIndex < 0 {
   109  		resource.CountIndex = 0
   110  	}
   111  
   112  	// Declare a bunch of variables that are used for state during
   113  	// evaluation. Most of this are written to by-address below.
   114  	var config *ResourceConfig
   115  	var diff *InstanceDiff
   116  	var provider ResourceProvider
   117  	var state *InstanceState
   118  
   119  	return &EvalSequence{
   120  		Nodes: []EvalNode{
   121  			// Always destroy the existing state first, since we must
   122  			// make sure that values from a previous read will not
   123  			// get interpolated if we end up needing to defer our
   124  			// loading until apply time.
   125  			&EvalWriteState{
   126  				Name:         stateId,
   127  				ResourceType: rs.Type,
   128  				Provider:     rs.Provider,
   129  				Dependencies: rs.Dependencies,
   130  				State:        &state, // state is nil here
   131  			},
   132  
   133  			&EvalInterpolate{
   134  				Config:   n.Config.RawConfig.Copy(),
   135  				Resource: resource,
   136  				Output:   &config,
   137  			},
   138  
   139  			// The rest of this pass can proceed only if there are no
   140  			// computed values in our config.
   141  			// (If there are, we'll deal with this during the plan and
   142  			// apply phases.)
   143  			&EvalIf{
   144  				If: func(ctx EvalContext) (bool, error) {
   145  					if config.ComputedKeys != nil && len(config.ComputedKeys) > 0 {
   146  						return true, EvalEarlyExitError{}
   147  					}
   148  
   149  					// If the config explicitly has a depends_on for this
   150  					// data source, assume the intention is to prevent
   151  					// refreshing ahead of that dependency.
   152  					if len(n.Config.DependsOn) > 0 {
   153  						return true, EvalEarlyExitError{}
   154  					}
   155  
   156  					return true, nil
   157  				},
   158  
   159  				Then: EvalNoop{},
   160  			},
   161  
   162  			// The remainder of this pass is the same as running
   163  			// a "plan" pass immediately followed by an "apply" pass,
   164  			// populating the state early so it'll be available to
   165  			// provider configurations that need this data during
   166  			// refresh/plan.
   167  			&EvalGetProvider{
   168  				Name:   n.ProvidedBy()[0],
   169  				Output: &provider,
   170  			},
   171  
   172  			&EvalReadDataDiff{
   173  				Info:        info,
   174  				Config:      &config,
   175  				Provider:    &provider,
   176  				Output:      &diff,
   177  				OutputState: &state,
   178  			},
   179  
   180  			&EvalReadDataApply{
   181  				Info:     info,
   182  				Diff:     &diff,
   183  				Provider: &provider,
   184  				Output:   &state,
   185  			},
   186  
   187  			&EvalWriteState{
   188  				Name:         stateId,
   189  				ResourceType: rs.Type,
   190  				Provider:     rs.Provider,
   191  				Dependencies: rs.Dependencies,
   192  				State:        &state,
   193  			},
   194  
   195  			&EvalUpdateStateHook{},
   196  		},
   197  	}
   198  }