terraform: module inputs are passed through to subgraphs
This commit is contained in:
parent
5595229430
commit
23d097ee53
|
@ -118,6 +118,9 @@ func (c *Context2) Plan(opts *PlanOpts) (*Plan, error) {
|
||||||
|
|
||||||
// Do the walk
|
// Do the walk
|
||||||
walker, err := c.walk(operation)
|
walker, err := c.walk(operation)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
p.Diff = walker.Diff
|
p.Diff = walker.Diff
|
||||||
|
|
||||||
// Update the diff so that our context is up-to-date
|
// Update the diff so that our context is up-to-date
|
||||||
|
|
|
@ -109,12 +109,11 @@ func TestContext2Plan_modules(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
func TestContext2Plan_moduleInput(t *testing.T) {
|
||||||
func TestContextPlan_moduleInput(t *testing.T) {
|
|
||||||
m := testModule(t, "plan-module-input")
|
m := testModule(t, "plan-module-input")
|
||||||
p := testProvider("aws")
|
p := testProvider("aws")
|
||||||
p.DiffFn = testDiffFn
|
p.DiffFn = testDiffFn
|
||||||
ctx := testContext(t, &ContextOpts{
|
ctx := testContext2(t, &ContextOpts{
|
||||||
Module: m,
|
Module: m,
|
||||||
Providers: map[string]ResourceProviderFactory{
|
Providers: map[string]ResourceProviderFactory{
|
||||||
"aws": testProviderFuncFixed(p),
|
"aws": testProviderFuncFixed(p),
|
||||||
|
@ -133,6 +132,7 @@ func TestContextPlan_moduleInput(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
func TestContextPlan_moduleInputComputed(t *testing.T) {
|
func TestContextPlan_moduleInputComputed(t *testing.T) {
|
||||||
m := testModule(t, "plan-module-input-computed")
|
m := testModule(t, "plan-module-input-computed")
|
||||||
p := testProvider("aws")
|
p := testProvider("aws")
|
||||||
|
|
|
@ -49,6 +49,11 @@ type EvalContext interface {
|
||||||
// that is currently being acted upon.
|
// that is currently being acted upon.
|
||||||
Interpolate(*config.RawConfig, *Resource) (*ResourceConfig, error)
|
Interpolate(*config.RawConfig, *Resource) (*ResourceConfig, error)
|
||||||
|
|
||||||
|
// SetVariables sets the variables for interpolation. These variables
|
||||||
|
// should not have a "var." prefix. For example: "var.foo" should be
|
||||||
|
// "foo" as the key.
|
||||||
|
SetVariables(map[string]string)
|
||||||
|
|
||||||
// Diff returns the global diff as well as the lock that should
|
// Diff returns the global diff as well as the lock that should
|
||||||
// be used to modify that diff.
|
// be used to modify that diff.
|
||||||
Diff() (*Diff, *sync.RWMutex)
|
Diff() (*Diff, *sync.RWMutex)
|
||||||
|
@ -100,6 +105,9 @@ type MockEvalContext struct {
|
||||||
PathCalled bool
|
PathCalled bool
|
||||||
PathPath []string
|
PathPath []string
|
||||||
|
|
||||||
|
SetVariablesCalled bool
|
||||||
|
SetVariablesVariables map[string]string
|
||||||
|
|
||||||
DiffCalled bool
|
DiffCalled bool
|
||||||
DiffDiff *Diff
|
DiffDiff *Diff
|
||||||
DiffLock *sync.RWMutex
|
DiffLock *sync.RWMutex
|
||||||
|
@ -164,6 +172,11 @@ func (c *MockEvalContext) Path() []string {
|
||||||
return c.PathPath
|
return c.PathPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *MockEvalContext) SetVariables(vs map[string]string) {
|
||||||
|
c.SetVariablesCalled = true
|
||||||
|
c.SetVariablesVariables = vs
|
||||||
|
}
|
||||||
|
|
||||||
func (c *MockEvalContext) Diff() (*Diff, *sync.RWMutex) {
|
func (c *MockEvalContext) Diff() (*Diff, *sync.RWMutex) {
|
||||||
c.DiffCalled = true
|
c.DiffCalled = true
|
||||||
return c.DiffDiff, c.DiffLock
|
return c.DiffDiff, c.DiffLock
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -72,7 +70,7 @@ func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.ProviderCache[ctx.pathCacheKey(ctx.Path())] = p
|
ctx.ProviderCache[PathCacheKey(ctx.Path())] = p
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +80,7 @@ func (ctx *BuiltinEvalContext) Provider(n string) ResourceProvider {
|
||||||
ctx.ProviderLock.Lock()
|
ctx.ProviderLock.Lock()
|
||||||
defer ctx.ProviderLock.Unlock()
|
defer ctx.ProviderLock.Unlock()
|
||||||
|
|
||||||
return ctx.ProviderCache[ctx.pathCacheKey(ctx.Path())]
|
return ctx.ProviderCache[PathCacheKey(ctx.Path())]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *BuiltinEvalContext) ConfigureProvider(
|
func (ctx *BuiltinEvalContext) ConfigureProvider(
|
||||||
|
@ -94,7 +92,7 @@ func (ctx *BuiltinEvalContext) ConfigureProvider(
|
||||||
|
|
||||||
// Save the configuration
|
// Save the configuration
|
||||||
ctx.ProviderLock.Lock()
|
ctx.ProviderLock.Lock()
|
||||||
ctx.ProviderConfigCache[ctx.pathCacheKey(ctx.Path())] = cfg
|
ctx.ProviderConfigCache[PathCacheKey(ctx.Path())] = cfg
|
||||||
ctx.ProviderLock.Unlock()
|
ctx.ProviderLock.Unlock()
|
||||||
|
|
||||||
return p.Configure(cfg)
|
return p.Configure(cfg)
|
||||||
|
@ -106,7 +104,7 @@ func (ctx *BuiltinEvalContext) ParentProviderConfig(n string) *ResourceConfig {
|
||||||
|
|
||||||
path := ctx.Path()
|
path := ctx.Path()
|
||||||
for i := len(path) - 1; i >= 1; i-- {
|
for i := len(path) - 1; i >= 1; i-- {
|
||||||
k := ctx.pathCacheKey(path[:i])
|
k := PathCacheKey(path[:i])
|
||||||
if v, ok := ctx.ProviderConfigCache[k]; ok {
|
if v, ok := ctx.ProviderConfigCache[k]; ok {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
@ -139,7 +137,7 @@ func (ctx *BuiltinEvalContext) InitProvisioner(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.ProvisionerCache[ctx.pathCacheKey(ctx.Path())] = p
|
ctx.ProvisionerCache[PathCacheKey(ctx.Path())] = p
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +147,7 @@ func (ctx *BuiltinEvalContext) Provisioner(n string) ResourceProvisioner {
|
||||||
ctx.ProvisionerLock.Lock()
|
ctx.ProvisionerLock.Lock()
|
||||||
defer ctx.ProvisionerLock.Unlock()
|
defer ctx.ProvisionerLock.Unlock()
|
||||||
|
|
||||||
return ctx.ProvisionerCache[ctx.pathCacheKey(ctx.Path())]
|
return ctx.ProvisionerCache[PathCacheKey(ctx.Path())]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *BuiltinEvalContext) Interpolate(
|
func (ctx *BuiltinEvalContext) Interpolate(
|
||||||
|
@ -179,6 +177,12 @@ func (ctx *BuiltinEvalContext) Path() []string {
|
||||||
return ctx.PathValue
|
return ctx.PathValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ctx *BuiltinEvalContext) SetVariables(vs map[string]string) {
|
||||||
|
for k, v := range vs {
|
||||||
|
ctx.Interpolater.Variables[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (ctx *BuiltinEvalContext) Diff() (*Diff, *sync.RWMutex) {
|
func (ctx *BuiltinEvalContext) Diff() (*Diff, *sync.RWMutex) {
|
||||||
return ctx.DiffValue, ctx.DiffLock
|
return ctx.DiffValue, ctx.DiffLock
|
||||||
}
|
}
|
||||||
|
@ -194,23 +198,3 @@ func (ctx *BuiltinEvalContext) init() {
|
||||||
ctx.Providers = make(map[string]ResourceProviderFactory)
|
ctx.Providers = make(map[string]ResourceProviderFactory)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pathCacheKey returns a cache key for the current module path, unique to
|
|
||||||
// the module path.
|
|
||||||
//
|
|
||||||
// This is used because there is a variety of information that needs to be
|
|
||||||
// cached per-path, rather than per-context.
|
|
||||||
func (ctx *BuiltinEvalContext) pathCacheKey(path []string) string {
|
|
||||||
// There is probably a better way to do this, but this is working for now.
|
|
||||||
// We just create an MD5 hash of all the MD5 hashes of all the path
|
|
||||||
// elements. This gets us the property that it is unique per ordering.
|
|
||||||
hash := md5.New()
|
|
||||||
for _, p := range path {
|
|
||||||
single := md5.Sum([]byte(p))
|
|
||||||
if _, err := hash.Write(single[:]); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hex.EncodeToString(hash.Sum(nil))
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EvalSetVariables is an EvalNode implementation that sets the variables
|
||||||
|
// explicitly for interpolation later.
|
||||||
|
type EvalSetVariables struct {
|
||||||
|
Variables map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *EvalSetVariables) Args() ([]EvalNode, []EvalType) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test
|
||||||
|
func (n *EvalSetVariables) Eval(
|
||||||
|
ctx EvalContext, args []interface{}) (interface{}, error) {
|
||||||
|
ctx.SetVariables(n.Variables)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *EvalSetVariables) Type() EvalType {
|
||||||
|
return EvalTypeNull
|
||||||
|
}
|
||||||
|
|
||||||
|
// EvalVariableBlock is an EvalNode implementation that evaluates the
|
||||||
|
// given configuration, and uses the final values as a way to set the
|
||||||
|
// mapping.
|
||||||
|
type EvalVariableBlock struct {
|
||||||
|
Config EvalNode
|
||||||
|
Variables map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *EvalVariableBlock) Args() ([]EvalNode, []EvalType) {
|
||||||
|
return []EvalNode{n.Config}, []EvalType{EvalTypeConfig}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test
|
||||||
|
func (n *EvalVariableBlock) Eval(
|
||||||
|
ctx EvalContext, args []interface{}) (interface{}, error) {
|
||||||
|
// Clear out the existing mapping
|
||||||
|
for k, _ := range n.Variables {
|
||||||
|
delete(n.Variables, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get our configuration
|
||||||
|
rc := args[0].(*ResourceConfig)
|
||||||
|
for k, v := range rc.Config {
|
||||||
|
n.Variables[k] = v.(string)
|
||||||
|
}
|
||||||
|
for k, _ := range rc.Raw {
|
||||||
|
if _, ok := n.Variables[k]; !ok {
|
||||||
|
n.Variables[k] = config.UnknownVariableValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *EvalVariableBlock) Type() EvalType {
|
||||||
|
return EvalTypeNull
|
||||||
|
}
|
|
@ -48,8 +48,26 @@ func (n *GraphNodeConfigModule) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeExpandable
|
// GraphNodeExpandable
|
||||||
func (n *GraphNodeConfigModule) Expand(b GraphBuilder) (*Graph, error) {
|
func (n *GraphNodeConfigModule) Expand(b GraphBuilder) (GraphNodeSubgraph, error) {
|
||||||
return b.Build(n.Path)
|
// Build the graph first
|
||||||
|
graph, err := b.Build(n.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the parameters node to the module
|
||||||
|
t := &ModuleInputTransformer{Variables: make(map[string]string)}
|
||||||
|
if err := t.Transform(graph); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the actual subgraph node
|
||||||
|
return &graphNodeModuleExpanded{
|
||||||
|
Original: n,
|
||||||
|
Graph: graph,
|
||||||
|
InputConfig: n.Module.RawConfig,
|
||||||
|
Variables: t.Variables,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeExpandable
|
// GraphNodeExpandable
|
||||||
|
@ -181,3 +199,34 @@ func (n *GraphNodeConfigResource) ProvisionedBy() []string {
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// graphNodeModuleExpanded represents a module where the graph has
|
||||||
|
// been expanded. It stores the graph of the module as well as a reference
|
||||||
|
// to the map of variables.
|
||||||
|
type graphNodeModuleExpanded struct {
|
||||||
|
Original dag.Vertex
|
||||||
|
Graph *Graph
|
||||||
|
InputConfig *config.RawConfig
|
||||||
|
|
||||||
|
// Variables is a map of the input variables. This reference should
|
||||||
|
// be shared with ModuleInputTransformer in order to create a connection
|
||||||
|
// where the variables are set properly.
|
||||||
|
Variables map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *graphNodeModuleExpanded) Name() string {
|
||||||
|
return fmt.Sprintf("%s (expanded)", dag.VertexName(n.Original))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GraphNodeEvalable impl.
|
||||||
|
func (n *graphNodeModuleExpanded) EvalTree() EvalNode {
|
||||||
|
return &EvalVariableBlock{
|
||||||
|
Config: &EvalInterpolate{Config: n.InputConfig},
|
||||||
|
Variables: n.Variables,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GraphNodeSubgraph impl.
|
||||||
|
func (n *graphNodeModuleExpanded) Subgraph() *Graph {
|
||||||
|
return n.Graph
|
||||||
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ func TestGraphNodeConfigModuleExpand(t *testing.T) {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
actual := strings.TrimSpace(g.String())
|
actual := strings.TrimSpace(g.Subgraph().String())
|
||||||
expected := strings.TrimSpace(testGraphNodeModuleExpandStr)
|
expected := strings.TrimSpace(testGraphNodeModuleExpandStr)
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
t.Fatalf("bad:\n\n%s", actual)
|
t.Fatalf("bad:\n\n%s", actual)
|
||||||
|
|
|
@ -26,6 +26,8 @@ type ContextGraphWalker struct {
|
||||||
errorLock sync.Mutex
|
errorLock sync.Mutex
|
||||||
once sync.Once
|
once sync.Once
|
||||||
diffLock sync.RWMutex
|
diffLock sync.RWMutex
|
||||||
|
contexts map[string]*BuiltinEvalContext
|
||||||
|
contextLock sync.Mutex
|
||||||
providerCache map[string]ResourceProvider
|
providerCache map[string]ResourceProvider
|
||||||
providerConfigCache map[string]*ResourceConfig
|
providerConfigCache map[string]*ResourceConfig
|
||||||
providerLock sync.Mutex
|
providerLock sync.Mutex
|
||||||
|
@ -36,7 +38,25 @@ type ContextGraphWalker struct {
|
||||||
func (w *ContextGraphWalker) EnterGraph(g *Graph) EvalContext {
|
func (w *ContextGraphWalker) EnterGraph(g *Graph) EvalContext {
|
||||||
w.once.Do(w.init)
|
w.once.Do(w.init)
|
||||||
|
|
||||||
return &BuiltinEvalContext{
|
w.contextLock.Lock()
|
||||||
|
defer w.contextLock.Unlock()
|
||||||
|
|
||||||
|
// If we already have a context for this path cached, use that
|
||||||
|
key := PathCacheKey(g.Path)
|
||||||
|
if ctx, ok := w.contexts[key]; ok {
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variables should be our context variables, but these only apply
|
||||||
|
// to the root module. As we enter subgraphs, we don't want to set
|
||||||
|
// variables, which is set by the SetVariables EvalContext function.
|
||||||
|
variables := w.Context.variables
|
||||||
|
if len(g.Path) > 1 {
|
||||||
|
// We're in a submodule, the variables should be empty
|
||||||
|
variables = make(map[string]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := &BuiltinEvalContext{
|
||||||
PathValue: g.Path,
|
PathValue: g.Path,
|
||||||
Hooks: w.Context.hooks,
|
Hooks: w.Context.hooks,
|
||||||
Providers: w.Context.providers,
|
Providers: w.Context.providers,
|
||||||
|
@ -55,9 +75,12 @@ func (w *ContextGraphWalker) EnterGraph(g *Graph) EvalContext {
|
||||||
Module: w.Context.module,
|
Module: w.Context.module,
|
||||||
State: w.Context.state,
|
State: w.Context.state,
|
||||||
StateLock: &w.Context.stateLock,
|
StateLock: &w.Context.stateLock,
|
||||||
Variables: w.Context.variables,
|
Variables: variables,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w.contexts[key] = ctx
|
||||||
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *ContextGraphWalker) EnterEvalTree(v dag.Vertex, n EvalNode) EvalNode {
|
func (w *ContextGraphWalker) EnterEvalTree(v dag.Vertex, n EvalNode) EvalNode {
|
||||||
|
@ -94,6 +117,7 @@ func (w *ContextGraphWalker) init() {
|
||||||
w.Diff = new(Diff)
|
w.Diff = new(Diff)
|
||||||
w.Diff.init()
|
w.Diff.init()
|
||||||
|
|
||||||
|
w.contexts = make(map[string]*BuiltinEvalContext, 5)
|
||||||
w.providerCache = make(map[string]ResourceProvider, 5)
|
w.providerCache = make(map[string]ResourceProvider, 5)
|
||||||
w.providerConfigCache = make(map[string]*ResourceConfig, 5)
|
w.providerConfigCache = make(map[string]*ResourceConfig, 5)
|
||||||
w.provisionerCache = make(map[string]ResourceProvisioner, 5)
|
w.provisionerCache = make(map[string]ResourceProvisioner, 5)
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PathCacheKey returns a cache key for a module path.
|
||||||
|
//
|
||||||
|
// TODO: test
|
||||||
|
func PathCacheKey(path []string) string {
|
||||||
|
// There is probably a better way to do this, but this is working for now.
|
||||||
|
// We just create an MD5 hash of all the MD5 hashes of all the path
|
||||||
|
// elements. This gets us the property that it is unique per ordering.
|
||||||
|
hash := md5.New()
|
||||||
|
for _, p := range path {
|
||||||
|
single := md5.Sum([]byte(p))
|
||||||
|
if _, err := hash.Write(single[:]); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hex.EncodeToString(hash.Sum(nil))
|
||||||
|
}
|
|
@ -1,8 +1,6 @@
|
||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/dag"
|
"github.com/hashicorp/terraform/dag"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -10,7 +8,7 @@ import (
|
||||||
// signal that they can be expanded. Expanded nodes turn into
|
// signal that they can be expanded. Expanded nodes turn into
|
||||||
// GraphNodeSubgraph nodes within the graph.
|
// GraphNodeSubgraph nodes within the graph.
|
||||||
type GraphNodeExpandable interface {
|
type GraphNodeExpandable interface {
|
||||||
Expand(GraphBuilder) (*Graph, error)
|
Expand(GraphBuilder) (GraphNodeSubgraph, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeDynamicExpandable is an interface that nodes can implement
|
// GraphNodeDynamicExpandable is an interface that nodes can implement
|
||||||
|
@ -43,27 +41,5 @@ func (t *ExpandTransform) Transform(v dag.Vertex) (dag.Vertex, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expand the subgraph!
|
// Expand the subgraph!
|
||||||
g, err := ev.Expand(t.Builder)
|
return ev.Expand(t.Builder)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace with our special node
|
|
||||||
return &graphNodeExpanded{
|
|
||||||
Graph: g,
|
|
||||||
OriginalName: dag.VertexName(v),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type graphNodeExpanded struct {
|
|
||||||
Graph *Graph
|
|
||||||
OriginalName string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *graphNodeExpanded) Name() string {
|
|
||||||
return fmt.Sprintf("%s (expanded)", n.OriginalName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *graphNodeExpanded) Subgraph() *Graph {
|
|
||||||
return n.Graph
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/dag"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ModuleInputTransformer is a GraphTransformer that adds a node to the
|
||||||
|
// graph for setting the module input variables for the remainder of the
|
||||||
|
// graph.
|
||||||
|
type ModuleInputTransformer struct {
|
||||||
|
Variables map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *ModuleInputTransformer) Transform(g *Graph) error {
|
||||||
|
// Create the node
|
||||||
|
n := &graphNodeModuleInput{Variables: t.Variables}
|
||||||
|
|
||||||
|
// Add it to the graph
|
||||||
|
g.Add(n)
|
||||||
|
|
||||||
|
// Connect the inputs to the bottom of the graph so that it happens
|
||||||
|
// first.
|
||||||
|
for _, v := range g.Vertices() {
|
||||||
|
if v == n {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if g.DownEdges(v).Len() == 0 {
|
||||||
|
g.Connect(dag.BasicEdge(v, n))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type graphNodeModuleInput struct {
|
||||||
|
Variables map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *graphNodeModuleInput) Name() string {
|
||||||
|
return "module inputs"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GraphNodeEvalable impl.
|
||||||
|
func (n *graphNodeModuleInput) EvalTree() EvalNode {
|
||||||
|
return &EvalSetVariables{Variables: n.Variables}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/dag"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestModuleInputTransformer(t *testing.T) {
|
||||||
|
var g Graph
|
||||||
|
g.Add(1)
|
||||||
|
g.Add(2)
|
||||||
|
g.Add(3)
|
||||||
|
g.Connect(dag.BasicEdge(1, 2))
|
||||||
|
g.Connect(dag.BasicEdge(1, 3))
|
||||||
|
|
||||||
|
{
|
||||||
|
tf := &ModuleInputTransformer{}
|
||||||
|
if err := tf.Transform(&g); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := strings.TrimSpace(g.String())
|
||||||
|
expected := strings.TrimSpace(testModuleInputTransformStr)
|
||||||
|
if actual != expected {
|
||||||
|
t.Fatalf("bad:\n\n%s", actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const testModuleInputTransformStr = `
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
2
|
||||||
|
module inputs
|
||||||
|
3
|
||||||
|
module inputs
|
||||||
|
module inputs
|
||||||
|
`
|
Loading…
Reference in New Issue