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 // 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{
55 // Add the count orphans to make sure these resources are accounted for
57 &OrphanResourceCountTransformer{
58 Concrete: concreteResource,
60 Addr: n.ResourceAddr(),
65 &AttachStateTransformer{State: state},
68 &TargetsTransformer{ParsedTargets: n.Targets},
70 // Connect references so ordering is correct
71 &ReferenceTransformer{},
73 // Make sure there is a single root
78 b := &BasicGraphBuilder{
81 Name: "NodeRefreshableManagedResource",
84 return b.Build(ctx.Path())
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 {
94 func (n *NodeRefreshableManagedResourceInstance) DestroyAddr() *ResourceAddress {
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()
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
110 dn = &NodeRefreshableDataResourceInstance{
111 NodeAbstractResource: n.NodeAbstractResource,
114 dn = &NodeDestroyableDataResource{
115 NodeAbstractResource: n.NodeAbstractResource,
121 panic(fmt.Errorf("unsupported resource mode %s", mode))
125 func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResource() EvalNode {
126 addr := n.NodeAbstractResource.Addr
128 // stateId is the ID to put into the state
129 stateId := addr.stateId()
131 // Build the instance info. More of this will be populated during eval
132 info := &InstanceInfo{
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
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 {
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.",
150 return &EvalReturnError{Error: &err}
153 return &EvalSequence{
156 Name: n.ProvidedBy()[0],
171 ResourceType: n.ResourceState.Type,
172 Provider: n.ResourceState.Provider,
173 Dependencies: n.ResourceState.Dependencies,