Skip to content

Commit

Permalink
Merge pull request hashicorp#24331 from hashicorp/jbardin/module-expa…
Browse files Browse the repository at this point in the history
…nsion-in-part

More module expansion groundwork
  • Loading branch information
jbardin authored Mar 11, 2020
2 parents 1ec76b9 + 41f28f5 commit 7d9fde1
Show file tree
Hide file tree
Showing 36 changed files with 324 additions and 347 deletions.
18 changes: 18 additions & 0 deletions addrs/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ func (m Module) String() string {
return strings.Join(steps, ".")
}

func (m Module) Equal(other Module) bool {
return m.String() == other.String()
}

// Child returns the address of a child call in the receiver, identified by the
// given name.
func (m Module) Child(name string) Module {
Expand Down Expand Up @@ -77,3 +81,17 @@ func (m Module) Call() (Module, ModuleCall) {
Name: callName,
}
}

// Ancestors returns a slice containing the receiver and all of its ancestor
// modules, all the way up to (and including) the root module. The result is
// ordered by depth, with the root module always first.
//
// Since the result always includes the root module, a caller may choose to
// ignore it by slicing the result with [1:].
func (m Module) Ancestors() []Module {
ret := make([]Module, 0, len(m)+1)
for i := 0; i <= len(m); i++ {
ret = append(ret, m[:i])
}
return ret
}
17 changes: 2 additions & 15 deletions terraform/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ func (g *Graph) walk(walker GraphWalker) tfdiags.Diagnostics {

// vertexCtx is the context that we use when evaluating. This
// is normally the context of our graph but can be overridden
// with a GraphNodeSubPath impl.
// with a GraphNodeModuleInstance impl.
vertexCtx := ctx
if pn, ok := v.(GraphNodeSubPath); ok && len(pn.Path()) > 0 {
if pn, ok := v.(GraphNodeModuleInstance); ok && len(pn.Path()) > 0 {
vertexCtx = walker.EnterPath(pn.Path())
defer walker.ExitPath(pn.Path())
}
Expand Down Expand Up @@ -101,19 +101,6 @@ func (g *Graph) walk(walker GraphWalker) tfdiags.Diagnostics {
log.Printf("[TRACE] vertex %q: produced no dynamic subgraph", dag.VertexName(v))
}
}

// If the node has a subgraph, then walk the subgraph
if sn, ok := v.(GraphNodeSubgraph); ok {
log.Printf("[TRACE] vertex %q: entering static subgraph", dag.VertexName(v))

subDiags := sn.Subgraph().(*Graph).walk(walker)
if subDiags.HasErrors() {
log.Printf("[TRACE] vertex %q: static subgraph encountered errors", dag.VertexName(v))
return
}
log.Printf("[TRACE] vertex %q: static subgraph completed successfully", dag.VertexName(v))
}

return
}

Expand Down
2 changes: 1 addition & 1 deletion terraform/graph_builder_apply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ func TestApplyGraphBuilder_doubleCBD(t *testing.T) {
continue
}

