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