github.com/nathanielks/terraform@v0.6.1-0.20170509030759-13e1a62319dc/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 rs = &ResourceState{ 96 Type: n.Config.Type, 97 Provider: n.Config.Provider, 98 Dependencies: n.StateReferences(), 99 } 100 } 101 102 // Build the resource for eval 103 resource := &Resource{ 104 Name: addr.Name, 105 Type: addr.Type, 106 CountIndex: addr.Index, 107 } 108 if resource.CountIndex < 0 { 109 resource.CountIndex = 0 110 } 111 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 118 119 return &EvalSequence{ 120 Nodes: []EvalNode{ 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. 125 &EvalWriteState{ 126 Name: stateId, 127 ResourceType: rs.Type, 128 Provider: rs.Provider, 129 Dependencies: rs.Dependencies, 130 State: &state, // state is nil here 131 }, 132 133 &EvalInterpolate{ 134 Config: n.Config.RawConfig.Copy(), 135 Resource: resource, 136 Output: &config, 137 }, 138 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 142 // apply phases.) 143 &EvalIf{ 144 If: func(ctx EvalContext) (bool, error) { 145 if config.ComputedKeys != nil && len(config.ComputedKeys) > 0 { 146 return true, EvalEarlyExitError{} 147 } 148 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{} 154 } 155 156 return true, nil 157 }, 158 159 Then: EvalNoop{}, 160 }, 161 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 166 // refresh/plan. 167 &EvalGetProvider{ 168 Name: n.ProvidedBy()[0], 169 Output: &provider, 170 }, 171 172 &EvalReadDataDiff{ 173 Info: info, 174 Config: &config, 175 Provider: &provider, 176 Output: &diff, 177 OutputState: &state, 178 }, 179 180 &EvalReadDataApply{ 181 Info: info, 182 Diff: &diff, 183 Provider: &provider, 184 Output: &state, 185 }, 186 187 &EvalWriteState{ 188 Name: stateId, 189 ResourceType: rs.Type, 190 Provider: rs.Provider, 191 Dependencies: rs.Dependencies, 192 State: &state, 193 }, 194 195 &EvalUpdateStateHook{}, 196 }, 197 } 198 }