mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-27 09:21:14 -06:00
0df5a7e6cf
Before expansion happens, we only have expansion resource nodes that know their ConfigResource address. In order to properly compare these to targets within a module instance, we need to generalize the target to also be a ConfigResource. We can also remove the IgnoreIndices field from the transformer, since we have addresses that are properly scoped and can compare them in the correct context.
158 lines
4.2 KiB
Go
158 lines
4.2 KiB
Go
package terraform
|
|
|
|
import (
|
|
"log"
|
|
|
|
"github.com/hashicorp/terraform/addrs"
|
|
"github.com/hashicorp/terraform/dag"
|
|
)
|
|
|
|
// GraphNodeTargetable is an interface for graph nodes to implement when they
|
|
// need to be told about incoming targets. This is useful for nodes that need
|
|
// to respect targets as they dynamically expand. Note that the list of targets
|
|
// provided will contain every target provided, and each implementing graph
|
|
// node must filter this list to targets considered relevant.
|
|
type GraphNodeTargetable interface {
|
|
SetTargets([]addrs.Targetable)
|
|
}
|
|
|
|
// 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 []addrs.Targetable
|
|
}
|
|
|
|
func (t *TargetsTransformer) Transform(g *Graph) error {
|
|
if len(t.Targets) > 0 {
|
|
targetedNodes, err := t.selectTargetedNodes(g, t.Targets)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, v := range g.Vertices() {
|
|
if !targetedNodes.Include(v) {
|
|
log.Printf("[DEBUG] Removing %q, filtered by targeting.", dag.VertexName(v))
|
|
g.Remove(v)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Returns a set of targeted nodes. A targeted node is either addressed
|
|
// directly, address indirectly via its container, or it's a dependency of a
|
|
// targeted node.
|
|
func (t *TargetsTransformer) selectTargetedNodes(g *Graph, addrs []addrs.Targetable) (dag.Set, error) {
|
|
targetedNodes := make(dag.Set)
|
|
|
|
vertices := g.Vertices()
|
|
|
|
for _, v := range 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)
|
|
}
|
|
|
|
deps, _ := g.Ancestors(v)
|
|
for _, d := range deps {
|
|
targetedNodes.Add(d)
|
|
}
|
|
}
|
|
}
|
|
|
|
// It is expected that outputs which are only derived from targeted
|
|
// resources are also updated. While we don't include any other possible
|
|
// side effects from the targeted nodes, these are added because outputs
|
|
// cannot be targeted on their own.
|
|
// Start by finding the root module output nodes themselves
|
|
for _, v := range vertices {
|
|
// outputs are all temporary value types
|
|
tv, ok := v.(graphNodeTemporaryValue)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
// root module outputs indicate that while they are an output type,
|
|
// they not temporary and will return false here.
|
|
if tv.temporaryValue() {
|
|
continue
|
|
}
|
|
|
|
// If this output is descended only from targeted resources, then we
|
|
// will keep it
|
|
deps, _ := g.Ancestors(v)
|
|
found := 0
|
|
for _, d := range deps {
|
|
switch d.(type) {
|
|
case GraphNodeResourceInstance:
|
|
case GraphNodeConfigResource:
|
|
default:
|
|
continue
|
|
}
|
|
|
|
if !targetedNodes.Include(d) {
|
|
// this dependency isn't being targeted, so we can't process this
|
|
// output
|
|
found = 0
|
|
break
|
|
}
|
|
|
|
found++
|
|
}
|
|
|
|
if found > 0 {
|
|
// we found an output we can keep; add it, and all it's dependencies
|
|
targetedNodes.Add(v)
|
|
for _, d := range deps {
|
|
targetedNodes.Add(d)
|
|
}
|
|
}
|
|
}
|
|
|
|
return targetedNodes, nil
|
|
}
|
|
|
|
func (t *TargetsTransformer) nodeIsTarget(v dag.Vertex, targets []addrs.Targetable) bool {
|
|
var vertexAddr addrs.Targetable
|
|
switch r := v.(type) {
|
|
case GraphNodeResourceInstance:
|
|
vertexAddr = r.ResourceInstanceAddr()
|
|
case GraphNodeConfigResource:
|
|
vertexAddr = r.ResourceAddr()
|
|
|
|
default:
|
|
// Only resource and resource instance nodes can be targeted.
|
|
return false
|
|
}
|
|
|
|
for _, targetAddr := range targets {
|
|
switch vertexAddr.(type) {
|
|
case addrs.ConfigResource:
|
|
// Before expansion happens, we only have nodes that know their
|
|
// ConfigResource address. We need to take the more specific
|
|
// target addresses and generalize them in order to compare with a
|
|
// ConfigResource.
|
|
switch target := targetAddr.(type) {
|
|
case addrs.AbsResourceInstance:
|
|
targetAddr = target.ContainingResource().Config()
|
|
case addrs.AbsResource:
|
|
targetAddr = target.Config()
|
|
}
|
|
}
|
|
|
|
if targetAddr.TargetContains(vertexAddr) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|