github.com/ibm-cloud/terraform@v0.6.4-0.20170726051544-8872b87621df/terraform/node_resource_refresh.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/terraform/config" 7 "github.com/hashicorp/terraform/dag" 8 ) 9 10 // NodeRefreshableManagedResource represents a resource that is expanabled into 11 // NodeRefreshableManagedResourceInstance. Resource count orphans are also added. 12 type NodeRefreshableManagedResource struct { 13 *NodeAbstractCountResource 14 } 15 16 // GraphNodeDynamicExpandable 17 func (n *NodeRefreshableManagedResource) DynamicExpand(ctx EvalContext) (*Graph, error) { 18 // Grab the state which we read 19 state, lock := ctx.State() 20 lock.RLock() 21 defer lock.RUnlock() 22 23 // Expand the resource count which must be available by now from EvalTree 24 count, err := n.Config.Count() 25 if err != nil { 26 return nil, err 27 } 28 29 // The concrete resource factory we'll use 30 concreteResource := func(a *NodeAbstractResource) dag.Vertex { 31 // Add the config and state since we don't do that via transforms 32 a.Config = n.Config 33 34 return &NodeRefreshableManagedResourceInstance{ 35 NodeAbstractResource: a, 36 } 37 } 38 39 // Start creating the steps 40 steps := []GraphTransformer{ 41 // Expand the count. 42 &ResourceCountTransformer{ 43 Concrete: concreteResource, 44 Count: count, 45 Addr: n.ResourceAddr(), 46 }, 47 48 // Switch up any node missing state to a plannable resource. This helps 49 // catch cases where data sources depend on the counts from this resource 50 // during a scale out. 51 &ResourceRefreshPlannableTransformer{ 52 State: state, 53 }, 54 55 // Add the count orphans to make sure these resources are accounted for 56 // during a scale in. 57 &OrphanResourceCountTransformer{ 58 Concrete: concreteResource, 59 Count: count, 60 Addr: n.ResourceAddr(), 61 State: state, 62 }, 63 64 // Attach the state 65 &AttachStateTransformer{State: state}, 66 67 // Targeting 68 &TargetsTransformer{ParsedTargets: n.Targets}, 69 70 // Connect references so ordering is correct 71 &ReferenceTransformer{}, 72 73 // Make sure there is a single root 74 &RootTransformer{}, 75 } 76 77 // Build the graph 78 b := &BasicGraphBuilder{ 79 Steps: steps, 80 Validate: true, 81 Name: "NodeRefreshableManagedResource", 82 } 83 84 return b.Build(ctx.Path()) 85 } 86 87 // NodeRefreshableManagedResourceInstance represents a resource that is "applyable": 88 // it is ready to be applied and is represented by a diff. 89 type NodeRefreshableManagedResourceInstance struct { 90 *NodeAbstractResource 91 } 92 93 // GraphNodeDestroyer 94 func (n *NodeRefreshableManagedResourceInstance) DestroyAddr() *ResourceAddress { 95 return n.Addr 96 } 97 98 // GraphNodeEvalable 99 func (n *NodeRefreshableManagedResourceInstance) EvalTree() EvalNode { 100 // Eval info is different depending on what kind of resource this is 101 switch mode := n.Addr.Mode; mode { 102 case config.ManagedResourceMode: 103 return n.evalTreeManagedResource() 104 105 case config.DataResourceMode: 106 // Get the data source node. If we don't have a configuration 107 // then it is an orphan so we destroy it (remove it from the state). 108 var dn GraphNodeEvalable 109 if n.Config != nil { 110 dn = &NodeRefreshableDataResourceInstance{ 111 NodeAbstractResource: n.NodeAbstractResource, 112 } 113 } else { 114 dn = &NodeDestroyableDataResource{ 115 NodeAbstractResource: n.NodeAbstractResource, 116 } 117 } 118 119 return dn.EvalTree() 120 default: 121 panic(fmt.Errorf("unsupported resource mode %s", mode)) 122 } 123 } 124 125 func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResource() EvalNode { 126 addr := n.NodeAbstractResource.Addr 127 128 // stateId is the ID to put into the state 129 stateId := addr.stateId() 130 131 // Build the instance info. More of this will be populated during eval 132 info := &InstanceInfo{ 133 Id: stateId, 134 Type: addr.Type, 135 } 136 137 // Declare a bunch of variables that are used for state during 138 // evaluation. Most of this are written to by-address below. 139 var provider ResourceProvider 140 var state *InstanceState 141 142 // This happened during initial development. All known cases were 143 // fixed and tested but as a sanity check let's assert here. 144 if n.ResourceState == nil { 145 err := fmt.Errorf( 146 "No resource state attached for addr: %s\n\n"+ 147 "This is a bug. Please report this to Terraform with your configuration\n"+ 148 "and state attached. Please be careful to scrub any sensitive information.", 149 addr) 150 return &EvalReturnError{Error: &err} 151 } 152 153 return &EvalSequence{ 154 Nodes: []EvalNode{ 155 &EvalGetProvider{ 156 Name: n.ProvidedBy()[0], 157 Output: &provider, 158 }, 159 &EvalReadState{ 160 Name: stateId, 161 Output: &state, 162 }, 163 &EvalRefresh{ 164 Info: info, 165 Provider: &provider, 166 State: &state, 167 Output: &state, 168 }, 169 &EvalWriteState{ 170 Name: stateId, 171 ResourceType: n.ResourceState.Type, 172 Provider: n.ResourceState.Provider, 173 Dependencies: n.ResourceState.Dependencies, 174 State: &state, 175 }, 176 }, 177 } 178 }