4 "github.com/hashicorp/terraform/dag"
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
13 // GraphNodeDynamicExpandable
14 func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
15 // Grab the state which we read
16 state, lock := ctx.State()
20 // Expand the resource count which must be available by now from EvalTree
21 count, err := n.Config.Count()
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
31 return &NodeRefreshableDataResourceInstance{
32 NodeAbstractResource: a,
36 // Start creating the steps
37 steps := []GraphTransformer{
39 &ResourceCountTransformer{
40 Concrete: concreteResource,
42 Addr: n.ResourceAddr(),
46 &AttachStateTransformer{State: state},
49 &TargetsTransformer{ParsedTargets: n.Targets},
51 // Connect references so ordering is correct
52 &ReferenceTransformer{},
54 // Make sure there is a single root
59 b := &BasicGraphBuilder{
62 Name: "NodeRefreshableDataResource",
65 return b.Build(ctx.Path())
68 // NodeRefreshableDataResourceInstance represents a _single_ resource instance
69 // that is refreshable.
70 type NodeRefreshableDataResourceInstance struct {
75 func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode {
76 addr := n.NodeAbstractResource.Addr
78 // stateId is the ID to put into the state
79 stateId := addr.stateId()
81 // Build the instance info. More of this will be populated during eval
82 info := &InstanceInfo{
87 // Get the state if we have it, if not we build it
93 // If the config isn't empty we update the state
97 Provider: n.Config.Provider,
98 Dependencies: n.StateReferences(),
102 // Build the resource for eval
103 resource := &Resource{
106 CountIndex: addr.Index,
108 if resource.CountIndex < 0 {
109 resource.CountIndex = 0
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
119 return &EvalSequence{
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.
127 ResourceType: rs.Type,
128 Provider: rs.Provider,
129 Dependencies: rs.Dependencies,
130 State: &state, // state is nil here
134 Config: n.Config.RawConfig.Copy(),
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
144 If: func(ctx EvalContext) (bool, error) {
145 if config.ComputedKeys != nil && len(config.ComputedKeys) > 0 {
146 return true, EvalEarlyExitError{}
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{}
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
168 Name: n.ProvidedBy()[0],
189 ResourceType: rs.Type,
190 Provider: rs.Provider,
191 Dependencies: rs.Dependencies,
195 &EvalUpdateStateHook{},