diff --git a/internal/terraform/context_apply_action_test.go b/internal/terraform/context_apply_action_test.go index b8bee939e7..d75ea81c1b 100644 --- a/internal/terraform/context_apply_action_test.go +++ b/internal/terraform/context_apply_action_test.go @@ -404,9 +404,9 @@ resource "test_object" "b" { _, diags = ctx.Apply(plan, m, nil) if tc.expectDiagnostics.HasErrors() { tfdiags.AssertDiagnosticsMatch(t, diags, tc.expectDiagnostics) - return + } else { + tfdiags.AssertNoDiagnostics(t, diags) } - tfdiags.AssertNoDiagnostics(t, diags) if tc.expectInvokeActionCalled && len(invokeActionCalls) == 0 { t.Fatalf("expected invoke action to be called, but it was not") diff --git a/internal/terraform/node_action_apply.go b/internal/terraform/node_action_apply.go index ee970692dd..cc7a8dddd7 100644 --- a/internal/terraform/node_action_apply.go +++ b/internal/terraform/node_action_apply.go @@ -21,9 +21,10 @@ type nodeActionApply struct { } var ( - _ GraphNodeExecutable = (*nodeActionApply)(nil) - _ GraphNodeReferencer = (*nodeActionApply)(nil) - _ dag.GraphNodeDotter = (*nodeActionApply)(nil) + _ GraphNodeExecutable = (*nodeActionApply)(nil) + _ GraphNodeReferencer = (*nodeActionApply)(nil) + _ dag.GraphNodeDotter = (*nodeActionApply)(nil) + _ GraphNodeActionProviders = (*nodeActionApply)(nil) ) func (n *nodeActionApply) Name() string { @@ -154,3 +155,11 @@ func (n *nodeActionApply) References() []*addrs.Reference { return refs } + +func (n *nodeActionApply) ActionProviders() []addrs.AbsProviderConfig { + ret := []addrs.AbsProviderConfig{} + for _, invocation := range n.ActionInvocations { + ret = append(ret, invocation.ProviderAddr) + } + return ret +} diff --git a/internal/terraform/node_resource_apply_instance.go b/internal/terraform/node_resource_apply_instance.go index d22da735a1..f3f533954a 100644 --- a/internal/terraform/node_resource_apply_instance.go +++ b/internal/terraform/node_resource_apply_instance.go @@ -41,14 +41,14 @@ type NodeApplyableResourceInstance struct { } var ( - _ GraphNodeConfigResource = (*NodeApplyableResourceInstance)(nil) - _ GraphNodeResourceInstance = (*NodeApplyableResourceInstance)(nil) - _ GraphNodeCreator = (*NodeApplyableResourceInstance)(nil) - _ GraphNodeReferencer = (*NodeApplyableResourceInstance)(nil) - _ GraphNodeDeposer = (*NodeApplyableResourceInstance)(nil) - _ GraphNodeExecutable = (*NodeApplyableResourceInstance)(nil) - _ GraphNodeAttachDependencies = (*NodeApplyableResourceInstance)(nil) - _ GraphNodeAttachBeforeActions = (*NodeApplyableResourceInstance)(nil) + _ GraphNodeConfigResource = (*NodeApplyableResourceInstance)(nil) + _ GraphNodeResourceInstance = (*NodeApplyableResourceInstance)(nil) + _ GraphNodeCreator = (*NodeApplyableResourceInstance)(nil) + _ GraphNodeReferencer = (*NodeApplyableResourceInstance)(nil) + _ GraphNodeDeposer = (*NodeApplyableResourceInstance)(nil) + _ GraphNodeExecutable = (*NodeApplyableResourceInstance)(nil) + _ GraphNodeAttachDependencies = (*NodeApplyableResourceInstance)(nil) + _ GraphNodeActionProviders = (*NodeApplyableResourceInstance)(nil) ) // GraphNodeCreator @@ -475,8 +475,12 @@ func (n *NodeApplyableResourceInstance) checkPlannedChange(ctx EvalContext, plan return diags } -func (n *NodeApplyableResourceInstance) AttachBeforeActions(ais []*plans.ActionInvocationInstance) { - n.beforeActionInvocations = ais +func (n *NodeApplyableResourceInstance) ActionProviders() []addrs.AbsProviderConfig { + ret := []addrs.AbsProviderConfig{} + for _, ai := range n.beforeActionInvocations { + ret = append(ret, ai.ProviderAddr) + } + return ret } // maybeTainted takes the resource addr, new value, planned change, and possible diff --git a/internal/terraform/node_resource_plan.go b/internal/terraform/node_resource_plan.go index 2a6e3590bd..98c7f7b4f6 100644 --- a/internal/terraform/node_resource_plan.go +++ b/internal/terraform/node_resource_plan.go @@ -61,6 +61,7 @@ var ( _ GraphNodeAttachResourceConfig = (*nodeExpandPlannableResource)(nil) _ GraphNodeAttachDependencies = (*nodeExpandPlannableResource)(nil) _ GraphNodeTargetable = (*nodeExpandPlannableResource)(nil) + _ GraphNodeActionProviders = (*nodeExpandPlannableResource)(nil) ) func (n *nodeExpandPlannableResource) Name() string { @@ -681,3 +682,38 @@ func (n *nodeExpandPlannableResource) validForceReplaceTargets(instanceAddrs []a return diags } + +// GraphNodeActionProviders +func (n *nodeExpandPlannableResource) ActionProviders() []addrs.AbsProviderConfig { + providers := []addrs.AbsProviderConfig{} + if n.Config == nil || n.Config.Managed == nil || n.Config.Managed.ActionTriggers == nil { + return providers + } + + for _, at := range n.Config.Managed.ActionTriggers { + for _, actionRef := range at.Actions { + ref, diags := addrs.ParseRef(actionRef.Traversal) + if diags.HasErrors() { + // This should have been validated before, so we panic here + panic(fmt.Sprintf("failed to parse action trigger reference %s: %s", actionRef.Traversal, diags.Err())) + } + var provider addrs.Provider + // TODO: This needs to take the provider field into account once we support it + if a, ok := ref.Subject.(addrs.ActionInstance); ok { + provider = addrs.ImpliedProviderForUnqualifiedType(a.Action.ImpliedProvider()) + } else if a, ok := ref.Subject.(addrs.Action); ok { + provider = addrs.ImpliedProviderForUnqualifiedType(a.ImpliedProvider()) + } else { + // This should have been validated before, so we panic here + panic(fmt.Sprintf("action trigger %s refers to an invalid address", actionRef.Traversal)) + } + + providers = append(providers, addrs.AbsProviderConfig{ + Provider: provider, + Module: n.ModulePath(), + }) + } + } + + return providers +} diff --git a/internal/terraform/node_resource_plan_instance.go b/internal/terraform/node_resource_plan_instance.go index 41498f15fd..85802d5387 100644 --- a/internal/terraform/node_resource_plan_instance.go +++ b/internal/terraform/node_resource_plan_instance.go @@ -623,7 +623,7 @@ func (n *NodePlannableResourceInstance) planActionTriggers(ctx EvalContext, chan if err != nil { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, - "Failed to get provider", + fmt.Sprintf("Failed to get provider for action %s", absActionAddr), fmt.Sprintf("Failed to get provider: %s", err), )) return diags diff --git a/internal/terraform/transform_provider.go b/internal/terraform/transform_provider.go index a34b51ca6f..30467eefbf 100644 --- a/internal/terraform/transform_provider.go +++ b/internal/terraform/transform_provider.go @@ -87,6 +87,10 @@ type GraphNodeProviderConsumer interface { SetProvider(addrs.AbsProviderConfig) } +type GraphNodeActionProviders interface { + ActionProviders() []addrs.AbsProviderConfig +} + // ProviderTransformer is a GraphTransformer that maps resources to providers // within the graph. This will error if there are any resources that don't map // to proper resources. @@ -306,6 +310,22 @@ func (t *CloseProviderTransformer) Transform(g *Graph) error { g.Connect(dag.BasicEdge(closer, v)) } + // Now look at all action provider consumers and connect them to the appropriate closers. + for _, v := range g.Vertices() { + apc, ok := v.(GraphNodeActionProviders) + if !ok { + continue + } + + for _, provider := range apc.ActionProviders() { + closer, ok := cpm[provider.String()] + if !ok { + return fmt.Errorf("no graphNodeCloseProvider for %s", provider) + } + g.Connect(dag.BasicEdge(closer, v)) + } + } + return err }