github.com/wikibal01/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 }