github.com/hs0210/hashicorp-terraform@v0.11.12-beta1/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  			Provider: n.ResolvedProvider,
   113  		}
   114  	}
   115  
   116  	// If the config isn't empty we update the state
   117  	if n.Config != nil {
   118  		rs = &ResourceState{
   119  			Type:         n.Config.Type,
   120  			Provider:     n.Config.Provider,
   121  			Dependencies: n.StateReferences(),
   122  		}
   123  	}
   124  
   125  	// Build the resource for eval
   126  	resource := &Resource{
   127  		Name:       addr.Name,
   128  		Type:       addr.Type,
   129  		CountIndex: addr.Index,
   130  	}
   131  	if resource.CountIndex < 0 {
   132  		resource.CountIndex = 0
   133  	}
   134  
   135  	// Declare a bunch of variables that are used for state during
   136  	// evaluation. Most of this are written to by-address below.
   137  	var config *ResourceConfig
   138  	var diff *InstanceDiff
   139  	var provider ResourceProvider
   140  	var state *InstanceState
   141  
   142  	return &EvalSequence{
   143  		Nodes: []EvalNode{
   144  			// Always destroy the existing state first, since we must
   145  			// make sure that values from a previous read will not
   146  			// get interpolated if we end up needing to defer our
   147  			// loading until apply time.
   148  			&EvalWriteState{
   149  				Name:         stateId,
   150  				ResourceType: rs.Type,
   151  				Provider:     n.ResolvedProvider,
   152  				Dependencies: rs.Dependencies,
   153  				State:        &state, // state is nil here
   154  			},
   155  
   156  			&EvalInterpolate{
   157  				Config:   n.Config.RawConfig.Copy(),
   158  				Resource: resource,
   159  				Output:   &config,
   160  			},
   161  
   162  			// The rest of this pass can proceed only if there are no
   163  			// computed values in our config.
   164  			// (If there are, we'll deal with this during the plan and
   165  			// apply phases.)
   166  			&EvalIf{
   167  				If: func(ctx EvalContext) (bool, error) {
   168  					if config.ComputedKeys != nil && len(config.ComputedKeys) > 0 {
   169  						return true, EvalEarlyExitError{}
   170  					}
   171  
   172  					// If the config explicitly has a depends_on for this
   173  					// data source, assume the intention is to prevent
   174  					// refreshing ahead of that dependency.
   175  					if len(n.Config.DependsOn) > 0 {
   176  						return true, EvalEarlyExitError{}
   177  					}
   178  
   179  					return true, nil
   180  				},
   181  
   182  				Then: EvalNoop{},
   183  			},
   184  
   185  			// The remainder of this pass is the same as running
   186  			// a "plan" pass immediately followed by an "apply" pass,
   187  			// populating the state early so it'll be available to
   188  			// provider configurations that need this data during
   189  			// refresh/plan.
   190  			&EvalGetProvider{
   191  				Name:   n.ResolvedProvider,
   192  				Output: &provider,
   193  			},
   194  
   195  			&EvalReadDataDiff{
   196  				Info:        info,
   197  				Config:      &config,
   198  				Provider:    &provider,
   199  				Output:      &diff,
   200  				OutputState: &state,
   201  			},
   202  
   203  			&EvalReadDataApply{
   204  				Info:     info,
   205  				Diff:     &diff,
   206  				Provider: &provider,
   207  				Output:   &state,
   208  			},
   209  
   210  			&EvalWriteState{
   211  				Name:         stateId,
   212  				ResourceType: rs.Type,
   213  				Provider:     n.ResolvedProvider,
   214  				Dependencies: rs.Dependencies,
   215  				State:        &state,
   216  			},
   217  
   218  			&EvalUpdateStateHook{},
   219  		},
   220  	}
   221  }