mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-24 16:10:46 -06:00
terraform: apply builder adds outputs to graphs
This commit is contained in:
parent
ba51295267
commit
0d7674b079
@ -63,6 +63,9 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
|
||||
&MissingProvisionerTransformer{Provisioners: b.Provisioners},
|
||||
&ProvisionerTransformer{},
|
||||
|
||||
// Add the outputs
|
||||
&OutputTransformer{Module: b.Module},
|
||||
|
||||
// Connect references so ordering is correct
|
||||
&ReferenceTransformer{},
|
||||
|
||||
|
57
terraform/node_output.go
Normal file
57
terraform/node_output.go
Normal file
@ -0,0 +1,57 @@
|
||||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/config"
|
||||
)
|
||||
|
||||
// NodeApplyableOutput represents an output that is "applyable":
|
||||
// it is ready to be applied.
|
||||
type NodeApplyableOutput struct {
|
||||
PathValue []string
|
||||
Config *config.Output // Config is the output in the config
|
||||
}
|
||||
|
||||
func (n *NodeApplyableOutput) Name() string {
|
||||
result := fmt.Sprintf("output.%s", n.Config.Name)
|
||||
if len(n.PathValue) > 1 {
|
||||
result = fmt.Sprintf("%s.%s", modulePrefixStr(n.PathValue), result)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// GraphNodeSubPath
|
||||
func (n *NodeApplyableOutput) Path() []string {
|
||||
return n.PathValue
|
||||
}
|
||||
|
||||
// GraphNodeReferenceable
|
||||
func (n *NodeApplyableOutput) ReferenceableName() []string {
|
||||
return []string{n.Name()}
|
||||
}
|
||||
|
||||
// GraphNodeReferencer
|
||||
func (n *NodeApplyableOutput) References() []string {
|
||||
var result []string
|
||||
result = append(result, ReferencesFromConfig(n.Config.RawConfig)...)
|
||||
return result
|
||||
}
|
||||
|
||||
// GraphNodeEvalable
|
||||
func (n *NodeApplyableOutput) EvalTree() EvalNode {
|
||||
return &EvalOpFilter{
|
||||
Ops: []walkOperation{walkRefresh, walkPlan, walkApply,
|
||||
walkDestroy, walkInput, walkValidate},
|
||||
Node: &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
&EvalWriteOutput{
|
||||
Name: n.Config.Name,
|
||||
Sensitive: n.Config.Sensitive,
|
||||
Value: n.Config.RawConfig,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package terraform
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
)
|
||||
|
||||
@ -15,5 +17,58 @@ type OutputTransformer struct {
|
||||
}
|
||||
|
||||
func (t *OutputTransformer) Transform(g *Graph) error {
|
||||
return t.transform(g, t.Module)
|
||||
}
|
||||
|
||||
func (t *OutputTransformer) transform(g *Graph, m *module.Tree) error {
|
||||
// If no config, no outputs
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Transform all the children. We must do this first because
|
||||
// we can reference module outputs and they must show up in the
|
||||
// reference map.
|
||||
for _, c := range m.Children() {
|
||||
if err := t.transform(g, c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// If we have no outputs, we're done!
|
||||
os := m.Config().Outputs
|
||||
if len(os) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Build the reference map so we can determine if we're referencing things.
|
||||
refMap := NewReferenceMap(g.Vertices())
|
||||
|
||||
// Add all outputs here
|
||||
for _, o := range os {
|
||||
// Build the node
|
||||
node := &NodeApplyableOutput{
|
||||
PathValue: m.Path(),
|
||||
Config: o,
|
||||
}
|
||||
|
||||
// If the node references something, then we check to make sure
|
||||
// that the thing it references is in the graph. If it isn't, then
|
||||
// we don't add it because we may not be able to compute the output.
|
||||
//
|
||||
// If the node references nothing, we always include it since there
|
||||
// is no other clear time to compute it.
|
||||
matches, missing := refMap.References(node)
|
||||
if len(matches) == 0 || len(missing) > 0 {
|
||||
log.Printf(
|
||||
"[INFO] Not including %q in graph, matches: %v, missing: %s",
|
||||
node, matches, missing)
|
||||
continue
|
||||
}
|
||||
|
||||
// Add it!
|
||||
g.Add(node)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -30,9 +30,67 @@ type GraphNodeReferencer interface {
|
||||
type ReferenceTransformer struct{}
|
||||
|
||||
func (t *ReferenceTransformer) Transform(g *Graph) error {
|
||||
// Build the mapping of reference => vertex for efficient lookups.
|
||||
// Build a reference map so we can efficiently look up the references
|
||||
vs := g.Vertices()
|
||||
m := NewReferenceMap(vs)
|
||||
|
||||
// Find the things that reference things and connect them
|
||||
for _, v := range vs {
|
||||
parents, _ := m.References(v)
|
||||
for _, parent := range parents {
|
||||
g.Connect(dag.BasicEdge(v, parent))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReferenceMap is a structure that can be used to efficiently check
|
||||
// for references on a graph.
|
||||
type ReferenceMap struct {
|
||||
// m is the mapping of referenceable name to list of verticies that
|
||||
// implement that name. This is built on initialization.
|
||||
m map[string][]dag.Vertex
|
||||
}
|
||||
|
||||
// References returns the list of vertices that this vertex
|
||||
// references along with any missing references.
|
||||
func (m *ReferenceMap) References(v dag.Vertex) ([]dag.Vertex, []string) {
|
||||
rn, ok := v.(GraphNodeReferencer)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// If this node represents a sub path then we prefix
|
||||
var prefix string
|
||||
if pn, ok := v.(GraphNodeSubPath); ok {
|
||||
if path := normalizeModulePath(pn.Path()); len(path) > 1 {
|
||||
prefix = modulePrefixStr(path[1:]) + "."
|
||||
}
|
||||
}
|
||||
|
||||
var matches []dag.Vertex
|
||||
var missing []string
|
||||
for _, n := range rn.References() {
|
||||
n = prefix + n
|
||||
parents, ok := m.m[n]
|
||||
if !ok {
|
||||
missing = append(missing, n)
|
||||
continue
|
||||
}
|
||||
|
||||
matches = append(matches, parents...)
|
||||
}
|
||||
|
||||
return matches, missing
|
||||
}
|
||||
|
||||
// NewReferenceMap is used to create a new reference map for the
|
||||
// given set of vertices.
|
||||
func NewReferenceMap(vs []dag.Vertex) *ReferenceMap {
|
||||
// Build the lookup table
|
||||
refMap := make(map[string][]dag.Vertex)
|
||||
for _, v := range g.Vertices() {
|
||||
for _, v := range vs {
|
||||
// We're only looking for referenceable nodes
|
||||
rn, ok := v.(GraphNodeReferenceable)
|
||||
if !ok {
|
||||
@ -54,32 +112,7 @@ func (t *ReferenceTransformer) Transform(g *Graph) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Find the things that reference things and connect them
|
||||
for _, v := range g.Vertices() {
|
||||
rn, ok := v.(GraphNodeReferencer)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// If this node represents a sub path then we prefix
|
||||
var prefix string
|
||||
if pn, ok := v.(GraphNodeSubPath); ok {
|
||||
if path := normalizeModulePath(pn.Path()); len(path) > 1 {
|
||||
prefix = modulePrefixStr(path[1:]) + "."
|
||||
}
|
||||
}
|
||||
|
||||
for _, n := range rn.References() {
|
||||
n = prefix + n
|
||||
if parents, ok := refMap[n]; ok {
|
||||
for _, parent := range parents {
|
||||
g.Connect(dag.BasicEdge(v, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return &ReferenceMap{m: refMap}
|
||||
}
|
||||
|
||||
// ReferencesFromConfig returns the references that a configuration has
|
||||
|
Loading…
Reference in New Issue
Block a user