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
33 a.ResolvedProvider = n.ResolvedProvider
35 return &NodeRefreshableManagedResourceInstance{
36 NodeAbstractResource: a,
40 // Start creating the steps
41 steps := []GraphTransformer{
43 &ResourceCountTransformer{
44 Concrete: concreteResource,
46 Addr: n.ResourceAddr(),
49 // Add the count orphans to make sure these resources are accounted for
51 &OrphanResourceCountTransformer{
52 Concrete: concreteResource,
54 Addr: n.ResourceAddr(),
59 &AttachStateTransformer{State: state},
62 &TargetsTransformer{ParsedTargets: n.Targets},
64 // Connect references so ordering is correct
65 &ReferenceTransformer{},
67 // Make sure there is a single root
72 b := &BasicGraphBuilder{
75 Name: "NodeRefreshableManagedResource",
78 return b.Build(ctx.Path())
81 // NodeRefreshableManagedResourceInstance represents a resource that is "applyable":
82 // it is ready to be applied and is represented by a diff.
83 type NodeRefreshableManagedResourceInstance struct {
88 func (n *NodeRefreshableManagedResourceInstance) DestroyAddr() *ResourceAddress {
93 func (n *NodeRefreshableManagedResourceInstance) EvalTree() EvalNode {
94 // Eval info is different depending on what kind of resource this is
95 switch mode := n.Addr.Mode; mode {
96 case config.ManagedResourceMode:
97 if n.ResourceState == nil {
98 return n.evalTreeManagedResourceNoState()
100 return n.evalTreeManagedResource()
102 case config.DataResourceMode:
103 // Get the data source node. If we don't have a configuration
104 // then it is an orphan so we destroy it (remove it from the state).
105 var dn GraphNodeEvalable
107 dn = &NodeRefreshableDataResourceInstance{
108 NodeAbstractResource: n.NodeAbstractResource,
111 dn = &NodeDestroyableDataResource{
112 NodeAbstractResource: n.NodeAbstractResource,
118 panic(fmt.Errorf("unsupported resource mode %s", mode))
122 func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResource() EvalNode {
123 addr := n.NodeAbstractResource.Addr
125 // stateId is the ID to put into the state
126 stateId := addr.stateId()
128 // Build the instance info. More of this will be populated during eval
129 info := &InstanceInfo{
134 // Declare a bunch of variables that are used for state during
135 // evaluation. Most of this are written to by-address below.
136 var provider ResourceProvider
137 var state *InstanceState
139 // This happened during initial development. All known cases were
140 // fixed and tested but as a sanity check let's assert here.
141 if n.ResourceState == nil {
143 "No resource state attached for addr: %s\n\n"+
144 "This is a bug. Please report this to Terraform with your configuration\n"+
145 "and state attached. Please be careful to scrub any sensitive information.",
147 return &EvalReturnError{Error: &err}
150 return &EvalSequence{
153 Name: n.ResolvedProvider,
168 ResourceType: n.ResourceState.Type,
169 Provider: n.ResolvedProvider,
170 Dependencies: n.ResourceState.Dependencies,
177 // evalTreeManagedResourceNoState produces an EvalSequence for refresh resource
178 // nodes that don't have state attached. An example of where this functionality
179 // is useful is when a resource that already exists in state is being scaled
180 // out, ie: has its resource count increased. In this case, the scaled out node
181 // needs to be available to other nodes (namely data sources) that may depend
182 // on it for proper interpolation, or confusing "index out of range" errors can
185 // The steps in this sequence are very similar to the steps carried out in
186 // plan, but nothing is done with the diff after it is created - it is dropped,
187 // and its changes are not counted in the UI.
188 func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResourceNoState() EvalNode {
189 // Declare a bunch of variables that are used for state during
190 // evaluation. Most of this are written to by-address below.
191 var provider ResourceProvider
192 var state *InstanceState
193 var resourceConfig *ResourceConfig
195 addr := n.NodeAbstractResource.Addr
196 stateID := addr.stateId()
197 info := &InstanceInfo{
200 ModulePath: normalizeModulePath(addr.Path),
203 // Build the resource for eval
204 resource := &Resource{
207 CountIndex: addr.Index,
209 if resource.CountIndex < 0 {
210 resource.CountIndex = 0
213 // Determine the dependencies for the state.
214 stateDeps := n.StateReferences()
216 // n.Config can be nil if the config and state don't match
217 var raw *config.RawConfig
219 raw = n.Config.RawConfig.Copy()
222 return &EvalSequence{
227 Output: &resourceConfig,
230 Name: n.ResolvedProvider,
233 // Re-run validation to catch any errors we missed, e.g. type
234 // mismatches on computed values.
235 &EvalValidateResource{
237 Config: &resourceConfig,
238 ResourceName: n.Config.Name,
239 ResourceType: n.Config.Type,
240 ResourceMode: n.Config.Mode,
241 IgnoreWarnings: true,
250 Config: &resourceConfig,
259 ResourceType: n.Config.Type,
260 Provider: n.ResolvedProvider,
261 Dependencies: stateDeps,