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},
|
&MissingProvisionerTransformer{Provisioners: b.Provisioners},
|
||||||
&ProvisionerTransformer{},
|
&ProvisionerTransformer{},
|
||||||
|
|
||||||
|
// Add the outputs
|
||||||
|
&OutputTransformer{Module: b.Module},
|
||||||
|
|
||||||
// Connect references so ordering is correct
|
// Connect references so ordering is correct
|
||||||
&ReferenceTransformer{},
|
&ReferenceTransformer{},
|
||||||
|
|
||||||
|
|
|
@ -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
|
package terraform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config/module"
|
"github.com/hashicorp/terraform/config/module"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,5 +17,58 @@ type OutputTransformer struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *OutputTransformer) Transform(g *Graph) error {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,9 +30,67 @@ type GraphNodeReferencer interface {
|
||||||
type ReferenceTransformer struct{}
|
type ReferenceTransformer struct{}
|
||||||
|
|
||||||
func (t *ReferenceTransformer) Transform(g *Graph) error {
|
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)
|
refMap := make(map[string][]dag.Vertex)
|
||||||
for _, v := range g.Vertices() {
|
for _, v := range vs {
|
||||||
// We're only looking for referenceable nodes
|
// We're only looking for referenceable nodes
|
||||||
rn, ok := v.(GraphNodeReferenceable)
|
rn, ok := v.(GraphNodeReferenceable)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -54,32 +112,7 @@ func (t *ReferenceTransformer) Transform(g *Graph) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the things that reference things and connect them
|
return &ReferenceMap{m: refMap}
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReferencesFromConfig returns the references that a configuration has
|
// ReferencesFromConfig returns the references that a configuration has
|
||||||
|
|
Loading…
Reference in New Issue