6 "github.com/hashicorp/terraform/config"
7 "github.com/hashicorp/terraform/dag"
10 // NodeRefreshableManagedResource represents a resource that is expanabled into
11 // NodeRefreshableManagedResourceInstance. Resource count orphans are also added.
12 type NodeRefreshableManagedResource struct {
13 *NodeAbstractCountResource
16 // GraphNodeDynamicExpandable
17 func (n *NodeRefreshableManagedResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
18 // Grab the state which we read
19 state, lock := ctx.State()
23 // Expand the resource count which must be available by now from EvalTree
24 count, err := n.Config.Count()
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
34 return &NodeRefreshableManagedResourceInstance{
35 NodeAbstractResource: a,
39 // Start creating the steps
40 steps := []GraphTransformer{
42 &ResourceCountTransformer{
43 Concrete: concreteResource,
45 Addr: n.ResourceAddr(),
48 // Add the count orphans to make sure these resources are accounted for
50 &OrphanResourceCountTransformer{
51 Concrete: concreteResource,
53 Addr: n.ResourceAddr(),
58 &AttachStateTransformer{State: state},
61 &TargetsTransformer{ParsedTargets: n.Targets},
63 // Connect references so ordering is correct
64 &ReferenceTransformer{},
66 // Make sure there is a single root
71 b := &BasicGraphBuilder{
74 Name: "NodeRefreshableManagedResource",
77 return b.Build(ctx.Path())
80 // NodeRefreshableManagedResourceInstance represents a resource that is "applyable":
81 // it is ready to be applied and is represented by a diff.
82 type NodeRefreshableManagedResourceInstance struct {
87 func (n *NodeRefreshableManagedResourceInstance) DestroyAddr() *ResourceAddress {
92 func (n *NodeRefreshableManagedResourceInstance) EvalTree() EvalNode {
93 // Eval info is different depending on what kind of resource this is
94 switch mode := n.Addr.Mode; mode {
95 case config.ManagedResourceMode:
96 if n.ResourceState == nil {
97 return n.evalTreeManagedResourceNoState()
99 return n.evalTreeManagedResource()
101 case config.DataResourceMode:
102 // Get the data source node. If we don't have a configuration
103 // then it is an orphan so we destroy it (remove it from the state).
104 var dn GraphNodeEvalable
106 dn = &NodeRefreshableDataResourceInstance{
107 NodeAbstractResource: n.NodeAbstractResource,
110 dn = &NodeDestroyableDataResource{
111 NodeAbstractResource: n.NodeAbstractResource,
117 panic(fmt.Errorf("unsupported resource mode %s", mode))
121 func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResource() EvalNode {
122 addr := n.NodeAbstractResource.Addr
124 // stateId is the ID to put into the state
125 stateId := addr.stateId()
127 // Build the instance info. More of this will be populated during eval
128 info := &InstanceInfo{
133 // Declare a bunch of variables that are used for state during
134 // evaluation. Most of this are written to by-address below.
135 var provider ResourceProvider
136 var state *InstanceState
138 // This happened during initial development. All known cases were
139 // fixed and tested but as a sanity check let's assert here.
140 if n.ResourceState == nil {
142 "No resource state attached for addr: %s\n\n"+
143 "This is a bug. Please report this to Terraform with your configuration\n"+
144 "and state attached. Please be careful to scrub any sensitive information.",
146 return &EvalReturnError{Error: &err}
149 return &EvalSequence{
152 Name: n.ProvidedBy()[0],
167 ResourceType: n.ResourceState.Type,
168 Provider: n.ResourceState.Provider,
169 Dependencies: n.ResourceState.Dependencies,
176 // evalTreeManagedResourceNoState produces an EvalSequence for refresh resource
177 // nodes that don't have state attached. An example of where this functionality
178 // is useful is when a resource that already exists in state is being scaled
179 // out, ie: has its resource count increased. In this case, the scaled out node
180 // needs to be available to other nodes (namely data sources) that may depend
181 // on it for proper interpolation, or confusing "index out of range" errors can
184 // The steps in this sequence are very similar to the steps carried out in
185 // plan, but nothing is done with the diff after it is created - it is dropped,
186 // and its changes are not counted in the UI.
187 func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResourceNoState() EvalNode {
188 // Declare a bunch of variables that are used for state during
189 // evaluation. Most of this are written to by-address below.
190 var provider ResourceProvider
191 var state *InstanceState
192 var resourceConfig *ResourceConfig
194 addr := n.NodeAbstractResource.Addr
195 stateID := addr.stateId()
196 info := &InstanceInfo{
199 ModulePath: normalizeModulePath(addr.Path),
202 // Build the resource for eval
203 resource := &Resource{
206 CountIndex: addr.Index,
208 if resource.CountIndex < 0 {
209 resource.CountIndex = 0
212 // Determine the dependencies for the state.
213 stateDeps := n.StateReferences()
215 return &EvalSequence{
218 Config: n.Config.RawConfig.Copy(),
220 Output: &resourceConfig,
223 Name: n.ProvidedBy()[0],
226 // Re-run validation to catch any errors we missed, e.g. type
227 // mismatches on computed values.
228 &EvalValidateResource{
230 Config: &resourceConfig,
231 ResourceName: n.Config.Name,
232 ResourceType: n.Config.Type,
233 ResourceMode: n.Config.Mode,
234 IgnoreWarnings: true,
243 Config: &resourceConfig,
252 ResourceType: n.Config.Type,
253 Provider: n.Config.Provider,
254 Dependencies: stateDeps,