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