switch tv.Addr.Resource.Name {
switch tv.Addr.Name {
case "A":
destroyA = fmt.Sprintf("test_object.A (destroy deposed %s)", tv.DeposedKey)
case "B":
Expand Down
10 changes: 8 additions & 2 deletions terraform/graph_interface_subgraph.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@ import (
"github.com/hashicorp/terraform/addrs"
)

// GraphNodeSubPath says that a node is part of a graph with a
// GraphNodeModuleInstance says that a node is part of a graph with a
// different path, and the context should be adjusted accordingly.
type GraphNodeSubPath interface {
type GraphNodeModuleInstance interface {
Path() addrs.ModuleInstance
}

// GraphNodeModulePath is implemented by all referenceable nodes, to indicate
// their configuration path in unexpanded modules.
type GraphNodeModulePath interface {
ModulePath() addrs.Module
}
2 changes: 1 addition & 1 deletion terraform/node_data_refresh.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type NodeRefreshableDataResource struct {
}

var (
_ GraphNodeSubPath = (*NodeRefreshableDataResource)(nil)
_ GraphNodeModuleInstance = (*NodeRefreshableDataResource)(nil)
_ GraphNodeDynamicExpandable = (*NodeRefreshableDataResource)(nil)
_ GraphNodeReferenceable = (*NodeRefreshableDataResource)(nil)
_ GraphNodeReferencer = (*NodeRefreshableDataResource)(nil)
Expand Down
22 changes: 12 additions & 10 deletions terraform/node_data_refresh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,12 @@ func TestNodeRefreshableDataResourceDynamicExpand_scaleOut(t *testing.T) {

n := &NodeRefreshableDataResource{
NodeAbstractResource: &NodeAbstractResource{
Addr: addrs.RootModuleInstance.Resource(
addrs.DataResourceMode,
"aws_instance",
"foo",
),
Addr: addrs.Resource{
Mode: addrs.DataResourceMode,
Type: "aws_instance",
Name: "foo",
},
Module: addrs.RootModule,
Config: m.Module.DataResources["data.aws_instance.foo"],
},
}
Expand Down Expand Up @@ -124,11 +125,12 @@ func TestNodeRefreshableDataResourceDynamicExpand_scaleIn(t *testing.T) {

n := &NodeRefreshableDataResource{
NodeAbstractResource: &NodeAbstractResource{
Addr: addrs.RootModuleInstance.Resource(
addrs.DataResourceMode,
"aws_instance",
"foo",
),
Addr: addrs.Resource{
Mode: addrs.DataResourceMode,
Type: "aws_instance",
Name: "foo",
},
Module: addrs.RootModule,
Config: m.Module.DataResources["data.aws_instance.foo"],
ResolvedProvider: addrs.AbsProviderConfig{
Provider: addrs.NewLegacyProvider("aws"),
Expand Down
80 changes: 73 additions & 7 deletions terraform/node_local.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,73 @@
package terraform

import (
"log"

"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/dag"
"github.com/hashicorp/terraform/lang"
)

// NodePlannableLocal represents a named local value in a configuration module,
// which has not yet been expanded.
type NodePlannableLocal struct {
Addr addrs.LocalValue
Module addrs.Module
Config *configs.Local
}

var (
_ RemovableIfNotTargeted = (*NodePlannableLocal)(nil)
_ GraphNodeReferenceable = (*NodePlannableLocal)(nil)
_ GraphNodeReferencer = (*NodePlannableLocal)(nil)
_ GraphNodeDynamicExpandable = (*NodePlannableLocal)(nil)
)

func (n *NodePlannableLocal) Name() string {
path := n.Module.String()
addr := n.Addr.String()
if path != "" {
return path + "." + addr
}
return addr
}

// GraphNodeModulePath
func (n *NodePlannableLocal) ModulePath() addrs.Module {
return n.Module
}

// RemovableIfNotTargeted
func (n *NodePlannableLocal) RemoveIfNotTargeted() bool {
return true
}

// GraphNodeReferenceable
func (n *NodePlannableLocal) ReferenceableAddrs() []addrs.Referenceable {
return []addrs.Referenceable{n.Addr}
}

// GraphNodeReferencer
func (n *NodePlannableLocal) References() []*addrs.Reference {
refs, _ := lang.ReferencesInExpr(n.Config.Expr)
return appendResourceDestroyReferences(refs)
}

func (n *NodePlannableLocal) DynamicExpand(ctx EvalContext) (*Graph, error) {
var g Graph
expander := ctx.InstanceExpander()
for _, module := range expander.ExpandModule(n.Module) {
o := &NodeLocal{
Addr: n.Addr.Absolute(module),
Config: n.Config,
}
log.Printf("[TRACE] Expanding local: adding %s as %T", o.Addr.String(), o)
g.Add(o)
}
return &g, nil
}

// NodeLocal represents a named local value in a particular module.
//
// Local value nodes only have one operation, common to all walk types:
Expand All @@ -17,23 +78,28 @@ type NodeLocal struct {
}

var (
_ GraphNodeSubPath = (*NodeLocal)(nil)
_ RemovableIfNotTargeted = (*NodeLocal)(nil)
_ GraphNodeReferenceable = (*NodeLocal)(nil)
_ GraphNodeReferencer = (*NodeLocal)(nil)
_ GraphNodeEvalable = (*NodeLocal)(nil)
_ dag.GraphNodeDotter = (*NodeLocal)(nil)
_ GraphNodeModuleInstance = (*NodeLocal)(nil)
_ RemovableIfNotTargeted = (*NodeLocal)(nil)
_ GraphNodeReferenceable = (*NodeLocal)(nil)
_ GraphNodeReferencer = (*NodeLocal)(nil)
_ GraphNodeEvalable = (*NodeLocal)(nil)
_ dag.GraphNodeDotter = (*NodeLocal)(nil)
)

func (n *NodeLocal) Name() string {
return n.Addr.String()
}

// GraphNodeSubPath
// GraphNodeModuleInstance
func (n *NodeLocal) Path() addrs.ModuleInstance {
return n.Addr.Module
}

// GraphNodeModulePath
func (n *NodeLocal) ModulePath() addrs.Module {
return n.Addr.Module.Module()
}

// RemovableIfNotTargeted
func (n *NodeLocal) RemoveIfNotTargeted() bool {
return true
Expand Down
45 changes: 19 additions & 26 deletions terraform/node_module_expand.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package terraform

import (
"log"

"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/lang"
Expand All @@ -13,30 +11,27 @@ import (
// might expand into multiple module instances depending on how it is
// configured.
type nodeExpandModule struct {
CallerAddr addrs.ModuleInstance
Addr addrs.Module
Call addrs.ModuleCall
Config *configs.Module
ModuleCall *configs.ModuleCall
}

var (
_ GraphNodeSubPath = (*nodeExpandModule)(nil)
_ RemovableIfNotTargeted = (*nodeExpandModule)(nil)
_ GraphNodeEvalable = (*nodeExpandModule)(nil)
_ GraphNodeReferencer = (*nodeExpandModule)(nil)
)

func (n *nodeExpandModule) Name() string {
return n.CallerAddr.Child(n.Call.Name, addrs.NoKey).String()
return n.Addr.String()
}

// GraphNodeSubPath implementation
func (n *nodeExpandModule) Path() addrs.ModuleInstance {
// GraphNodeModulePath implementation
func (n *nodeExpandModule) ModulePath() addrs.Module {
// This node represents the module call within a module,
// so return the CallerAddr as the path as the module
// call may expand into multiple child instances
return n.CallerAddr
return n.Addr.Parent()
}

// GraphNodeReferencer implementation
Expand Down Expand Up @@ -77,8 +72,7 @@ func (n *nodeExpandModule) RemoveIfNotTargeted() bool {
// GraphNodeEvalable
func (n *nodeExpandModule) EvalTree() EvalNode {
return &evalPrepareModuleExpansion{
CallerAddr: n.CallerAddr,
Call: n.Call,
Addr: n.Addr,
Config: n.Config,
ModuleCall: n.ModuleCall,
}
Expand All @@ -87,8 +81,7 @@ func (n *nodeExpandModule) EvalTree() EvalNode {
// evalPrepareModuleExpansion is an EvalNode implementation
// that sets the count or for_each on the instance expander
type evalPrepareModuleExpansion struct {
CallerAddr addrs.ModuleInstance
Call addrs.ModuleCall
Addr addrs.Module
Config *configs.Module
ModuleCall *configs.ModuleCall
}
Expand All @@ -97,12 +90,7 @@ func (n *evalPrepareModuleExpansion) Eval(ctx EvalContext) (interface{}, error)
eachMode := states.NoEach
expander := ctx.InstanceExpander()

if n.ModuleCall == nil {
// FIXME: should we have gotten here with no module call?
log.Printf("[TRACE] evalPrepareModuleExpansion: %s is a singleton", n.CallerAddr.Child(n.Call.Name, addrs.NoKey))
expander.SetModuleSingle(n.CallerAddr, n.Call)
return nil, nil
}
_, call := n.Addr.Call()

count, countDiags := evaluateResourceCountExpression(n.ModuleCall.Count, ctx)
if countDiags.HasErrors() {
Expand All @@ -122,13 +110,18 @@ func (n *evalPrepareModuleExpansion) Eval(ctx EvalContext) (interface{}, error)
eachMode = states.EachMap
}

switch eachMode {
case states.EachList:
expander.SetModuleCount(ctx.Path(), n.Call, count)
case states.EachMap:
expander.SetModuleForEach(ctx.Path(), n.Call, forEach)
default:
expander.SetModuleSingle(n.CallerAddr, n.Call)
// nodeExpandModule itself does not have visibility into how it's ancestors
// were expended, so we use the expander here to provide all possible paths
// to our module, and register module instances with each of them.
for _, path := range expander.ExpandModule(n.Addr.Parent()) {
switch eachMode {
case states.EachList:
expander.SetModuleCount(path, call, count)
case states.EachMap:
expander.SetModuleForEach(path, call, forEach)
default:
expander.SetModuleSingle(path, call)
}
}

return nil, nil
Expand Down
12 changes: 10 additions & 2 deletions terraform/node_module_removed.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type NodeModuleRemoved struct {
}

var (
_ GraphNodeSubPath = (*NodeModuleRemoved)(nil)
_ GraphNodeModuleInstance = (*NodeModuleRemoved)(nil)
_ RemovableIfNotTargeted = (*NodeModuleRemoved)(nil)
_ GraphNodeEvalable = (*NodeModuleRemoved)(nil)
_ GraphNodeReferencer = (*NodeModuleRemoved)(nil)
Expand All @@ -24,11 +24,19 @@ func (n *NodeModuleRemoved) Name() string {
return fmt.Sprintf("%s (removed)", n.Addr.String())
}

// GraphNodeSubPath
// GraphNodeModuleInstance
func (n *NodeModuleRemoved) Path() addrs.ModuleInstance {
return n.Addr
}

// GraphNodeModulePath implementation
func (n *NodeModuleRemoved) ModulePath() addrs.Module {
// This node represents the module call within a module,
// so return the CallerAddr as the path as the module
// call may expand into multiple child instances
return n.Addr.Module()
}

// GraphNodeEvalable
func (n *NodeModuleRemoved) EvalTree() EvalNode {
return &EvalOpFilter{
Expand Down
Loading

0 comments on commit 7d9fde1

Please sign in to comment.