mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-30 10:47:14 -06:00
a0d3245ee3
Instead of trying to skip non-targeted orphans as they are added to the graph in OrphanTransformer, remove knowledge of targeting from OrphanTransformer and instead make the orphan resource nodes properly addressable. That allows us to use existing logic in TargetTransformer to filter out the nodes appropriately. This does require adding TargetTransformer to the list of transforms that run during DynamicExpand so that targeting can be applied to nodes with expanded counts. Fixes #4515 Fixes #2538 Fixes #4462
113 lines
2.7 KiB
Go
113 lines
2.7 KiB
Go
package terraform
|
|
|
|
import (
|
|
"log"
|
|
|
|
"github.com/hashicorp/terraform/dag"
|
|
)
|
|
|
|
// TargetsTransformer is a GraphTransformer that, when the user specifies a
|
|
// list of resources to target, limits the graph to only those resources and
|
|
// their dependencies.
|
|
type TargetsTransformer struct {
|
|
// List of targeted resource names specified by the user
|
|
Targets []string
|
|
|
|
// List of parsed targets, provided by callers like ResourceCountTransform
|
|
// that already have the targets parsed
|
|
ParsedTargets []ResourceAddress
|
|
|
|
// Set to true when we're in a `terraform destroy` or a
|
|
// `terraform plan -destroy`
|
|
Destroy bool
|
|
}
|
|
|
|
func (t *TargetsTransformer) Transform(g *Graph) error {
|
|
if len(t.Targets) > 0 && len(t.ParsedTargets) == 0 {
|
|
addrs, err := t.parseTargetAddresses()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
t.ParsedTargets = addrs
|
|
}
|
|
if len(t.ParsedTargets) > 0 {
|
|
targetedNodes, err := t.selectTargetedNodes(g, t.ParsedTargets)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, v := range g.Vertices() {
|
|
if _, ok := v.(GraphNodeAddressable); ok {
|
|
if !targetedNodes.Include(v) {
|
|
log.Printf("[DEBUG] Removing %q, filtered by targeting.", dag.VertexName(v))
|
|
g.Remove(v)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (t *TargetsTransformer) parseTargetAddresses() ([]ResourceAddress, error) {
|
|
addrs := make([]ResourceAddress, len(t.Targets))
|
|
for i, target := range t.Targets {
|
|
ta, err := ParseResourceAddress(target)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
addrs[i] = *ta
|
|
}
|
|
return addrs, nil
|
|
}
|
|
|
|
// Returns the list of targeted nodes. A targeted node is either addressed
|
|
// directly, or is an Ancestor of a targeted node. Destroy mode keeps
|
|
// Descendents instead of Ancestors.
|
|
func (t *TargetsTransformer) selectTargetedNodes(
|
|
g *Graph, addrs []ResourceAddress) (*dag.Set, error) {
|
|
targetedNodes := new(dag.Set)
|
|
for _, v := range g.Vertices() {
|
|
if t.nodeIsTarget(v, addrs) {
|
|
targetedNodes.Add(v)
|
|
|
|
// We inform nodes that ask about the list of targets - helps for nodes
|
|
// that need to dynamically expand. Note that this only occurs for nodes
|
|
// that are already directly targeted.
|
|
if tn, ok := v.(GraphNodeTargetable); ok {
|
|
tn.SetTargets(addrs)
|
|
}
|
|
|
|
var deps *dag.Set
|
|
var err error
|
|
if t.Destroy {
|
|
deps, err = g.Descendents(v)
|
|
} else {
|
|
deps, err = g.Ancestors(v)
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, d := range deps.List() {
|
|
targetedNodes.Add(d)
|
|
}
|
|
}
|
|
}
|
|
return targetedNodes, nil
|
|
}
|
|
|
|
func (t *TargetsTransformer) nodeIsTarget(
|
|
v dag.Vertex, addrs []ResourceAddress) bool {
|
|
r, ok := v.(GraphNodeAddressable)
|
|
if !ok {
|
|
return false
|
|
}
|
|
addr := r.ResourceAddress()
|
|
for _, targetAddr := range addrs {
|
|
if targetAddr.Equals(addr) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|