github.com/opentofu/opentofu@v1.7.1/internal/tofu/transform_orphan_output.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package tofu 7 8 import ( 9 "log" 10 11 "github.com/opentofu/opentofu/internal/addrs" 12 "github.com/opentofu/opentofu/internal/configs" 13 "github.com/opentofu/opentofu/internal/states" 14 ) 15 16 // OrphanOutputTransformer finds the outputs that aren't present 17 // in the given config that are in the state and adds them to the graph 18 // for deletion. 19 type OrphanOutputTransformer struct { 20 Config *configs.Config // Root of config tree 21 State *states.State // State is the root state 22 Planning bool 23 } 24 25 func (t *OrphanOutputTransformer) Transform(g *Graph) error { 26 if t.State == nil { 27 log.Printf("[DEBUG] No state, no orphan outputs") 28 return nil 29 } 30 31 for _, ms := range t.State.Modules { 32 if err := t.transform(g, ms); err != nil { 33 return err 34 } 35 } 36 return nil 37 } 38 39 func (t *OrphanOutputTransformer) transform(g *Graph, ms *states.Module) error { 40 if ms == nil { 41 return nil 42 } 43 44 moduleAddr := ms.Addr 45 46 // Get the config for this path, which is nil if the entire module has been 47 // removed. 48 var outputs map[string]*configs.Output 49 if c := t.Config.DescendentForInstance(moduleAddr); c != nil { 50 outputs = c.Module.Outputs 51 } 52 53 // An output is "orphaned" if it's present in the state but not declared 54 // in the configuration. 55 for name := range ms.OutputValues { 56 if _, exists := outputs[name]; exists { 57 continue 58 } 59 60 g.Add(&NodeDestroyableOutput{ 61 Addr: addrs.OutputValue{Name: name}.Absolute(moduleAddr), 62 Planning: t.Planning, 63 }) 64 } 65 66 return nil 67 }