mirror of
https://github.com/hashicorp/terraform.git
synced 2026-05-28 04:03:27 -04:00
Add integration test for stacks action invocation via lifecycle trigger
This commit is contained in:
parent
946918220c
commit
cb3dfa615f
4 changed files with 194 additions and 0 deletions
|
|
@ -301,6 +301,9 @@ func (l *Loader) AddRaw(rawMsg *anypb.Any) error {
|
|||
DeferredReason: deferredReason,
|
||||
})
|
||||
|
||||
case *tfstackdata1.PlanActionInvocationPlanned:
|
||||
// TODO: Implemented in a future apply-related PR.
|
||||
|
||||
default:
|
||||
// Should not get here, because a stack plan can only be loaded by
|
||||
// the same version of Terraform that created it, and the above
|
||||
|
|
|
|||
|
|
@ -1053,3 +1053,139 @@ func mustPlanDynamicValue(t *testing.T, v cty.Value) *tfstackdata1.DynamicValue
|
|||
}
|
||||
return tfstackdata1.Terraform1ToStackDataDynamicValue(ret)
|
||||
}
|
||||
|
||||
func TestPlanning_ActionInvocationLifecycle(t *testing.T) {
|
||||
// This integration test verifies that action invocations with lifecycle
|
||||
// triggers are correctly planned and included in the PlannedChange objects.
|
||||
|
||||
cfg := testStackConfig(t, "planning", "action_lifecycle")
|
||||
componentInstAddr := stackaddrs.AbsComponentInstance{
|
||||
Stack: stackaddrs.RootStackInstance,
|
||||
Item: stackaddrs.ComponentInstance{
|
||||
Component: stackaddrs.Component{
|
||||
Name: "web",
|
||||
},
|
||||
},
|
||||
}
|
||||
actionInstAddr := addrs.AbsActionInstance{
|
||||
Module: addrs.RootModuleInstance,
|
||||
Action: addrs.ActionInstance{
|
||||
Action: addrs.Action{
|
||||
Type: "test_action",
|
||||
Name: "notify",
|
||||
},
|
||||
Key: addrs.NoKey,
|
||||
},
|
||||
}
|
||||
providerAddr := addrs.NewBuiltInProvider("test")
|
||||
providerInstAddr := addrs.AbsProviderConfig{
|
||||
Module: addrs.RootModule,
|
||||
Provider: providerAddr,
|
||||
}
|
||||
|
||||
resourceTypeSchema := &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"value": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
actionTypeSchema := &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"message": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
main := NewForPlanning(cfg, stackstate.NewState(), PlanOpts{
|
||||
PlanningMode: plans.NormalMode,
|
||||
PlanTimestamp: time.Now().UTC(),
|
||||
ProviderFactories: ProviderFactories{
|
||||
addrs.NewBuiltInProvider("test"): func() (providers.Interface, error) {
|
||||
return &providerTesting.MockProvider{
|
||||
GetProviderSchemaResponse: &providers.GetProviderSchemaResponse{
|
||||
Provider: providers.Schema{
|
||||
Body: &configschema.Block{},
|
||||
},
|
||||
ResourceTypes: map[string]providers.Schema{
|
||||
"test_resource": {
|
||||
Body: resourceTypeSchema,
|
||||
},
|
||||
},
|
||||
Actions: map[string]providers.ActionSchema{
|
||||
"test_action": {
|
||||
ConfigSchema: actionTypeSchema,
|
||||
},
|
||||
},
|
||||
},
|
||||
ConfigureProviderFn: func(cpr providers.ConfigureProviderRequest) providers.ConfigureProviderResponse {
|
||||
return providers.ConfigureProviderResponse{}
|
||||
},
|
||||
PlanResourceChangeFn: func(prcr providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
|
||||
return providers.PlanResourceChangeResponse{
|
||||
PlannedState: prcr.ProposedNewState,
|
||||
}
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
outp, outpTest := testPlanOutput(t)
|
||||
main.PlanAll(context.Background(), outp)
|
||||
plan, diags := outpTest.Close(t)
|
||||
assertNoDiagnostics(t, diags)
|
||||
|
||||
cmpPlan := plan.GetComponent(componentInstAddr)
|
||||
if cmpPlan == nil {
|
||||
t.Fatalf("no plan for %s", componentInstAddr)
|
||||
}
|
||||
|
||||
// Verify that we have planned changes for action invocations
|
||||
plannedChanges := outpTest.PlannedChanges()
|
||||
var foundActionChange *stackplan.PlannedChangeActionInvocationInstancePlanned
|
||||
for _, pc := range plannedChanges {
|
||||
if actionChange, ok := pc.(*stackplan.PlannedChangeActionInvocationInstancePlanned); ok {
|
||||
foundActionChange = actionChange
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if foundActionChange == nil {
|
||||
t.Fatalf("no action invocation planned change found; got %d changes", len(plannedChanges))
|
||||
}
|
||||
|
||||
// Verify the action invocation details
|
||||
if got, want := foundActionChange.ActionInvocationAddr.Component.String(), componentInstAddr.String(); got != want {
|
||||
t.Errorf("wrong component instance\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
if got, want := foundActionChange.ActionInvocationAddr.Item.String(), actionInstAddr.String(); got != want {
|
||||
t.Errorf("wrong action instance\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
if got, want := foundActionChange.ProviderConfigAddr.String(), providerInstAddr.String(); got != want {
|
||||
t.Errorf("wrong provider config addr\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
|
||||
// Verify the invocation has the correct trigger type
|
||||
if foundActionChange.Invocation == nil {
|
||||
t.Fatal("invocation is nil")
|
||||
}
|
||||
if _, ok := foundActionChange.Invocation.ActionTrigger.(*plans.LifecycleActionTrigger); !ok {
|
||||
t.Errorf("wrong action trigger type\ngot: %T\nwant: *plans.LifecycleActionTrigger", foundActionChange.Invocation.ActionTrigger)
|
||||
}
|
||||
|
||||
// Verify we can convert to proto successfully
|
||||
protoChange, err := foundActionChange.PlannedChangeProto()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to convert to proto: %s", err)
|
||||
}
|
||||
if protoChange == nil {
|
||||
t.Fatal("proto change is nil")
|
||||
}
|
||||
if len(protoChange.Descriptions) == 0 {
|
||||
t.Error("expected at least one description in proto change")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
# Copyright (c) HashiCorp, Inc.
|
||||
# SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
required_providers {
|
||||
test = {
|
||||
source = "terraform.io/builtin/test"
|
||||
}
|
||||
}
|
||||
|
||||
provider "test" "main" {
|
||||
}
|
||||
|
||||
component "web" {
|
||||
source = "./module_web"
|
||||
|
||||
providers = {
|
||||
test = provider.test.main
|
||||
}
|
||||
}
|
||||
|
||||
output "result" {
|
||||
type = string
|
||||
value = component.web.result
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
terraform {
|
||||
required_providers {
|
||||
test = {
|
||||
source = "terraform.io/builtin/test"
|
||||
|
||||
configuration_aliases = [ test ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action "test_action" "notify" {
|
||||
config {
|
||||
message = "resource created"
|
||||
}
|
||||
}
|
||||
|
||||
resource "test_resource" "main" {
|
||||
value = "example"
|
||||
|
||||
lifecycle {
|
||||
action_trigger {
|
||||
events = [after_create]
|
||||
actions = [action.test_action.notify]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output "result" {
|
||||
value = test_resource.main.value
|
||||
}
|
||||
Loading…
Reference in a new issue