diff --git a/addrs/module.go b/addrs/module.go index e2984662c229..c887718b1d6d 100644 --- a/addrs/module.go +++ b/addrs/module.go @@ -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 { @@ -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 +} diff --git a/terraform/graph.go b/terraform/graph.go index 1ac8dac8ba7e..373819f7eb7a 100644 --- a/terraform/graph.go +++ b/terraform/graph.go @@ -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()) } @@ -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 } diff --git a/terraform/graph_builder_apply_test.go b/terraform/graph_builder_apply_test.go index 8eebecf0d6d6..293af99dbe67 100644 --- a/terraform/graph_builder_apply_test.go +++ b/terraform/graph_builder_apply_test.go @@ -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": diff --git a/terraform/graph_interface_subgraph.go b/terraform/graph_interface_subgraph.go index 768590fb053b..9ff6e763c8e4 100644 --- a/terraform/graph_interface_subgraph.go +++ b/terraform/graph_interface_subgraph.go @@ -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 +} diff --git a/terraform/node_data_refresh.go b/terraform/node_data_refresh.go index 87795a97f723..dd0b4335290c 100644 --- a/terraform/node_data_refresh.go +++ b/terraform/node_data_refresh.go @@ -15,7 +15,7 @@ type NodeRefreshableDataResource struct { } var ( - _ GraphNodeSubPath = (*NodeRefreshableDataResource)(nil) + _ GraphNodeModuleInstance = (*NodeRefreshableDataResource)(nil) _ GraphNodeDynamicExpandable = (*NodeRefreshableDataResource)(nil) _ GraphNodeReferenceable = (*NodeRefreshableDataResource)(nil) _ GraphNodeReferencer = (*NodeRefreshableDataResource)(nil) diff --git a/terraform/node_data_refresh_test.go b/terraform/node_data_refresh_test.go index 232f104b1429..eaa233b4c5bc 100644 --- a/terraform/node_data_refresh_test.go +++ b/terraform/node_data_refresh_test.go @@ -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"], }, } @@ -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"), diff --git a/terraform/node_local.go b/terraform/node_local.go index 591eb305a661..50cff018a829 100644 --- a/terraform/node_local.go +++ b/terraform/node_local.go @@ -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: @@ -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 diff --git a/terraform/node_module_expand.go b/terraform/node_module_expand.go index 71d8a177f343..1e7d4042a15c 100644 --- a/terraform/node_module_expand.go +++ b/terraform/node_module_expand.go @@ -1,8 +1,6 @@ package terraform import ( - "log" - "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/lang" @@ -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 @@ -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, } @@ -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 } @@ -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() { @@ -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 diff --git a/terraform/node_module_removed.go b/terraform/node_module_removed.go index 43d9bcd4b5f6..e472814907d6 100644 --- a/terraform/node_module_removed.go +++ b/terraform/node_module_removed.go @@ -13,7 +13,7 @@ type NodeModuleRemoved struct { } var ( - _ GraphNodeSubPath = (*NodeModuleRemoved)(nil) + _ GraphNodeModuleInstance = (*NodeModuleRemoved)(nil) _ RemovableIfNotTargeted = (*NodeModuleRemoved)(nil) _ GraphNodeEvalable = (*NodeModuleRemoved)(nil) _ GraphNodeReferencer = (*NodeModuleRemoved)(nil) @@ -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{ diff --git a/terraform/node_module_variable.go b/terraform/node_module_variable.go index 25954edbc333..a320cb373571 100644 --- a/terraform/node_module_variable.go +++ b/terraform/node_module_variable.go @@ -25,14 +25,13 @@ var ( _ GraphNodeReferenceOutside = (*NodePlannableModuleVariable)(nil) _ GraphNodeReferenceable = (*NodePlannableModuleVariable)(nil) _ GraphNodeReferencer = (*NodePlannableModuleVariable)(nil) - _ GraphNodeSubPath = (*NodePlannableModuleVariable)(nil) _ RemovableIfNotTargeted = (*NodePlannableModuleVariable)(nil) ) func (n *NodePlannableModuleVariable) DynamicExpand(ctx EvalContext) (*Graph, error) { var g Graph expander := ctx.InstanceExpander() - for _, module := range expander.ExpandModule(ctx.Path().Module()) { + for _, module := range expander.ExpandModule(n.Module) { o := &NodeApplyableModuleVariable{ Addr: n.Addr.Absolute(module), Config: n.Config, @@ -47,11 +46,9 @@ func (n *NodePlannableModuleVariable) Name() string { return fmt.Sprintf("%s.%s", n.Module, n.Addr.String()) } -// GraphNodeSubPath -func (n *NodePlannableModuleVariable) Path() addrs.ModuleInstance { - // Return an UnkeyedInstanceShim as our placeholder, - // given that modules will be unexpanded at this point in the walk - return n.Module.UnkeyedInstanceShim() +// GraphNodeModulePath +func (n *NodePlannableModuleVariable) ModulePath() addrs.Module { + return n.Module } // GraphNodeReferencer @@ -117,7 +114,7 @@ type NodeApplyableModuleVariable struct { // Ensure that we are implementing all of the interfaces we think we are // implementing. var ( - _ GraphNodeSubPath = (*NodeApplyableModuleVariable)(nil) + _ GraphNodeModuleInstance = (*NodeApplyableModuleVariable)(nil) _ RemovableIfNotTargeted = (*NodeApplyableModuleVariable)(nil) _ GraphNodeReferenceOutside = (*NodeApplyableModuleVariable)(nil) _ GraphNodeReferenceable = (*NodeApplyableModuleVariable)(nil) @@ -130,13 +127,18 @@ func (n *NodeApplyableModuleVariable) Name() string { return n.Addr.String() } -// GraphNodeSubPath +// GraphNodeModuleInstance func (n *NodeApplyableModuleVariable) Path() addrs.ModuleInstance { // We execute in the parent scope (above our own module) because // expressions in our value are resolved in that context. return n.Addr.Module.Parent() } +// GraphNodeModulePath +func (n *NodeApplyableModuleVariable) ModulePath() addrs.Module { + return n.Addr.Module.Parent().Module() +} + // RemovableIfNotTargeted func (n *NodeApplyableModuleVariable) RemoveIfNotTargeted() bool { // We need to add this so that this node will be removed if diff --git a/terraform/node_output.go b/terraform/node_output.go index 063611916bdf..6aa91da87d12 100644 --- a/terraform/node_output.go +++ b/terraform/node_output.go @@ -2,6 +2,7 @@ package terraform import ( "fmt" + "log" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs" @@ -18,10 +19,8 @@ type NodePlannableOutput struct { } var ( - _ GraphNodeSubPath = (*NodePlannableOutput)(nil) - _ RemovableIfNotTargeted = (*NodePlannableOutput)(nil) - _ GraphNodeReferenceable = (*NodePlannableOutput)(nil) - //_ GraphNodeEvalable = (*NodePlannableOutput)(nil) + _ RemovableIfNotTargeted = (*NodePlannableOutput)(nil) + _ GraphNodeReferenceable = (*NodePlannableOutput)(nil) _ GraphNodeReferencer = (*NodePlannableOutput)(nil) _ GraphNodeDynamicExpandable = (*NodePlannableOutput)(nil) ) @@ -29,26 +28,29 @@ var ( func (n *NodePlannableOutput) DynamicExpand(ctx EvalContext) (*Graph, error) { var g Graph expander := ctx.InstanceExpander() - for _, module := range expander.ExpandModule(ctx.Path().Module()) { + for _, module := range expander.ExpandModule(n.Module) { o := &NodeApplyableOutput{ Addr: n.Addr.Absolute(module), Config: n.Config, } - // log.Printf("[TRACE] Expanding output: adding %s as %T", o.Addr.String(), o) + log.Printf("[TRACE] Expanding output: adding %s as %T", o.Addr.String(), o) g.Add(o) } return &g, nil } func (n *NodePlannableOutput) Name() string { - return n.Addr.Absolute(n.Module.UnkeyedInstanceShim()).String() + path := n.Module.String() + addr := n.Addr.String() + if path != "" { + return path + "." + addr + } + return addr } -// GraphNodeSubPath -func (n *NodePlannableOutput) Path() addrs.ModuleInstance { - // Return an UnkeyedInstanceShim as our placeholder, - // given that modules will be unexpanded at this point in the walk - return n.Module.UnkeyedInstanceShim() +// GraphNodeModulePath +func (n *NodePlannableOutput) ModulePath() addrs.Module { + return n.Module } // GraphNodeReferenceable @@ -61,9 +63,6 @@ func (n *NodePlannableOutput) ReferenceableAddrs() []addrs.Referenceable { // the output is referenced through the module call, and via the // module itself. _, call := n.Module.Call() - - // FIXME: make something like ModuleCallOutput for this type of reference - // that doesn't need an instance shim callOutput := addrs.ModuleCallOutput{ Call: call.Instance(addrs.NoKey), Name: n.Addr.Name, @@ -109,7 +108,7 @@ type NodeApplyableOutput struct { } var ( - _ GraphNodeSubPath = (*NodeApplyableOutput)(nil) + _ GraphNodeModuleInstance = (*NodeApplyableOutput)(nil) _ RemovableIfNotTargeted = (*NodeApplyableOutput)(nil) _ GraphNodeTargetDownstream = (*NodeApplyableOutput)(nil) _ GraphNodeReferenceable = (*NodeApplyableOutput)(nil) @@ -123,11 +122,16 @@ func (n *NodeApplyableOutput) Name() string { return n.Addr.String() } -// GraphNodeSubPath +// GraphNodeModuleInstance func (n *NodeApplyableOutput) Path() addrs.ModuleInstance { return n.Addr.Module } +// GraphNodeModulePath +func (n *NodeApplyableOutput) ModulePath() addrs.Module { + return n.Addr.Module.Module() +} + // RemovableIfNotTargeted func (n *NodeApplyableOutput) RemoveIfNotTargeted() bool { // We need to add this so that this node will be removed if @@ -237,7 +241,6 @@ type NodeDestroyableOutput struct { } var ( - _ GraphNodeSubPath = (*NodeDestroyableOutput)(nil) _ RemovableIfNotTargeted = (*NodeDestroyableOutput)(nil) _ GraphNodeTargetDownstream = (*NodeDestroyableOutput)(nil) _ GraphNodeReferencer = (*NodeDestroyableOutput)(nil) @@ -249,9 +252,9 @@ func (n *NodeDestroyableOutput) Name() string { return fmt.Sprintf("%s (destroy)", n.Addr.String()) } -// GraphNodeSubPath -func (n *NodeDestroyableOutput) Path() addrs.ModuleInstance { - return n.Module.UnkeyedInstanceShim() +// GraphNodeModulePath +func (n *NodeDestroyableOutput) ModulePath() addrs.Module { + return n.Module } // RemovableIfNotTargeted diff --git a/terraform/node_output_orphan.go b/terraform/node_output_orphan.go index f8f7124c6868..060f27ea7b09 100644 --- a/terraform/node_output_orphan.go +++ b/terraform/node_output_orphan.go @@ -12,7 +12,7 @@ type NodeOutputOrphan struct { } var ( - _ GraphNodeSubPath = (*NodeOutputOrphan)(nil) + _ GraphNodeModuleInstance = (*NodeOutputOrphan)(nil) _ GraphNodeReferenceable = (*NodeOutputOrphan)(nil) _ GraphNodeReferenceOutside = (*NodeOutputOrphan)(nil) _ GraphNodeEvalable = (*NodeOutputOrphan)(nil) @@ -32,11 +32,16 @@ func (n *NodeOutputOrphan) ReferenceableAddrs() []addrs.Referenceable { return referenceableAddrsForOutput(n.Addr) } -// GraphNodeSubPath +// GraphNodeModuleInstance func (n *NodeOutputOrphan) Path() addrs.ModuleInstance { return n.Addr.Module } +// GraphNodeModulePath +func (n *NodeOutputOrphan) ModulePath() addrs.Module { + return n.Addr.Module.Module() +} + // GraphNodeEvalable func (n *NodeOutputOrphan) EvalTree() EvalNode { return &EvalOpFilter{ diff --git a/terraform/node_provider_abstract.go b/terraform/node_provider_abstract.go index a0cdcfe01d6a..5c0d953f31b1 100644 --- a/terraform/node_provider_abstract.go +++ b/terraform/node_provider_abstract.go @@ -26,7 +26,7 @@ type NodeAbstractProvider struct { } var ( - _ GraphNodeSubPath = (*NodeAbstractProvider)(nil) + _ GraphNodeModuleInstance = (*NodeAbstractProvider)(nil) _ RemovableIfNotTargeted = (*NodeAbstractProvider)(nil) _ GraphNodeReferencer = (*NodeAbstractProvider)(nil) _ GraphNodeProvider = (*NodeAbstractProvider)(nil) @@ -39,11 +39,16 @@ func (n *NodeAbstractProvider) Name() string { return n.Addr.String() } -// GraphNodeSubPath +// GraphNodeModuleInstance func (n *NodeAbstractProvider) Path() addrs.ModuleInstance { return n.Addr.Module } +// GraphNodeModulePath +func (n *NodeAbstractProvider) ModulePath() addrs.Module { + return n.Addr.Module.Module() +} + // RemovableIfNotTargeted func (n *NodeAbstractProvider) RemoveIfNotTargeted() bool { // We need to add this so that this node will be removed if diff --git a/terraform/node_provider_disabled.go b/terraform/node_provider_disabled.go index 30d8813a45db..fc66f2e60a2e 100644 --- a/terraform/node_provider_disabled.go +++ b/terraform/node_provider_disabled.go @@ -14,7 +14,7 @@ type NodeDisabledProvider struct { } var ( - _ GraphNodeSubPath = (*NodeDisabledProvider)(nil) + _ GraphNodeModuleInstance = (*NodeDisabledProvider)(nil) _ RemovableIfNotTargeted = (*NodeDisabledProvider)(nil) _ GraphNodeReferencer = (*NodeDisabledProvider)(nil) _ GraphNodeProvider = (*NodeDisabledProvider)(nil) diff --git a/terraform/node_provisioner.go b/terraform/node_provisioner.go index cf51cf06c3d9..1160498ae487 100644 --- a/terraform/node_provisioner.go +++ b/terraform/node_provisioner.go @@ -14,9 +14,9 @@ type NodeProvisioner struct { } var ( - _ GraphNodeSubPath = (*NodeProvisioner)(nil) - _ GraphNodeProvisioner = (*NodeProvisioner)(nil) - _ GraphNodeEvalable = (*NodeProvisioner)(nil) + _ GraphNodeModuleInstance = (*NodeProvisioner)(nil) + _ GraphNodeProvisioner = (*NodeProvisioner)(nil) + _ GraphNodeEvalable = (*NodeProvisioner)(nil) ) func (n *NodeProvisioner) Name() string { @@ -28,7 +28,7 @@ func (n *NodeProvisioner) Name() string { return result } -// GraphNodeSubPath +// GraphNodeModuleInstance func (n *NodeProvisioner) Path() addrs.ModuleInstance { return n.PathValue } diff --git a/terraform/node_resource_abstract.go b/terraform/node_resource_abstract.go index e14c021805b4..d67bd5953a4d 100644 --- a/terraform/node_resource_abstract.go +++ b/terraform/node_resource_abstract.go @@ -43,11 +43,8 @@ type GraphNodeResourceInstance interface { // operations. It registers all the interfaces for a resource that common // across multiple operation types. type NodeAbstractResource struct { - //FIXME: AbstractResources are no longer absolute, because modules are not expanded. - // Addr addrs.Resource - // Module addrs.Module - - Addr addrs.AbsResource // Addr is the address for this resource + Addr addrs.Resource + Module addrs.Module // The fields below will be automatically set using the Attach // interfaces if you're running those transforms, but also be explicitly @@ -69,7 +66,6 @@ type NodeAbstractResource struct { } var ( - _ GraphNodeSubPath = (*NodeAbstractResource)(nil) _ GraphNodeReferenceable = (*NodeAbstractResource)(nil) _ GraphNodeReferencer = (*NodeAbstractResource)(nil) _ GraphNodeProviderConsumer = (*NodeAbstractResource)(nil) @@ -83,11 +79,16 @@ var ( _ dag.GraphNodeDotter = (*NodeAbstractResource)(nil) ) +func (n *NodeAbstractResource) addr() addrs.AbsResource { + return n.Addr.Absolute(n.Module.UnkeyedInstanceShim()) +} + // NewNodeAbstractResource creates an abstract resource graph node for // the given absolute resource address. func NewNodeAbstractResource(addr addrs.AbsResource) *NodeAbstractResource { return &NodeAbstractResource{ - Addr: addr, + Addr: addr.Resource, + Module: addr.Module.Module(), } } @@ -108,7 +109,7 @@ type NodeAbstractResourceInstance struct { } var ( - _ GraphNodeSubPath = (*NodeAbstractResourceInstance)(nil) + _ GraphNodeModuleInstance = (*NodeAbstractResourceInstance)(nil) _ GraphNodeReferenceable = (*NodeAbstractResourceInstance)(nil) _ GraphNodeReferencer = (*NodeAbstractResourceInstance)(nil) _ GraphNodeProviderConsumer = (*NodeAbstractResourceInstance)(nil) @@ -134,7 +135,8 @@ func NewNodeAbstractResourceInstance(addr addrs.AbsResourceInstance) *NodeAbstra // request. return &NodeAbstractResourceInstance{ NodeAbstractResource: NodeAbstractResource{ - Addr: addr.ContainingResource(), + Addr: addr.Resource.Resource, + Module: addr.Module.Module(), }, InstanceKey: addr.Resource.Key, } @@ -148,14 +150,19 @@ func (n *NodeAbstractResourceInstance) Name() string { return n.ResourceInstanceAddr().String() } -// GraphNodeSubPath +// GraphNodeModuleInstance func (n *NodeAbstractResource) Path() addrs.ModuleInstance { - return n.Addr.Module + return n.Module.UnkeyedInstanceShim() +} + +// GraphNodeModulePath +func (n *NodeAbstractResource) ModulePath() addrs.Module { + return n.Module } // GraphNodeReferenceable func (n *NodeAbstractResource) ReferenceableAddrs() []addrs.Referenceable { - return []addrs.Referenceable{n.Addr.Resource} + return []addrs.Referenceable{n.Addr} } // GraphNodeReferenceable @@ -301,7 +308,7 @@ func (n *NodeAbstractResource) ProvidedBy() (addrs.ProviderConfig, bool) { // GraphNodeProviderConsumer func (n *NodeAbstractResource) ImpliedProvider() addrs.Provider { - return n.Addr.Resource.DefaultProvider() + return n.Addr.DefaultProvider() } // GraphNodeProviderConsumer @@ -329,7 +336,7 @@ func (n *NodeAbstractResourceInstance) ProvidedBy() (addrs.ProviderConfig, bool) // GraphNodeProviderConsumer func (n *NodeAbstractResourceInstance) ImpliedProvider() addrs.Provider { - return n.Addr.Resource.DefaultProvider() + return n.Addr.DefaultProvider() } // GraphNodeProvisionerConsumer @@ -359,17 +366,17 @@ func (n *NodeAbstractResource) AttachProvisionerSchema(name string, schema *conf // GraphNodeResource func (n *NodeAbstractResource) ResourceAddr() addrs.AbsResource { - return n.Addr + return n.addr() } // GraphNodeResourceInstance func (n *NodeAbstractResourceInstance) ResourceInstanceAddr() addrs.AbsResourceInstance { - return n.NodeAbstractResource.Addr.Instance(n.InstanceKey) + return n.NodeAbstractResource.addr().Instance(n.InstanceKey) } // GraphNodeAddressable, TODO: remove, used by target, should unify func (n *NodeAbstractResource) ResourceAddress() *ResourceAddress { - return NewLegacyResourceAddress(n.Addr) + return NewLegacyResourceAddress(n.addr()) } // GraphNodeTargetable diff --git a/terraform/node_resource_plan.go b/terraform/node_resource_plan.go index 69187d5f3040..bd567e0526ab 100644 --- a/terraform/node_resource_plan.go +++ b/terraform/node_resource_plan.go @@ -19,7 +19,7 @@ type NodePlannableResource struct { } var ( - _ GraphNodeSubPath = (*NodePlannableResource)(nil) + _ GraphNodeModuleInstance = (*NodePlannableResource)(nil) _ GraphNodeDestroyerCBD = (*NodePlannableResource)(nil) _ GraphNodeDynamicExpandable = (*NodePlannableResource)(nil) _ GraphNodeReferenceable = (*NodePlannableResource)(nil) diff --git a/terraform/node_resource_plan_destroy.go b/terraform/node_resource_plan_destroy.go index d0f63a561a4c..deb6b4f7b4cd 100644 --- a/terraform/node_resource_plan_destroy.go +++ b/terraform/node_resource_plan_destroy.go @@ -17,7 +17,7 @@ type NodePlanDestroyableResourceInstance struct { } var ( - _ GraphNodeSubPath = (*NodePlanDestroyableResourceInstance)(nil) + _ GraphNodeModuleInstance = (*NodePlanDestroyableResourceInstance)(nil) _ GraphNodeReferenceable = (*NodePlanDestroyableResourceInstance)(nil) _ GraphNodeReferencer = (*NodePlanDestroyableResourceInstance)(nil) _ GraphNodeDestroyer = (*NodePlanDestroyableResourceInstance)(nil) diff --git a/terraform/node_resource_plan_instance.go b/terraform/node_resource_plan_instance.go index e9a3c60f55ef..27465981a1fa 100644 --- a/terraform/node_resource_plan_instance.go +++ b/terraform/node_resource_plan_instance.go @@ -20,7 +20,7 @@ type NodePlannableResourceInstance struct { } var ( - _ GraphNodeSubPath = (*NodePlannableResourceInstance)(nil) + _ GraphNodeModuleInstance = (*NodePlannableResourceInstance)(nil) _ GraphNodeReferenceable = (*NodePlannableResourceInstance)(nil) _ GraphNodeReferencer = (*NodePlannableResourceInstance)(nil) _ GraphNodeResource = (*NodePlannableResourceInstance)(nil) diff --git a/terraform/node_resource_plan_orphan.go b/terraform/node_resource_plan_orphan.go index 841669491ad0..61f8e621f151 100644 --- a/terraform/node_resource_plan_orphan.go +++ b/terraform/node_resource_plan_orphan.go @@ -13,7 +13,7 @@ type NodePlannableResourceInstanceOrphan struct { } var ( - _ GraphNodeSubPath = (*NodePlannableResourceInstanceOrphan)(nil) + _ GraphNodeModuleInstance = (*NodePlannableResourceInstanceOrphan)(nil) _ GraphNodeReferenceable = (*NodePlannableResourceInstanceOrphan)(nil) _ GraphNodeReferencer = (*NodePlannableResourceInstanceOrphan)(nil) _ GraphNodeResource = (*NodePlannableResourceInstanceOrphan)(nil) diff --git a/terraform/node_resource_refresh.go b/terraform/node_resource_refresh.go index 37ac905922e8..53fdf2cbae9d 100644 --- a/terraform/node_resource_refresh.go +++ b/terraform/node_resource_refresh.go @@ -25,7 +25,7 @@ type NodeRefreshableManagedResource struct { } var ( - _ GraphNodeSubPath = (*NodeRefreshableManagedResource)(nil) + _ GraphNodeModuleInstance = (*NodeRefreshableManagedResource)(nil) _ GraphNodeDynamicExpandable = (*NodeRefreshableManagedResource)(nil) _ GraphNodeReferenceable = (*NodeRefreshableManagedResource)(nil) _ GraphNodeReferencer = (*NodeRefreshableManagedResource)(nil) @@ -142,7 +142,7 @@ type NodeRefreshableManagedResourceInstance struct { } var ( - _ GraphNodeSubPath = (*NodeRefreshableManagedResourceInstance)(nil) + _ GraphNodeModuleInstance = (*NodeRefreshableManagedResourceInstance)(nil) _ GraphNodeReferenceable = (*NodeRefreshableManagedResourceInstance)(nil) _ GraphNodeReferencer = (*NodeRefreshableManagedResourceInstance)(nil) _ GraphNodeDestroyer = (*NodeRefreshableManagedResourceInstance)(nil) diff --git a/terraform/node_resource_refresh_test.go b/terraform/node_resource_refresh_test.go index 4fec35f9e405..438010d39cd6 100644 --- a/terraform/node_resource_refresh_test.go +++ b/terraform/node_resource_refresh_test.go @@ -42,9 +42,12 @@ func TestNodeRefreshableManagedResourceDynamicExpand_scaleOut(t *testing.T) { n := &NodeRefreshableManagedResource{ NodeAbstractResource: &NodeAbstractResource{ - Addr: addrs.RootModuleInstance.Resource( - addrs.ManagedResourceMode, "aws_instance", "foo", - ), + Addr: addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "aws_instance", + Name: "foo", + }, + Module: addrs.RootModule, Config: m.Module.ManagedResources["aws_instance.foo"], }, } @@ -124,9 +127,12 @@ func TestNodeRefreshableManagedResourceDynamicExpand_scaleIn(t *testing.T) { n := &NodeRefreshableManagedResource{ NodeAbstractResource: &NodeAbstractResource{ - Addr: addrs.RootModuleInstance.Resource( - addrs.ManagedResourceMode, "aws_instance", "foo", - ), + Addr: addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "aws_instance", + Name: "foo", + }, + Module: addrs.RootModule, Config: m.Module.ManagedResources["aws_instance.foo"], }, } @@ -166,9 +172,12 @@ func TestNodeRefreshableManagedResourceEvalTree_scaleOut(t *testing.T) { n := &NodeRefreshableManagedResourceInstance{ NodeAbstractResourceInstance: &NodeAbstractResourceInstance{ NodeAbstractResource: NodeAbstractResource{ - Addr: addrs.RootModuleInstance.Resource( - addrs.ManagedResourceMode, "aws_instance", "foo", - ), + Addr: addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "aws_instance", + Name: "foo", + }, + Module: addrs.RootModule, Config: m.Module.ManagedResources["aws_instance.foo"], }, InstanceKey: addrs.IntKey(2), diff --git a/terraform/node_resource_validate.go b/terraform/node_resource_validate.go index dec63d8df975..3703d81ba9b0 100644 --- a/terraform/node_resource_validate.go +++ b/terraform/node_resource_validate.go @@ -15,7 +15,7 @@ type NodeValidatableResource struct { } var ( - _ GraphNodeSubPath = (*NodeValidatableResource)(nil) + _ GraphNodeModuleInstance = (*NodeValidatableResource)(nil) _ GraphNodeEvalable = (*NodeValidatableResource)(nil) _ GraphNodeReferenceable = (*NodeValidatableResource)(nil) _ GraphNodeReferencer = (*NodeValidatableResource)(nil) diff --git a/terraform/node_root_variable.go b/terraform/node_root_variable.go index e3aee6fc80e6..1144f3f1fd03 100644 --- a/terraform/node_root_variable.go +++ b/terraform/node_root_variable.go @@ -13,20 +13,24 @@ type NodeRootVariable struct { } var ( - _ GraphNodeSubPath = (*NodeRootVariable)(nil) - _ GraphNodeReferenceable = (*NodeRootVariable)(nil) - _ dag.GraphNodeDotter = (*NodeApplyableModuleVariable)(nil) + _ GraphNodeModuleInstance = (*NodeRootVariable)(nil) + _ GraphNodeReferenceable = (*NodeRootVariable)(nil) + _ dag.GraphNodeDotter = (*NodeApplyableModuleVariable)(nil) ) func (n *NodeRootVariable) Name() string { return n.Addr.String() } -// GraphNodeSubPath +// GraphNodeModuleInstance func (n *NodeRootVariable) Path() addrs.ModuleInstance { return addrs.RootModuleInstance } +func (n *NodeRootVariable) ModulePath() addrs.Module { + return addrs.RootModule +} + // GraphNodeReferenceable func (n *NodeRootVariable) ReferenceableAddrs() []addrs.Referenceable { return []addrs.Referenceable{n.Addr} diff --git a/terraform/transform_attach_config_provider.go b/terraform/transform_attach_config_provider.go index 897a7e791762..e1cf66362bd0 100644 --- a/terraform/transform_attach_config_provider.go +++ b/terraform/transform_attach_config_provider.go @@ -9,7 +9,7 @@ import ( // that want provider configurations attached. type GraphNodeAttachProvider interface { // Must be implemented to determine the path for the configuration - GraphNodeSubPath + GraphNodeModuleInstance // ProviderName with no module prefix. Example: "aws". ProviderAddr() addrs.AbsProviderConfig diff --git a/terraform/transform_config.go b/terraform/transform_config.go index 9d3b6f4b49bd..0c16f9b56eb7 100644 --- a/terraform/transform_config.go +++ b/terraform/transform_config.go @@ -37,6 +37,11 @@ type ConfigTransformer struct { uniqueMap map[string]struct{} } +// FIXME: should we have an addr.Module + addr.Resource type? +type configName interface { + Name() string +} + func (t *ConfigTransformer) Transform(g *Graph) error { // Lock since we use some internal state t.l.Lock() @@ -53,8 +58,8 @@ func (t *ConfigTransformer) Transform(g *Graph) error { defer func() { t.uniqueMap = nil }() if t.Unique { for _, v := range g.Vertices() { - if rn, ok := v.(GraphNodeResource); ok { - t.uniqueMap[rn.ResourceAddr().String()] = struct{}{} + if rn, ok := v.(configName); ok { + t.uniqueMap[rn.Name()] = struct{}{} } } } @@ -89,14 +94,6 @@ func (t *ConfigTransformer) transformSingle(g *Graph, config *configs.Config) er module := config.Module log.Printf("[TRACE] ConfigTransformer: Starting for path: %v", path) - // For now we assume that each module call produces only one module - // instance with no key, since we don't yet support "count" and "for_each" - // on modules. - // FIXME: As part of supporting "count" and "for_each" on modules, rework - // this so that we'll "expand" the module call first and then create graph - // nodes for each module instance separately. - instPath := path.UnkeyedInstanceShim() - allResources := make([]*configs.Resource, 0, len(module.ManagedResources)+len(module.DataResources)) for _, r := range module.ManagedResources { allResources = append(allResources, r) @@ -113,14 +110,17 @@ func (t *ConfigTransformer) transformSingle(g *Graph, config *configs.Config) er continue } - addr := relAddr.Absolute(instPath) - if _, ok := t.uniqueMap[addr.String()]; ok { + abstract := &NodeAbstractResource{ + Addr: relAddr, + Module: path, + } + + if _, ok := t.uniqueMap[abstract.Name()]; ok { // We've already seen a resource with this address. This should // never happen, because we enforce uniqueness in the config loader. continue } - abstract := &NodeAbstractResource{Addr: addr} var node dag.Vertex = abstract if f := t.Concrete; f != nil { node = f(abstract) diff --git a/terraform/transform_expand.go b/terraform/transform_expand.go index 982c098b81d1..dca71b630fe5 100644 --- a/terraform/transform_expand.go +++ b/terraform/transform_expand.go @@ -1,18 +1,5 @@ package terraform -import ( - "log" - - "github.com/hashicorp/terraform/dag" -) - -// GraphNodeExapndable is an interface that nodes can implement to -// signal that they can be expanded. Expanded nodes turn into -// GraphNodeSubgraph nodes within the graph. -type GraphNodeExpandable interface { - Expand(GraphBuilder) (GraphNodeSubgraph, error) -} - // GraphNodeDynamicExpandable is an interface that nodes can implement // to signal that they can be expanded at eval-time (hence dynamic). // These nodes are given the eval context and are expected to return @@ -20,29 +7,3 @@ type GraphNodeExpandable interface { type GraphNodeDynamicExpandable interface { DynamicExpand(EvalContext) (*Graph, error) } - -// GraphNodeSubgraph is an interface a node can implement if it has -// a larger subgraph that should be walked. -type GraphNodeSubgraph interface { - Subgraph() dag.Grapher -} - -// ExpandTransform is a transformer that does a subgraph expansion -// at graph transform time (vs. at eval time). The benefit of earlier -// subgraph expansion is that errors with the graph build can be detected -// at an earlier stage. -type ExpandTransform struct { - Builder GraphBuilder -} - -func (t *ExpandTransform) Transform(v dag.Vertex) (dag.Vertex, error) { - ev, ok := v.(GraphNodeExpandable) - if !ok { - // This isn't an expandable vertex, so just ignore it. - return v, nil - } - - // Expand the subgraph! - log.Printf("[DEBUG] vertex %q: static expanding", dag.VertexName(ev)) - return ev.Expand(t.Builder) -} diff --git a/terraform/transform_expand_test.go b/terraform/transform_expand_test.go deleted file mode 100644 index d422eddf0994..000000000000 --- a/terraform/transform_expand_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package terraform - -import ( - "strings" - "testing" - - "github.com/hashicorp/terraform/dag" -) - -func TestExpandTransform_impl(t *testing.T) { - var _ GraphVertexTransformer = new(ExpandTransform) -} - -func TestExpandTransform(t *testing.T) { - var g Graph - g.Add(1) - g.Add(2) - g.Connect(dag.BasicEdge(1, 2)) - - tf := &ExpandTransform{} - out, err := tf.Transform(&testExpandable{ - Result: &g, - }) - if err != nil { - t.Fatalf("err: %s", err) - } - - sn, ok := out.(GraphNodeSubgraph) - if !ok { - t.Fatalf("not subgraph: %#v", out) - } - - actual := strings.TrimSpace(sn.Subgraph().(*Graph).String()) - expected := strings.TrimSpace(testExpandTransformStr) - if actual != expected { - t.Fatalf("bad: %s", actual) - } -} - -func TestExpandTransform_nonExpandable(t *testing.T) { - tf := &ExpandTransform{} - out, err := tf.Transform(42) - if err != nil { - t.Fatalf("err: %s", err) - } - if out != 42 { - t.Fatalf("bad: %#v", out) - } -} - -type testExpandable struct { - // Inputs - Result *Graph - ResultError error - - // Outputs - Builder GraphBuilder -} - -func (n *testExpandable) Expand(b GraphBuilder) (GraphNodeSubgraph, error) { - n.Builder = b - return &testSubgraph{n.Result}, n.ResultError -} - -type testSubgraph struct { - Graph *Graph -} - -func (n *testSubgraph) Subgraph() dag.Grapher { - return n.Graph -} - -const testExpandTransformStr = ` -1 - 2 -2 -` diff --git a/terraform/transform_import_state.go b/terraform/transform_import_state.go index 7fe5f12adc03..22c56be4d56c 100644 --- a/terraform/transform_import_state.go +++ b/terraform/transform_import_state.go @@ -48,7 +48,7 @@ type graphNodeImportState struct { } var ( - _ GraphNodeSubPath = (*graphNodeImportState)(nil) + _ GraphNodeModuleInstance = (*graphNodeImportState)(nil) _ GraphNodeEvalable = (*graphNodeImportState)(nil) _ GraphNodeProviderConsumer = (*graphNodeImportState)(nil) _ GraphNodeDynamicExpandable = (*graphNodeImportState)(nil) @@ -83,7 +83,7 @@ func (n *graphNodeImportState) SetProvider(addr addrs.AbsProviderConfig) { n.ResolvedProvider = addr } -// GraphNodeSubPath +// GraphNodeModuleInstance func (n *graphNodeImportState) Path() addrs.ModuleInstance { return n.Addr.Module } @@ -199,8 +199,8 @@ type graphNodeImportStateSub struct { } var ( - _ GraphNodeSubPath = (*graphNodeImportStateSub)(nil) - _ GraphNodeEvalable = (*graphNodeImportStateSub)(nil) + _ GraphNodeModuleInstance = (*graphNodeImportStateSub)(nil) + _ GraphNodeEvalable = (*graphNodeImportStateSub)(nil) ) func (n *graphNodeImportStateSub) Name() string { diff --git a/terraform/transform_local.go b/terraform/transform_local.go index 84eb26b2609c..7739ff48d35f 100644 --- a/terraform/transform_local.go +++ b/terraform/transform_local.go @@ -1,6 +1,7 @@ package terraform import ( + "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs" ) @@ -20,18 +21,11 @@ func (t *LocalTransformer) transformModule(g *Graph, c *configs.Config) error { return nil } - // Our addressing system distinguishes between modules and module instances, - // but we're not yet ready to make that distinction here (since we don't - // support "count"/"for_each" on modules) and so we just do a naive - // transform of the module path into a module instance path, assuming that - // no keys are in use. This should be removed when "count" and "for_each" - // are implemented for modules. - path := c.Path.UnkeyedInstanceShim() - for _, local := range c.Module.Locals { - addr := path.LocalValue(local.Name) - node := &NodeLocal{ + addr := addrs.LocalValue{Name: local.Name} + node := &NodePlannableLocal{ Addr: addr, + Module: c.Path, Config: local, } g.Add(node) diff --git a/terraform/transform_module_expansion.go b/terraform/transform_module_expansion.go index e8d2c4a328e0..266081e1926f 100644 --- a/terraform/transform_module_expansion.go +++ b/terraform/transform_module_expansion.go @@ -33,25 +33,16 @@ func (t *ModuleExpansionTransformer) Transform(g *Graph) error { } func (t *ModuleExpansionTransformer) transform(g *Graph, c *configs.Config, parentNode dag.Vertex) error { - // FIXME: We're using addrs.ModuleInstance to represent the paths here - // because the rest of Terraform Core is expecting that, but in practice - // thus is representing a path through the static module instances (not - // expanded yet), and so as we weave in support for repetition of module - // calls we'll need to make the plan processing actually use addrs.Module - // to represent that our graph nodes are actually representing unexpanded - // static configuration objects, not instances. - fullAddr := c.Path.UnkeyedInstanceShim() - callerAddr, callAddr := fullAddr.Call() + _, call := c.Path.Call() + modCall := c.Parent.Module.ModuleCalls[call.Name] - modulecall := c.Parent.Module.ModuleCalls["child"] v := &nodeExpandModule{ - CallerAddr: callerAddr, - Call: callAddr, + Addr: c.Path, Config: c.Module, - ModuleCall: modulecall, + ModuleCall: modCall, } g.Add(v) - log.Printf("[TRACE] ModuleExpansionTransformer: Added %s as %T", fullAddr, v) + log.Printf("[TRACE] ModuleExpansionTransformer: Added %s as %T", c.Path, v) if parentNode != nil { log.Printf("[TRACE] ModuleExpansionTransformer: %s must wait for expansion of %s", dag.VertexName(v), dag.VertexName(parentNode)) @@ -65,12 +56,12 @@ func (t *ModuleExpansionTransformer) transform(g *Graph, c *configs.Config, pare // work to properly support "count" and "for_each" for modules. Nodes // in the plan graph actually belong to modules, not to module instances. for _, childV := range g.Vertices() { - pather, ok := childV.(GraphNodeSubPath) + pather, ok := childV.(GraphNodeModulePath) if !ok { continue } - if pather.Path().Equal(fullAddr) { - log.Printf("[TRACE] ModuleExpansionTransformer: %s must wait for expansion of %s", dag.VertexName(childV), fullAddr) + if pather.ModulePath().Equal(c.Path) { + log.Printf("[TRACE] ModuleExpansionTransformer: %s must wait for expansion of %s", dag.VertexName(childV), c.Path) g.Connect(dag.BasicEdge(childV, v)) } } diff --git a/terraform/transform_module_variable.go b/terraform/transform_module_variable.go index 2cbb5cd117b8..c651358882e7 100644 --- a/terraform/transform_module_variable.go +++ b/terraform/transform_module_variable.go @@ -58,15 +58,7 @@ func (t *ModuleVariableTransformer) transform(g *Graph, parent, c *configs.Confi } func (t *ModuleVariableTransformer) transformSingle(g *Graph, parent, c *configs.Config) error { - - // Our addressing system distinguishes between modules and module instances, - // but we're not yet ready to make that distinction here (since we don't - // support "count"/"for_each" on modules) and so we just do a naive - // transform of the module path into a module instance path, assuming that - // no keys are in use. This should be removed when "count" and "for_each" - // are implemented for modules. - path := c.Path.UnkeyedInstanceShim() - _, call := path.Call() + _, call := c.Path.Call() // Find the call in the parent module configuration, so we can get the // expressions given for each input variable at the call site. @@ -74,7 +66,7 @@ func (t *ModuleVariableTransformer) transformSingle(g *Graph, parent, c *configs if !exists { // This should never happen, since it indicates an improperly-constructed // configuration tree. - panic(fmt.Errorf("no module call block found for %s", path)) + panic(fmt.Errorf("no module call block found for %s", c.Path)) } // We need to construct a schema for the expected call arguments based on diff --git a/terraform/transform_output.go b/terraform/transform_output.go index 8e19000af9c4..7eb8e4884ec8 100644 --- a/terraform/transform_output.go +++ b/terraform/transform_output.go @@ -72,6 +72,11 @@ func (t *DestroyOutputTransformer) Transform(g *Graph) error { continue } + // We only destroy root outputs + if !output.Module.Equal(addrs.RootModule) { + continue + } + // create the destroy node for this output node := &NodeDestroyableOutput{ Addr: output.Addr, diff --git a/terraform/transform_provider.go b/terraform/transform_provider.go index 18f628995380..910e8aac189e 100644 --- a/terraform/transform_provider.go +++ b/terraform/transform_provider.go @@ -44,7 +44,7 @@ func TransformProviders(providers []string, concrete ConcreteProviderNodeFunc, c // // Name returns the full name of the provider in the config. type GraphNodeProvider interface { - GraphNodeSubPath + GraphNodeModuleInstance ProviderAddr() addrs.AbsProviderConfig Name() string } @@ -53,7 +53,7 @@ type GraphNodeProvider interface { // provider must implement. The CloseProviderName returned is the name of // the provider they satisfy. type GraphNodeCloseProvider interface { - GraphNodeSubPath + GraphNodeModuleInstance CloseProviderAddr() addrs.AbsProviderConfig } @@ -63,7 +63,7 @@ type GraphNodeCloseProvider interface { // or in an ancestor module, with the resulting absolute address passed to // SetProvider. type GraphNodeProviderConsumer interface { - GraphNodeSubPath + GraphNodeModuleInstance // ProvidedBy returns the address of the provider configuration the node // refers to, if available. The following value types may be returned: // @@ -186,7 +186,7 @@ func (t *ProviderTransformer) Transform(g *Graph) error { p := req.Addr target := m[key] - _, ok := v.(GraphNodeSubPath) + _, ok := v.(GraphNodeModuleInstance) if !ok && target == nil { // No target and no path to traverse up from diags = diags.Append(fmt.Errorf("%s: provider %s couldn't be found", dag.VertexName(v), p)) @@ -412,7 +412,7 @@ func (t *MissingProviderTransformer) Transform(g *Graph) error { // ParentProviderTransformer connects provider nodes to their parents. // // This works by finding nodes that are both GraphNodeProviders and -// GraphNodeSubPath. It then connects the providers to their parent +// GraphNodeModuleInstance. It then connects the providers to their parent // path. The parent provider is always at the root level. type ParentProviderTransformer struct{} @@ -511,7 +511,7 @@ func (n *graphNodeCloseProvider) Name() string { return n.Addr.String() + " (close)" } -// GraphNodeSubPath impl. +// GraphNodeModuleInstance impl. func (n *graphNodeCloseProvider) Path() addrs.ModuleInstance { return n.Addr.Module } diff --git a/terraform/transform_reference.go b/terraform/transform_reference.go index 9b397ccd4907..5dff1a0ba016 100644 --- a/terraform/transform_reference.go +++ b/terraform/transform_reference.go @@ -22,7 +22,7 @@ import ( // be referenced and other methods of referencing may still be possible (such // as by path!) type GraphNodeReferenceable interface { - GraphNodeSubPath + GraphNodeModulePath // ReferenceableAddrs returns a list of addresses through which this can be // referenced. @@ -32,7 +32,7 @@ type GraphNodeReferenceable interface { // GraphNodeReferencer must be implemented by nodes that reference other // Terraform items and therefore depend on them. type GraphNodeReferencer interface { - GraphNodeSubPath + GraphNodeModulePath // References returns a list of references made by this node, which // include both a referenced address and source location information for @@ -252,9 +252,6 @@ func (m *ReferenceMap) References(v dag.Vertex) []dag.Vertex { if !ok { return nil } - if _, ok := v.(GraphNodeSubPath); !ok { - return nil - } var matches []dag.Vertex @@ -288,7 +285,7 @@ func (m *ReferenceMap) References(v dag.Vertex) []dag.Vertex { return matches } -func (m *ReferenceMap) mapKey(path addrs.ModuleInstance, addr addrs.Referenceable) string { +func (m *ReferenceMap) mapKey(path addrs.Module, addr addrs.Referenceable) string { return fmt.Sprintf("%s|%s", path.String(), addr.String()) } @@ -296,48 +293,48 @@ func (m *ReferenceMap) mapKey(path addrs.ModuleInstance, addr addrs.Referenceabl // referenced. This is the path that its results from ReferenceableAddrs // are considered to be relative to. // -// Only GraphNodeSubPath implementations can be referenced, so this method will +// Only GraphNodeModulePath implementations can be referenced, so this method will // panic if the given vertex does not implement that interface. -func (m *ReferenceMap) vertexReferenceablePath(v dag.Vertex) addrs.ModuleInstance { - sp, ok := v.(GraphNodeSubPath) +func vertexReferenceablePath(v dag.Vertex) addrs.Module { + sp, ok := v.(GraphNodeModulePath) if !ok { // Only nodes with paths can participate in a reference map. - panic(fmt.Errorf("vertexMapKey on vertex type %T which doesn't implement GraphNodeSubPath", sp)) + panic(fmt.Errorf("vertexMapKey on vertex type %T which doesn't implement GraphNodeModulePath", sp)) } if outside, ok := v.(GraphNodeReferenceOutside); ok { // Vertex is referenced from a different module than where it was // declared. path, _ := outside.ReferenceOutside() - return path.UnkeyedInstanceShim() + return path } // Vertex is referenced from the same module as where it was declared. - return sp.Path() + return sp.ModulePath() } // vertexReferencePath returns the path in which references _from_ the given // vertex must be interpreted. // -// Only GraphNodeSubPath implementations can have references, so this method +// Only GraphNodeModulePath implementations can have references, so this method // will panic if the given vertex does not implement that interface. -func vertexReferencePath(referrer dag.Vertex) addrs.ModuleInstance { - sp, ok := referrer.(GraphNodeSubPath) +func vertexReferencePath(v dag.Vertex) addrs.Module { + sp, ok := v.(GraphNodeModulePath) if !ok { // Only nodes with paths can participate in a reference map. - panic(fmt.Errorf("vertexReferencePath on vertex type %T which doesn't implement GraphNodeSubPath", sp)) + panic(fmt.Errorf("vertexReferencePath on vertex type %T which doesn't implement GraphNodeModulePath", v)) } - if outside, ok := referrer.(GraphNodeReferenceOutside); ok { + if outside, ok := v.(GraphNodeReferenceOutside); ok { // Vertex makes references to objects in a different module than where // it was declared. _, path := outside.ReferenceOutside() - return path.UnkeyedInstanceShim() + return path } // Vertex makes references to objects in the same module as where it // was declared. - return sp.Path() + return sp.ModulePath() } // referenceMapKey produces keys for the "edges" map. "referrer" is the vertex @@ -347,7 +344,7 @@ func vertexReferencePath(referrer dag.Vertex) addrs.ModuleInstance { // The result is an opaque string that includes both the address of the given // object and the address of the module instance that object belongs to. // -// Only GraphNodeSubPath implementations can be referrers, so this method will +// Only GraphNodeModulePath implementations can be referrers, so this method will // panic if the given vertex does not implement that interface. func (m *ReferenceMap) referenceMapKey(referrer dag.Vertex, addr addrs.Referenceable) string { path := vertexReferencePath(referrer) @@ -362,19 +359,13 @@ func NewReferenceMap(vs []dag.Vertex) *ReferenceMap { // Build the lookup table vertices := make(map[string][]dag.Vertex) for _, v := range vs { - _, ok := v.(GraphNodeSubPath) - if !ok { - // Only nodes with paths can participate in a reference map. - continue - } - // We're only looking for referenceable nodes rn, ok := v.(GraphNodeReferenceable) if !ok { continue } - path := m.vertexReferenceablePath(v) + path := vertexReferenceablePath(v) // Go through and cache them for _, addr := range rn.ReferenceableAddrs() { @@ -389,11 +380,8 @@ func NewReferenceMap(vs []dag.Vertex) *ReferenceMap { // an instance key) or as the bare module call itself (the "module" // block in the parent module that created the instance). callPath, call := addr.Call() - callInstPath, callInst := addr.CallInstance() callKey := m.mapKey(callPath, call) - callInstKey := m.mapKey(callInstPath, callInst) vertices[callKey] = append(vertices[callKey], v) - vertices[callInstKey] = append(vertices[callInstKey], v) } } diff --git a/terraform/transform_reference_test.go b/terraform/transform_reference_test.go index 004dbde485cf..073d02d347af 100644 --- a/terraform/transform_reference_test.go +++ b/terraform/transform_reference_test.go @@ -153,6 +153,10 @@ func (n *graphNodeRefParentTest) Path() addrs.ModuleInstance { return normalizeModulePath(n.PathValue) } +func (n *graphNodeRefParentTest) ModulePath() addrs.Module { + return normalizeModulePath(n.PathValue).Module() +} + type graphNodeRefChildTest struct { NameValue string PathValue []string @@ -179,6 +183,10 @@ func (n *graphNodeRefChildTest) Path() addrs.ModuleInstance { return normalizeModulePath(n.PathValue) } +func (n *graphNodeRefChildTest) ModulePath() addrs.Module { + return normalizeModulePath(n.PathValue).Module() +} + const testTransformRefBasicStr = ` A B