mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-29 10:21:01 -06:00
36d0a50427
This is part of a general effort to move all of Terraform's non-library package surface under internal in order to reinforce that these are for internal use within Terraform only. If you were previously importing packages under this prefix into an external codebase, you could pin to an earlier release tag as an interim solution until you've make a plan to achieve the same functionality some other way.
160 lines
4.2 KiB
Go
160 lines
4.2 KiB
Go
package terraform
|
|
|
|
import (
|
|
"log"
|
|
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
"github.com/hashicorp/terraform/internal/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()
|
|
case addrs.ModuleInstance:
|
|
targetAddr = target.Module()
|
|
}
|
|
}
|
|
|
|
if targetAddr.TargetContains(vertexAddr) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|