github.com/opentofu/opentofu@v1.7.1/internal/tofu/transform_orphan_resource.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/configs"
    12  	"github.com/opentofu/opentofu/internal/dag"
    13  	"github.com/opentofu/opentofu/internal/states"
    14  )
    15  
    16  // OrphanResourceInstanceTransformer is a GraphTransformer that adds orphaned
    17  // resource instances to the graph. An "orphan" is an instance that is present
    18  // in the state but belongs to a resource that is no longer present in the
    19  // configuration.
    20  //
    21  // This is not the transformer that deals with "count orphans" (instances that
    22  // are no longer covered by a resource's "count" or "for_each" setting); that's
    23  // handled instead by OrphanResourceCountTransformer.
    24  type OrphanResourceInstanceTransformer struct {
    25  	Concrete ConcreteResourceInstanceNodeFunc
    26  
    27  	// State is the global state. We require the global state to
    28  	// properly find module orphans at our path.
    29  	State *states.State
    30  
    31  	// Config is the root node in the configuration tree. We'll look up
    32  	// the appropriate note in this tree using the path in each node.
    33  	Config *configs.Config
    34  
    35  	// Do not apply this transformer
    36  	skip bool
    37  }
    38  
    39  func (t *OrphanResourceInstanceTransformer) Transform(g *Graph) error {
    40  	if t.skip {
    41  		return nil
    42  	}
    43  
    44  	if t.State == nil {
    45  		// If the entire state is nil, there can't be any orphans
    46  		return nil
    47  	}
    48  	if t.Config == nil {
    49  		// Should never happen: we can't be doing any OpenTofu operations
    50  		// without at least an empty configuration.
    51  		panic("OrphanResourceInstanceTransformer used without setting Config")
    52  	}
    53  
    54  	// Go through the modules and for each module transform in order
    55  	// to add the orphan.
    56  	for _, ms := range t.State.Modules {
    57  		if err := t.transform(g, ms); err != nil {
    58  			return err
    59  		}
    60  	}
    61  
    62  	return nil
    63  }
    64  
    65  func (t *OrphanResourceInstanceTransformer) transform(g *Graph, ms *states.Module) error {
    66  	if ms == nil {
    67  		return nil
    68  	}
    69  
    70  	moduleAddr := ms.Addr
    71  
    72  	// Get the configuration for this module. The configuration might be
    73  	// nil if the module was removed from the configuration. This is okay,
    74  	// this just means that every resource is an orphan.
    75  	var m *configs.Module
    76  	if c := t.Config.DescendentForInstance(moduleAddr); c != nil {
    77  		m = c.Module
    78  	}
    79  
    80  	// An "orphan" is a resource that is in the state but not the configuration,
    81  	// so we'll walk the state resources and try to correlate each of them
    82  	// with a configuration block. Each orphan gets a node in the graph whose
    83  	// type is decided by t.Concrete.
    84  	//
    85  	// We don't handle orphans related to changes in the "count" and "for_each"
    86  	// pseudo-arguments here. They are handled by OrphanResourceCountTransformer.
    87  	for _, rs := range ms.Resources {
    88  		if m != nil {
    89  			if r := m.ResourceByAddr(rs.Addr.Resource); r != nil {
    90  				continue
    91  			}
    92  		}
    93  
    94  		for key, inst := range rs.Instances {
    95  			// Instances which have no current objects (only one or more
    96  			// deposed objects) will be taken care of separately
    97  			if inst.Current == nil {
    98  				continue
    99  			}
   100  
   101  			addr := rs.Addr.Instance(key)
   102  			abstract := NewNodeAbstractResourceInstance(addr)
   103  			var node dag.Vertex = abstract
   104  			if f := t.Concrete; f != nil {
   105  				node = f(abstract)
   106  			}
   107  			log.Printf("[TRACE] OrphanResourceInstanceTransformer: adding single-instance orphan node for %s", addr)
   108  			g.Add(node)
   109  		}
   110  	}
   111  
   112  	return nil
   113  }