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