Files
opentf/internal/tofu/transform_orphan_resource.go
Martin Atkins 75c7834ca6 tofu: GraphTransformer.Transform takes context.Context
Most of our transformers are pure compute and so don't really have a strong
need to generate trace spans under our current focus of only exposing
user-facing concepts and external requests in our traces, but unfortunately
some of them indirectly depend on provider schema, which in turn means that
they can potentially be unlucky enough to be the trigger for making all
of the provider requests needed to fill the schema cache and therefore
would end up with provider request spans being reported beneath them.

As usual with these interface updates, this initial change focuses only
on changing the interface and updating its direct callers and implementers
to match, without any further refactoring or attempts to plumb contexts
to or from other functions that don't have them yet. That means there are
a few new context.TODO() calls here that we'll tidy up in a later commit
that hopefully won't involve all of the noise that is caused by changing
an interface API.

Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
2025-05-08 07:16:09 -07:00

115 lines
3.3 KiB
Go

// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package tofu
import (
"context"
"log"
"github.com/opentofu/opentofu/internal/configs"
"github.com/opentofu/opentofu/internal/dag"
"github.com/opentofu/opentofu/internal/states"
)
// OrphanResourceInstanceTransformer is a GraphTransformer that adds orphaned
// resource instances to the graph. An "orphan" is an instance that is present
// in the state but belongs to a resource that is no longer present in the
// configuration.
//
// This is not the transformer that deals with "count orphans" (instances that
// are no longer covered by a resource's "count" or "for_each" setting); that's
// handled instead by OrphanResourceCountTransformer.
type OrphanResourceInstanceTransformer struct {
Concrete ConcreteResourceInstanceNodeFunc
// State is the global state. We require the global state to
// properly find module orphans at our path.
State *states.State
// Config is the root node in the configuration tree. We'll look up
// the appropriate note in this tree using the path in each node.
Config *configs.Config
// Do not apply this transformer
skip bool
}
func (t *OrphanResourceInstanceTransformer) Transform(_ context.Context, g *Graph) error {
if t.skip {
return nil
}
if t.State == nil {
// If the entire state is nil, there can't be any orphans
return nil
}
if t.Config == nil {
// Should never happen: we can't be doing any OpenTofu operations
// without at least an empty configuration.
panic("OrphanResourceInstanceTransformer used without setting Config")
}
// Go through the modules and for each module transform in order
// to add the orphan.
for _, ms := range t.State.Modules {
if err := t.transform(g, ms); err != nil {
return err
}
}
return nil
}
func (t *OrphanResourceInstanceTransformer) transform(g *Graph, ms *states.Module) error {
if ms == nil {
return nil
}
moduleAddr := ms.Addr
// Get the configuration for this module. The configuration might be
// nil if the module was removed from the configuration. This is okay,
// this just means that every resource is an orphan.
var m *configs.Module
if c := t.Config.DescendentForInstance(moduleAddr); c != nil {
m = c.Module
}
// An "orphan" is a resource that is in the state but not the configuration,
// so we'll walk the state resources and try to correlate each of them
// with a configuration block. Each orphan gets a node in the graph whose
// type is decided by t.Concrete.
//
// We don't handle orphans related to changes in the "count" and "for_each"
// pseudo-arguments here. They are handled by OrphanResourceCountTransformer.
for _, rs := range ms.Resources {
if m != nil {
if r := m.ResourceByAddr(rs.Addr.Resource); r != nil {
continue
}
}
for key, inst := range rs.Instances {
// Instances which have no current objects (only one or more
// deposed objects) will be taken care of separately
if inst.Current == nil {
continue
}
addr := rs.Addr.Instance(key)
abstract := NewNodeAbstractResourceInstance(addr)
var node dag.Vertex = abstract
if f := t.Concrete; f != nil {
node = f(abstract)
}
log.Printf("[TRACE] OrphanResourceInstanceTransformer: adding single-instance orphan node for %s", addr)
g.Add(node)
}
}
return nil
}