github.com/skyscape-cloud-services/terraform@v0.9.2-0.20170609144644-7ece028a1747/terraform/transform_deposed.go (about) 1 package terraform 2 3 import "fmt" 4 5 // DeposedTransformer is a GraphTransformer that adds deposed resources 6 // to the graph. 7 type DeposedTransformer struct { 8 // State is the global state. We'll automatically find the correct 9 // ModuleState based on the Graph.Path that is being transformed. 10 State *State 11 12 // View, if non-empty, is the ModuleState.View used around the state 13 // to find deposed resources. 14 View string 15 } 16 17 func (t *DeposedTransformer) Transform(g *Graph) error { 18 state := t.State.ModuleByPath(g.Path) 19 if state == nil { 20 // If there is no state for our module there can't be any deposed 21 // resources, since they live in the state. 22 return nil 23 } 24 25 // If we have a view, apply it now 26 if t.View != "" { 27 state = state.View(t.View) 28 } 29 30 // Go through all the resources in our state to look for deposed resources 31 for k, rs := range state.Resources { 32 // If we have no deposed resources, then move on 33 if len(rs.Deposed) == 0 { 34 continue 35 } 36 deposed := rs.Deposed 37 38 for i, _ := range deposed { 39 g.Add(&graphNodeDeposedResource{ 40 Index: i, 41 ResourceName: k, 42 ResourceType: rs.Type, 43 Provider: rs.Provider, 44 }) 45 } 46 } 47 48 return nil 49 } 50 51 // graphNodeDeposedResource is the graph vertex representing a deposed resource. 52 type graphNodeDeposedResource struct { 53 Index int 54 ResourceName string 55 ResourceType string 56 Provider string 57 } 58 59 func (n *graphNodeDeposedResource) Name() string { 60 return fmt.Sprintf("%s (deposed #%d)", n.ResourceName, n.Index) 61 } 62 63 func (n *graphNodeDeposedResource) ProvidedBy() []string { 64 return []string{resourceProvider(n.ResourceName, n.Provider)} 65 } 66 67 // GraphNodeEvalable impl. 68 func (n *graphNodeDeposedResource) EvalTree() EvalNode { 69 var provider ResourceProvider 70 var state *InstanceState 71 72 seq := &EvalSequence{Nodes: make([]EvalNode, 0, 5)} 73 74 // Build instance info 75 info := &InstanceInfo{Id: n.Name(), Type: n.ResourceType} 76 seq.Nodes = append(seq.Nodes, &EvalInstanceInfo{Info: info}) 77 78 // Refresh the resource 79 seq.Nodes = append(seq.Nodes, &EvalOpFilter{ 80 Ops: []walkOperation{walkRefresh}, 81 Node: &EvalSequence{ 82 Nodes: []EvalNode{ 83 &EvalGetProvider{ 84 Name: n.ProvidedBy()[0], 85 Output: &provider, 86 }, 87 &EvalReadStateDeposed{ 88 Name: n.ResourceName, 89 Output: &state, 90 Index: n.Index, 91 }, 92 &EvalRefresh{ 93 Info: info, 94 Provider: &provider, 95 State: &state, 96 Output: &state, 97 }, 98 &EvalWriteStateDeposed{ 99 Name: n.ResourceName, 100 ResourceType: n.ResourceType, 101 Provider: n.Provider, 102 State: &state, 103 Index: n.Index, 104 }, 105 }, 106 }, 107 }) 108 109 // Apply 110 var diff *InstanceDiff 111 var err error 112 seq.Nodes = append(seq.Nodes, &EvalOpFilter{ 113 Ops: []walkOperation{walkApply, walkDestroy}, 114 Node: &EvalSequence{ 115 Nodes: []EvalNode{ 116 &EvalGetProvider{ 117 Name: n.ProvidedBy()[0], 118 Output: &provider, 119 }, 120 &EvalReadStateDeposed{ 121 Name: n.ResourceName, 122 Output: &state, 123 Index: n.Index, 124 }, 125 &EvalDiffDestroy{ 126 Info: info, 127 State: &state, 128 Output: &diff, 129 }, 130 // Call pre-apply hook 131 &EvalApplyPre{ 132 Info: info, 133 State: &state, 134 Diff: &diff, 135 }, 136 &EvalApply{ 137 Info: info, 138 State: &state, 139 Diff: &diff, 140 Provider: &provider, 141 Output: &state, 142 Error: &err, 143 }, 144 // Always write the resource back to the state deposed... if it 145 // was successfully destroyed it will be pruned. If it was not, it will 146 // be caught on the next run. 147 &EvalWriteStateDeposed{ 148 Name: n.ResourceName, 149 ResourceType: n.ResourceType, 150 Provider: n.Provider, 151 State: &state, 152 Index: n.Index, 153 }, 154 &EvalApplyPost{ 155 Info: info, 156 State: &state, 157 Error: &err, 158 }, 159 &EvalReturnError{ 160 Error: &err, 161 }, 162 &EvalUpdateStateHook{}, 163 }, 164 }, 165 }) 166 167 return seq 168 }