2019-11-22 03:45:43 +01:00
|
|
|
package terraform
|
|
|
|
|
|
|
|
import (
|
2020-04-01 22:37:55 +02:00
|
|
|
"fmt"
|
|
|
|
|
2019-11-22 03:45:43 +01:00
|
|
|
"github.com/hashicorp/terraform/addrs"
|
|
|
|
"github.com/hashicorp/terraform/configs"
|
2020-02-24 23:42:32 +01:00
|
|
|
"github.com/hashicorp/terraform/lang"
|
2019-11-22 03:45:43 +01:00
|
|
|
)
|
|
|
|
|
2020-04-01 22:37:55 +02:00
|
|
|
// graphNodeModuleCloser is an interface implemented by nodes that finalize the
|
|
|
|
// evaluation of modules.
|
|
|
|
type graphNodeModuleCloser interface {
|
|
|
|
CloseModule() addrs.Module
|
|
|
|
}
|
|
|
|
|
2019-11-22 03:45:43 +01:00
|
|
|
// nodeExpandModule represents a module call in the configuration that
|
|
|
|
// might expand into multiple module instances depending on how it is
|
|
|
|
// configured.
|
|
|
|
type nodeExpandModule struct {
|
2020-02-24 23:42:32 +01:00
|
|
|
Addr addrs.Module
|
2019-11-22 03:45:43 +01:00
|
|
|
Config *configs.Module
|
2020-02-24 23:42:32 +01:00
|
|
|
ModuleCall *configs.ModuleCall
|
2019-11-22 03:45:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
2020-03-06 23:16:07 +01:00
|
|
|
_ RemovableIfNotTargeted = (*nodeExpandModule)(nil)
|
|
|
|
_ GraphNodeEvalable = (*nodeExpandModule)(nil)
|
|
|
|
_ GraphNodeReferencer = (*nodeExpandModule)(nil)
|
2019-11-22 03:45:43 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
func (n *nodeExpandModule) Name() string {
|
2020-03-06 17:53:46 +01:00
|
|
|
return n.Addr.String()
|
2019-11-22 03:45:43 +01:00
|
|
|
}
|
|
|
|
|
2020-03-05 03:00:16 +01:00
|
|
|
// 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
|
2020-03-06 17:53:46 +01:00
|
|
|
return n.Addr.Parent()
|
2020-03-05 03:00:16 +01:00
|
|
|
}
|
|
|
|
|
2019-11-22 03:45:43 +01:00
|
|
|
// GraphNodeReferencer implementation
|
|
|
|
func (n *nodeExpandModule) References() []*addrs.Reference {
|
2020-02-24 23:42:32 +01:00
|
|
|
var refs []*addrs.Reference
|
|
|
|
|
|
|
|
if n.ModuleCall == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-11-22 03:45:43 +01:00
|
|
|
// Expansion only uses the count and for_each expressions, so this
|
|
|
|
// particular graph node only refers to those.
|
|
|
|
// Individual variable values in the module call definition might also
|
|
|
|
// refer to other objects, but that's handled by
|
|
|
|
// NodeApplyableModuleVariable.
|
|
|
|
//
|
|
|
|
// Because our Path method returns the module instance that contains
|
|
|
|
// our call, these references will be correctly interpreted as being
|
|
|
|
// in the calling module's namespace, not the namespaces of any of the
|
|
|
|
// child module instances we might expand to during our evaluation.
|
2020-02-24 23:42:32 +01:00
|
|
|
|
|
|
|
if n.ModuleCall.Count != nil {
|
|
|
|
refs, _ = lang.ReferencesInExpr(n.ModuleCall.Count)
|
|
|
|
}
|
|
|
|
if n.ModuleCall.ForEach != nil {
|
|
|
|
refs, _ = lang.ReferencesInExpr(n.ModuleCall.ForEach)
|
|
|
|
}
|
|
|
|
return appendResourceDestroyReferences(refs)
|
2019-11-22 03:45:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// RemovableIfNotTargeted implementation
|
|
|
|
func (n *nodeExpandModule) RemoveIfNotTargeted() bool {
|
|
|
|
// We need to add this so that this node will be removed if
|
|
|
|
// it isn't targeted or a dependency of a target.
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// GraphNodeEvalable
|
|
|
|
func (n *nodeExpandModule) EvalTree() EvalNode {
|
|
|
|
return &evalPrepareModuleExpansion{
|
2020-03-06 17:53:46 +01:00
|
|
|
Addr: n.Addr,
|
2019-11-22 03:45:43 +01:00
|
|
|
Config: n.Config,
|
2020-02-24 23:42:32 +01:00
|
|
|
ModuleCall: n.ModuleCall,
|
2019-11-22 03:45:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-03 15:30:25 +02:00
|
|
|
// nodeCloseModule represents an expanded module during apply, and is visited
|
2020-04-01 22:37:55 +02:00
|
|
|
// after all other module instance nodes. This node will depend on all module
|
|
|
|
// instance resource and outputs, and anything depending on the module should
|
|
|
|
// wait on this node.
|
|
|
|
// Besides providing a root node for dependency ordering, nodeCloseModule also
|
|
|
|
// cleans up state after all the module nodes have been evaluated, removing
|
|
|
|
// empty resources and modules from the state.
|
|
|
|
type nodeCloseModule struct {
|
2020-04-02 21:49:32 +02:00
|
|
|
Addr addrs.Module
|
2020-04-01 22:37:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (n *nodeCloseModule) Name() string {
|
2020-04-02 21:49:32 +02:00
|
|
|
if len(n.Addr) == 0 {
|
|
|
|
return "root"
|
|
|
|
}
|
2020-04-01 22:37:55 +02:00
|
|
|
return n.Addr.String() + " (close)"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *nodeCloseModule) CloseModule() addrs.Module {
|
|
|
|
return n.Addr
|
|
|
|
}
|
|
|
|
|
|
|
|
// RemovableIfNotTargeted implementation
|
|
|
|
func (n *nodeCloseModule) RemoveIfNotTargeted() bool {
|
|
|
|
// We need to add this so that this node will be removed if
|
|
|
|
// it isn't targeted or a dependency of a target.
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *nodeCloseModule) EvalTree() EvalNode {
|
|
|
|
return &EvalSequence{
|
|
|
|
Nodes: []EvalNode{
|
|
|
|
&EvalOpFilter{
|
|
|
|
Ops: []walkOperation{walkApply, walkDestroy},
|
2020-04-02 22:02:52 +02:00
|
|
|
Node: &evalCloseModule{
|
2020-04-02 21:49:32 +02:00
|
|
|
Addr: n.Addr,
|
2020-04-01 22:37:55 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-02 22:02:52 +02:00
|
|
|
type evalCloseModule struct {
|
2020-04-02 21:49:32 +02:00
|
|
|
Addr addrs.Module
|
2020-04-01 22:37:55 +02:00
|
|
|
}
|
|
|
|
|
2020-04-02 22:02:52 +02:00
|
|
|
func (n *evalCloseModule) Eval(ctx EvalContext) (interface{}, error) {
|
2020-04-01 22:37:55 +02:00
|
|
|
// We need the full, locked state, because SyncState does not provide a way to
|
|
|
|
// transact over multiple module instances at the moment.
|
|
|
|
state := ctx.State().Lock()
|
|
|
|
defer ctx.State().Unlock()
|
|
|
|
|
|
|
|
expander := ctx.InstanceExpander()
|
|
|
|
currentModuleInstances := expander.ExpandModule(n.Addr)
|
|
|
|
|
|
|
|
for modKey, mod := range state.Modules {
|
|
|
|
if !n.Addr.Equal(mod.Addr.Module()) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// clean out any empty resources
|
|
|
|
for resKey, res := range mod.Resources {
|
|
|
|
if len(res.Instances) == 0 {
|
|
|
|
delete(mod.Resources, resKey)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if this instance is not in the current expansion, remove it from the
|
|
|
|
// state
|
|
|
|
found := false
|
|
|
|
for _, current := range currentModuleInstances {
|
|
|
|
if current.Equal(mod.Addr) {
|
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !found {
|
|
|
|
if len(mod.Resources) > 0 {
|
|
|
|
// FIXME: add more info to this error
|
|
|
|
return nil, fmt.Errorf("module %q still contains resources in state", mod.Addr)
|
|
|
|
}
|
|
|
|
|
|
|
|
delete(state.Modules, modKey)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2020-02-24 23:42:32 +01:00
|
|
|
// evalPrepareModuleExpansion is an EvalNode implementation
|
|
|
|
// that sets the count or for_each on the instance expander
|
2019-11-22 03:45:43 +01:00
|
|
|
type evalPrepareModuleExpansion struct {
|
2020-03-06 17:53:46 +01:00
|
|
|
Addr addrs.Module
|
2019-11-22 03:45:43 +01:00
|
|
|
Config *configs.Module
|
2020-02-24 23:42:32 +01:00
|
|
|
ModuleCall *configs.ModuleCall
|
2019-11-22 03:45:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (n *evalPrepareModuleExpansion) Eval(ctx EvalContext) (interface{}, error) {
|
2020-02-24 23:42:32 +01:00
|
|
|
expander := ctx.InstanceExpander()
|
2020-03-06 17:53:46 +01:00
|
|
|
_, call := n.Addr.Call()
|
2020-02-24 23:42:32 +01:00
|
|
|
|
2020-03-24 20:19:12 +01:00
|
|
|
// nodeExpandModule itself does not have visibility into how its ancestors
|
|
|
|
// were expanded, so we use the expander here to provide all possible paths
|
|
|
|
// to our module, and register module instances with each of them.
|
|
|
|
for _, module := range expander.ExpandModule(n.Addr.Parent()) {
|
|
|
|
ctx = ctx.WithPath(module)
|
2020-02-24 23:42:32 +01:00
|
|
|
|
2020-04-05 16:46:36 +02:00
|
|
|
switch {
|
|
|
|
case n.ModuleCall.Count != nil:
|
|
|
|
count, diags := evaluateResourceCountExpression(n.ModuleCall.Count, ctx)
|
|
|
|
if diags.HasErrors() {
|
|
|
|
return nil, diags.Err()
|
|
|
|
}
|
2020-03-24 20:19:12 +01:00
|
|
|
expander.SetModuleCount(module, call, count)
|
2020-04-05 16:46:36 +02:00
|
|
|
|
|
|
|
case n.ModuleCall.ForEach != nil:
|
|
|
|
forEach, diags := evaluateResourceForEachExpression(n.ModuleCall.ForEach, ctx)
|
|
|
|
if diags.HasErrors() {
|
|
|
|
return nil, diags.Err()
|
|
|
|
}
|
2020-03-24 20:19:12 +01:00
|
|
|
expander.SetModuleForEach(module, call, forEach)
|
2020-04-05 16:46:36 +02:00
|
|
|
|
2020-03-06 23:16:07 +01:00
|
|
|
default:
|
2020-03-24 20:19:12 +01:00
|
|
|
expander.SetModuleSingle(module, call)
|
2020-03-06 23:16:07 +01:00
|
|
|
}
|
2020-02-24 23:42:32 +01:00
|
|
|
}
|
|
|
|
|
2019-11-22 03:45:43 +01:00
|
|
|
return nil, nil
|
|
|
|
}
|