github.com/tomaszheflik/terraform@v0.7.3-0.20160827060421-32f990b41594/terraform/transform_orphan.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/terraform/config" 7 "github.com/hashicorp/terraform/config/module" 8 "github.com/hashicorp/terraform/dag" 9 ) 10 11 // GraphNodeStateRepresentative is an interface that can be implemented by 12 // a node to say that it is representing a resource in the state. 13 type GraphNodeStateRepresentative interface { 14 StateId() []string 15 } 16 17 // OrphanTransformer is a GraphTransformer that adds orphans to the 18 // graph. This transformer adds both resource and module orphans. 19 type OrphanTransformer struct { 20 // State is the global state. We require the global state to 21 // properly find module orphans at our path. 22 State *State 23 24 // Module is the root module. We'll look up the proper configuration 25 // using the graph path. 26 Module *module.Tree 27 28 // View, if non-nil will set a view on the module state. 29 View string 30 } 31 32 func (t *OrphanTransformer) Transform(g *Graph) error { 33 if t.State == nil { 34 // If the entire state is nil, there can't be any orphans 35 return nil 36 } 37 38 // Build up all our state representatives 39 resourceRep := make(map[string]struct{}) 40 for _, v := range g.Vertices() { 41 if sr, ok := v.(GraphNodeStateRepresentative); ok { 42 for _, k := range sr.StateId() { 43 resourceRep[k] = struct{}{} 44 } 45 } 46 } 47 48 var config *config.Config 49 if t.Module != nil { 50 if module := t.Module.Child(g.Path[1:]); module != nil { 51 config = module.Config() 52 } 53 } 54 55 var resourceVertexes []dag.Vertex 56 if state := t.State.ModuleByPath(g.Path); state != nil { 57 // If we have state, then we can have orphan resources 58 59 // If we have a view, get the view 60 if t.View != "" { 61 state = state.View(t.View) 62 } 63 64 resourceOrphans := state.Orphans(config) 65 66 resourceVertexes = make([]dag.Vertex, len(resourceOrphans)) 67 for i, k := range resourceOrphans { 68 // If this orphan is represented by some other node somehow, 69 // then ignore it. 70 if _, ok := resourceRep[k]; ok { 71 continue 72 } 73 74 rs := state.Resources[k] 75 76 rsk, err := ParseResourceStateKey(k) 77 if err != nil { 78 return err 79 } 80 resourceVertexes[i] = g.Add(&graphNodeOrphanResource{ 81 Path: g.Path, 82 ResourceKey: rsk, 83 Provider: rs.Provider, 84 dependentOn: rs.Dependencies, 85 }) 86 } 87 } 88 89 // Go over each module orphan and add it to the graph. We store the 90 // vertexes and states outside so that we can connect dependencies later. 91 moduleOrphans := t.State.ModuleOrphans(g.Path, config) 92 moduleVertexes := make([]dag.Vertex, len(moduleOrphans)) 93 for i, path := range moduleOrphans { 94 var deps []string 95 if s := t.State.ModuleByPath(path); s != nil { 96 deps = s.Dependencies 97 } 98 99 moduleVertexes[i] = g.Add(&graphNodeOrphanModule{ 100 Path: path, 101 dependentOn: deps, 102 }) 103 } 104 105 // Now do the dependencies. We do this _after_ adding all the orphan 106 // nodes above because there are cases in which the orphans themselves 107 // depend on other orphans. 108 109 // Resource dependencies 110 for _, v := range resourceVertexes { 111 g.ConnectDependent(v) 112 } 113 114 // Module dependencies 115 for _, v := range moduleVertexes { 116 g.ConnectDependent(v) 117 } 118 119 return nil 120 } 121 122 // graphNodeOrphanModule is the graph vertex representing an orphan resource.. 123 type graphNodeOrphanModule struct { 124 Path []string 125 126 dependentOn []string 127 } 128 129 func (n *graphNodeOrphanModule) DependableName() []string { 130 return []string{n.dependableName()} 131 } 132 133 func (n *graphNodeOrphanModule) DependentOn() []string { 134 return n.dependentOn 135 } 136 137 func (n *graphNodeOrphanModule) Name() string { 138 return fmt.Sprintf("%s (orphan)", n.dependableName()) 139 } 140 141 func (n *graphNodeOrphanModule) dependableName() string { 142 return fmt.Sprintf("module.%s", n.Path[len(n.Path)-1]) 143 } 144 145 // GraphNodeExpandable 146 func (n *graphNodeOrphanModule) Expand(b GraphBuilder) (GraphNodeSubgraph, error) { 147 g, err := b.Build(n.Path) 148 if err != nil { 149 return nil, err 150 } 151 152 return &GraphNodeBasicSubgraph{ 153 NameValue: n.Name(), 154 Graph: g, 155 }, nil 156 } 157 158 // graphNodeOrphanResource is the graph vertex representing an orphan resource.. 159 type graphNodeOrphanResource struct { 160 Path []string 161 ResourceKey *ResourceStateKey 162 Provider string 163 164 dependentOn []string 165 } 166 167 func (n *graphNodeOrphanResource) ConfigType() GraphNodeConfigType { 168 return GraphNodeConfigTypeResource 169 } 170 171 func (n *graphNodeOrphanResource) ResourceAddress() *ResourceAddress { 172 return &ResourceAddress{ 173 Index: n.ResourceKey.Index, 174 InstanceType: TypePrimary, 175 Name: n.ResourceKey.Name, 176 Path: n.Path[1:], 177 Type: n.ResourceKey.Type, 178 Mode: n.ResourceKey.Mode, 179 } 180 } 181 182 func (n *graphNodeOrphanResource) DependableName() []string { 183 return []string{n.dependableName()} 184 } 185 186 func (n *graphNodeOrphanResource) DependentOn() []string { 187 return n.dependentOn 188 } 189 190 func (n *graphNodeOrphanResource) Flatten(p []string) (dag.Vertex, error) { 191 return &graphNodeOrphanResourceFlat{ 192 graphNodeOrphanResource: n, 193 PathValue: p, 194 }, nil 195 } 196 197 func (n *graphNodeOrphanResource) Name() string { 198 return fmt.Sprintf("%s (orphan)", n.ResourceKey) 199 } 200 201 func (n *graphNodeOrphanResource) ProvidedBy() []string { 202 return []string{resourceProvider(n.ResourceKey.Type, n.Provider)} 203 } 204 205 // GraphNodeEvalable impl. 206 func (n *graphNodeOrphanResource) EvalTree() EvalNode { 207 208 seq := &EvalSequence{Nodes: make([]EvalNode, 0, 5)} 209 210 // Build instance info 211 info := &InstanceInfo{Id: n.ResourceKey.String(), Type: n.ResourceKey.Type} 212 seq.Nodes = append(seq.Nodes, &EvalInstanceInfo{Info: info}) 213 214 // Each resource mode has its own lifecycle 215 switch n.ResourceKey.Mode { 216 case config.ManagedResourceMode: 217 seq.Nodes = append( 218 seq.Nodes, 219 n.managedResourceEvalNodes(info)..., 220 ) 221 case config.DataResourceMode: 222 seq.Nodes = append( 223 seq.Nodes, 224 n.dataResourceEvalNodes(info)..., 225 ) 226 default: 227 panic(fmt.Errorf("unsupported resource mode %s", n.ResourceKey.Mode)) 228 } 229 230 return seq 231 } 232 233 func (n *graphNodeOrphanResource) managedResourceEvalNodes(info *InstanceInfo) []EvalNode { 234 var provider ResourceProvider 235 var state *InstanceState 236 237 nodes := make([]EvalNode, 0, 3) 238 239 // Refresh the resource 240 nodes = append(nodes, &EvalOpFilter{ 241 Ops: []walkOperation{walkRefresh}, 242 Node: &EvalSequence{ 243 Nodes: []EvalNode{ 244 &EvalGetProvider{ 245 Name: n.ProvidedBy()[0], 246 Output: &provider, 247 }, 248 &EvalReadState{ 249 Name: n.ResourceKey.String(), 250 Output: &state, 251 }, 252 &EvalRefresh{ 253 Info: info, 254 Provider: &provider, 255 State: &state, 256 Output: &state, 257 }, 258 &EvalWriteState{ 259 Name: n.ResourceKey.String(), 260 ResourceType: n.ResourceKey.Type, 261 Provider: n.Provider, 262 Dependencies: n.DependentOn(), 263 State: &state, 264 }, 265 }, 266 }, 267 }) 268 269 // Diff the resource 270 var diff *InstanceDiff 271 nodes = append(nodes, &EvalOpFilter{ 272 Ops: []walkOperation{walkPlan, walkPlanDestroy}, 273 Node: &EvalSequence{ 274 Nodes: []EvalNode{ 275 &EvalReadState{ 276 Name: n.ResourceKey.String(), 277 Output: &state, 278 }, 279 &EvalDiffDestroy{ 280 Info: info, 281 State: &state, 282 Output: &diff, 283 }, 284 &EvalWriteDiff{ 285 Name: n.ResourceKey.String(), 286 Diff: &diff, 287 }, 288 }, 289 }, 290 }) 291 292 // Apply 293 var err error 294 nodes = append(nodes, &EvalOpFilter{ 295 Ops: []walkOperation{walkApply, walkDestroy}, 296 Node: &EvalSequence{ 297 Nodes: []EvalNode{ 298 &EvalReadDiff{ 299 Name: n.ResourceKey.String(), 300 Diff: &diff, 301 }, 302 &EvalGetProvider{ 303 Name: n.ProvidedBy()[0], 304 Output: &provider, 305 }, 306 &EvalReadState{ 307 Name: n.ResourceKey.String(), 308 Output: &state, 309 }, 310 &EvalApply{ 311 Info: info, 312 State: &state, 313 Diff: &diff, 314 Provider: &provider, 315 Output: &state, 316 Error: &err, 317 }, 318 &EvalWriteState{ 319 Name: n.ResourceKey.String(), 320 ResourceType: n.ResourceKey.Type, 321 Provider: n.Provider, 322 Dependencies: n.DependentOn(), 323 State: &state, 324 }, 325 &EvalApplyPost{ 326 Info: info, 327 State: &state, 328 Error: &err, 329 }, 330 &EvalUpdateStateHook{}, 331 }, 332 }, 333 }) 334 335 return nodes 336 } 337 338 func (n *graphNodeOrphanResource) dataResourceEvalNodes(info *InstanceInfo) []EvalNode { 339 nodes := make([]EvalNode, 0, 3) 340 341 // This will remain nil, since we don't retain states for orphaned 342 // data resources. 343 var state *InstanceState 344 345 // On both refresh and apply we just drop our state altogether, 346 // since the config resource validation pass will have proven that the 347 // resources remaining in the configuration don't need it. 348 nodes = append(nodes, &EvalOpFilter{ 349 Ops: []walkOperation{walkRefresh, walkApply}, 350 Node: &EvalSequence{ 351 Nodes: []EvalNode{ 352 &EvalWriteState{ 353 Name: n.ResourceKey.String(), 354 ResourceType: n.ResourceKey.Type, 355 Provider: n.Provider, 356 Dependencies: n.DependentOn(), 357 State: &state, // state is nil 358 }, 359 }, 360 }, 361 }) 362 363 return nodes 364 } 365 366 func (n *graphNodeOrphanResource) dependableName() string { 367 return n.ResourceKey.String() 368 } 369 370 // GraphNodeDestroyable impl. 371 func (n *graphNodeOrphanResource) DestroyNode() GraphNodeDestroy { 372 return n 373 } 374 375 // GraphNodeDestroy impl. 376 func (n *graphNodeOrphanResource) CreateBeforeDestroy() bool { 377 return false 378 } 379 380 func (n *graphNodeOrphanResource) CreateNode() dag.Vertex { 381 return n 382 } 383 384 // Same as graphNodeOrphanResource, but for flattening 385 type graphNodeOrphanResourceFlat struct { 386 *graphNodeOrphanResource 387 388 PathValue []string 389 } 390 391 func (n *graphNodeOrphanResourceFlat) Name() string { 392 return fmt.Sprintf( 393 "%s.%s", modulePrefixStr(n.PathValue), n.graphNodeOrphanResource.Name()) 394 } 395 396 func (n *graphNodeOrphanResourceFlat) Path() []string { 397 return n.PathValue 398 } 399 400 // GraphNodeDestroyable impl. 401 func (n *graphNodeOrphanResourceFlat) DestroyNode() GraphNodeDestroy { 402 return n 403 } 404 405 // GraphNodeDestroy impl. 406 func (n *graphNodeOrphanResourceFlat) CreateBeforeDestroy() bool { 407 return false 408 } 409 410 func (n *graphNodeOrphanResourceFlat) CreateNode() dag.Vertex { 411 return n 412 } 413 414 func (n *graphNodeOrphanResourceFlat) ProvidedBy() []string { 415 return modulePrefixList( 416 n.graphNodeOrphanResource.ProvidedBy(), 417 modulePrefixStr(n.PathValue)) 418 }