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  }