From eb26b72d55f4a5b99d172e551bc02e4d7327d725 Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Mon, 28 Jul 2025 15:56:20 +0200 Subject: [PATCH] display lifecycle triggered actions aside their triggering resources --- internal/command/jsonformat/diff.go | 38 ++- internal/command/jsonformat/plan.go | 37 ++- .../command/jsonplan/action_invocations.go | 63 ++++- internal/command/jsonplan/plan.go | 4 +- internal/plans/action_invocation.go | 17 +- internal/plans/changes.go | 10 +- internal/plans/changes_src.go | 28 ++- internal/plans/planproto/planfile.pb.go | 222 +++++++++--------- internal/plans/planproto/planfile.proto | 9 +- internal/schemarepo/schemas.go | 8 + internal/terraform/node_action_apply.go | 42 +++- .../terraform/node_resource_apply_instance.go | 2 +- internal/terraform/transform_diff.go | 19 +- 13 files changed, 351 insertions(+), 148 deletions(-) diff --git a/internal/command/jsonformat/diff.go b/internal/command/jsonformat/diff.go index 402a414d84..9c035f9af9 100644 --- a/internal/command/jsonformat/diff.go +++ b/internal/command/jsonformat/diff.go @@ -4,6 +4,9 @@ package jsonformat import ( + "fmt" + "slices" + "github.com/hashicorp/terraform/internal/command/jsonformat/computed" "github.com/hashicorp/terraform/internal/command/jsonformat/differ" "github.com/hashicorp/terraform/internal/command/jsonformat/structured" @@ -56,9 +59,34 @@ func precomputeDiffs(plan Plan, mode plans.Mode) diffs { for _, change := range plan.ResourceChanges { schema := plan.getSchema(change) structuredChange := structured.FromJsonChange(change.Change, attribute_path.AlwaysMatcher()) + + beforeActionsTriggered := []jsonplan.ActionInvocation{} + afterActionsTriggered := []jsonplan.ActionInvocation{} + + for _, action := range plan.ActionInvocations { + if action.TriggeringResourceAddress != change.Address { + continue + } + + switch action.TriggerEvent { + case "BeforeCreate", "BeforeUpdate", "BeforeDestroy": + beforeActionsTriggered = append(beforeActionsTriggered, action) + case "AfterCreate", "AfterUpdate", "AfterDestroy": + afterActionsTriggered = append(afterActionsTriggered, action) + default: + // The switch should be exhaustive. + panic(fmt.Sprintf("Unexpected triggering event when rendering action %s", action.TriggerEvent)) + } + } + + slices.SortFunc(beforeActionsTriggered, jsonplan.ActionInvocationCompare) + slices.SortFunc(afterActionsTriggered, jsonplan.ActionInvocationCompare) + diffs.changes = append(diffs.changes, diff{ - change: change, - diff: differ.ComputeDiffForBlock(structuredChange, schema.Block), + change: change, + diff: differ.ComputeDiffForBlock(structuredChange, schema.Block), + beforeActionsTriggered: beforeActionsTriggered, + afterActionsTriggered: afterActionsTriggered, }) } @@ -106,8 +134,10 @@ func (d diffs) Empty() bool { } type diff struct { - change jsonplan.ResourceChange - diff computed.Diff + change jsonplan.ResourceChange + diff computed.Diff + beforeActionsTriggered []jsonplan.ActionInvocation + afterActionsTriggered []jsonplan.ActionInvocation } func (d diff) Moved() bool { diff --git a/internal/command/jsonformat/plan.go b/internal/command/jsonformat/plan.go index 090346dff6..c816320d82 100644 --- a/internal/command/jsonformat/plan.go +++ b/internal/command/jsonformat/plan.go @@ -393,6 +393,31 @@ func renderHumanDiff(renderer Renderer, diff diff, cause string) (string, bool) opts.ShowUnchangedChildren = diff.Importing() buf.WriteString(fmt.Sprintf("%s %s %s", renderer.Colorize.Color(format.DiffActionSymbol(action)), resourceChangeHeader(diff.change), diff.diff.RenderHuman(0, opts))) + + if len(diff.beforeActionsTriggered) > 0 { + buf.WriteString(renderer.Colorize.Color("\n\n [bold]# Actions to be invoked before this change in order:[reset]\n")) + for _, ai := range diff.beforeActionsTriggered { + buf.WriteString(fmt.Sprintf(" # - %s\n", ai.Address)) + } + } + + if len(diff.afterActionsTriggered) > 0 { + buf.WriteString(renderer.Colorize.Color("\n\n [bold]# Actions to be invoked after this change in order:[reset]\n")) + for _, ai := range diff.afterActionsTriggered { + // TODO: Save the type and name in jsonplan.ActionInvocation for easy access + buf.WriteString(fmt.Sprintf(" %s {\n", ai.Address)) + if len(ai.ConfigValues) > 0 { + buf.WriteString(" config {\n") + for key, value := range ai.ConfigValues { + // TODO: How do I properly render this? + buf.WriteString(fmt.Sprintf(" %s = %s\n", renderers.EnsureValidAttributeName(key), value)) + } + buf.WriteString(" }") + } + buf.WriteString(" }\n") + } + } + return buf.String(), true } @@ -438,16 +463,10 @@ func renderHumanDeferredDiff(renderer Renderer, deferred deferredDiff) (string, return buf.String(), true } +// All actions that run based on the resource lifecycle should be rendered as part of the resource +// changes, therefore this function only renders actions that are invoked by the CLI func renderHumanActionInvocations(renderer Renderer, actionInvocations []jsonplan.ActionInvocation) string { - var buf bytes.Buffer - for _, action := range actionInvocations { - buf.WriteString(renderer.Colorize.Color(fmt.Sprintf( - "[bold] # Triggered by %s[reset]\n", - action.TriggeringResourceAddress, - ))) - buf.WriteString(fmt.Sprintf(" %s\n", action.Address)) - } - return buf.String() + return "" // TODO: We will use this function once we support CLI invoked actions. } func resourceChangeComment(resource jsonplan.ResourceChange, action plans.Action, changeCause string) string { diff --git a/internal/command/jsonplan/action_invocations.go b/internal/command/jsonplan/action_invocations.go index d2117a0703..cfcc06603d 100644 --- a/internal/command/jsonplan/action_invocations.go +++ b/internal/command/jsonplan/action_invocations.go @@ -4,12 +4,23 @@ package jsonplan import ( + "fmt" + "github.com/hashicorp/terraform/internal/plans" + "github.com/hashicorp/terraform/internal/terraform" + "github.com/zclconf/go-cty/cty" ) type ActionInvocation struct { // Address is the absolute action address Address string `json:"address,omitempty"` + // Type is the type of the action + Type string `json:"type,omitempty"` + // Name is the name of the action + Name string `json:"name,omitempty"` + + // ConfigValues is the JSON representation of the values in the config block of the action + ConfigValues attributeValues `json:"config_values,omitempty"` // ProviderName allows the property "type" to be interpreted unambiguously // in the unusual situation where a provider offers a type whose @@ -30,13 +41,55 @@ type ActionInvocation struct { TriggerEvent string `json:"trigger_event,omitempty"` } -func MarshalActionInvocations(actions []*plans.ActionInvocationInstanceSrc) ([]ActionInvocation, error) { +func ActionInvocationCompare(a, b ActionInvocation) int { + if a.TriggeringResourceAddress < b.TriggeringResourceAddress { + return -1 + } else if a.TriggeringResourceAddress > b.TriggeringResourceAddress { + return 1 + } + + if a.ActionTriggerBlockIndex != nil && b.ActionTriggerBlockIndex != nil { + if *a.ActionTriggerBlockIndex < *b.ActionTriggerBlockIndex { + return -1 + } else if *a.ActionTriggerBlockIndex > *b.ActionTriggerBlockIndex { + return 1 + } + } + + if a.ActionsListIndex != nil && b.ActionsListIndex != nil { + if *a.ActionsListIndex < *b.ActionsListIndex { + return -1 + + } else if *a.ActionsListIndex > *b.ActionsListIndex { + return 1 + } + } + + return 0 +} + +func MarshalActionInvocations(actions []*plans.ActionInvocationInstanceSrc, schemas *terraform.Schemas) ([]ActionInvocation, error) { ret := make([]ActionInvocation, 0, len(actions)) for _, action := range actions { + schema := schemas.ActionTypeConfig( + action.ProviderAddr.Provider, + action.Addr.Action.Action.Type, + ) + if schema.ConfigSchema == nil { + return ret, fmt.Errorf("no schema found for %s (in provider %s)", action.Addr.Action.Action.Type, action.ProviderAddr.Provider) + } + + actionDec, err := action.Decode(&schema) + if err != nil { + return ret, fmt.Errorf("failed to decode action %s: %w", action.Addr, err) + } + ai := ActionInvocation{ Address: action.Addr.String(), + Type: action.Addr.Action.Action.Type, + Name: action.Addr.Action.Action.Name, ProviderName: action.ProviderAddr.String(), // These fields are only used for non-CLI actions. We will need to find another format @@ -47,6 +100,14 @@ func MarshalActionInvocations(actions []*plans.ActionInvocationInstanceSrc) ([]A TriggerEvent: action.TriggerEvent.String(), } + if actionDec.ConfigValue != cty.NilVal { + if actionDec.ConfigValue.IsWhollyKnown() { + ai.ConfigValues = marshalAttributeValues(actionDec.ConfigValue) + } else { + knowns := omitUnknowns(actionDec.ConfigValue) + ai.ConfigValues = marshalAttributeValues(knowns) + } + } ret = append(ret, ai) } diff --git a/internal/command/jsonplan/plan.go b/internal/command/jsonplan/plan.go index 4604289931..5256a52515 100644 --- a/internal/command/jsonplan/plan.go +++ b/internal/command/jsonplan/plan.go @@ -239,7 +239,7 @@ func MarshalForRenderer( return nil, nil, nil, nil, nil, err } - if output.ActionInvocations, err = MarshalActionInvocations(p.Changes.ActionInvocations); err != nil { + if output.ActionInvocations, err = MarshalActionInvocations(p.Changes.ActionInvocations, schemas); err != nil { return nil, nil, nil, nil, nil, err } @@ -338,7 +338,7 @@ func Marshal( // output.Changes.ActionInvocations if p.Changes.ActionInvocations != nil { - if output.ActionInvocations, err = MarshalActionInvocations(p.Changes.ActionInvocations); err != nil { + if output.ActionInvocations, err = MarshalActionInvocations(p.Changes.ActionInvocations, schemas); err != nil { return nil, fmt.Errorf("error marshaling action invocations: %s", err) } } diff --git a/internal/plans/action_invocation.go b/internal/plans/action_invocation.go index 235dc2bbca..06059d818b 100644 --- a/internal/plans/action_invocation.go +++ b/internal/plans/action_invocation.go @@ -6,6 +6,8 @@ package plans import ( "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs" + "github.com/hashicorp/terraform/internal/providers" + "github.com/zclconf/go-cty/cty" ) type ActionInvocationInstance struct { @@ -24,12 +26,24 @@ type ActionInvocationInstance struct { // to plan this action, and thus the configuration that must also be // used to apply it. ProviderAddr addrs.AbsProviderConfig + + ConfigValue cty.Value } // Encode produces a variant of the receiver that has its change values // serialized so it can be written to a plan file. Pass the implied type of the // corresponding resource type schema for correct operation. -func (ai *ActionInvocationInstance) Encode() (*ActionInvocationInstanceSrc, error) { +func (ai *ActionInvocationInstance) Encode(schema *providers.ActionSchema) (*ActionInvocationInstanceSrc, error) { + ty := cty.DynamicPseudoType + if schema != nil { + ty = schema.ConfigSchema.ImpliedType() + } + + configValue, err := NewDynamicValue(ai.ConfigValue, ty) + if err != nil { + return nil, err + } + return &ActionInvocationInstanceSrc{ Addr: ai.Addr, TriggeringResourceAddr: ai.TriggeringResourceAddr, @@ -37,6 +51,7 @@ func (ai *ActionInvocationInstance) Encode() (*ActionInvocationInstanceSrc, erro ActionTriggerBlockIndex: ai.ActionTriggerBlockIndex, ActionsListIndex: ai.ActionsListIndex, ProviderAddr: ai.ProviderAddr, + ConfigValue: configValue, }, nil } diff --git a/internal/plans/changes.go b/internal/plans/changes.go index f827edb33a..c4321908a0 100644 --- a/internal/plans/changes.go +++ b/internal/plans/changes.go @@ -99,7 +99,15 @@ func (c *Changes) Encode(schemas *schemarepo.Schemas) (*ChangesSrc, error) { } for _, ai := range c.ActionInvocations { - a, err := ai.Encode() + p, ok := schemas.Providers[ai.ProviderAddr.Provider] + if !ok { + return changesSrc, fmt.Errorf("Changes.Encode: missing provider %s for %s", ai.ProviderAddr, ai.Addr) + } + schema, ok := p.Actions[ai.Addr.Action.Action.Type] + if !ok { + return changesSrc, fmt.Errorf("Changes.Encode: missing schema for %s", ai.Addr.Action.Action.Type) + } + a, err := ai.Encode(&schema) if err != nil { return changesSrc, fmt.Errorf("Changes.Encode: %w", err) } diff --git a/internal/plans/changes_src.go b/internal/plans/changes_src.go index 35b523b0b4..112923b78f 100644 --- a/internal/plans/changes_src.go +++ b/internal/plans/changes_src.go @@ -161,7 +161,16 @@ func (c *ChangesSrc) Decode(schemas *schemarepo.Schemas) (*Changes, error) { } for _, ais := range c.ActionInvocations { - action, err := ais.Decode() + p, ok := schemas.Providers[ais.ProviderAddr.Provider] + if !ok { + return nil, fmt.Errorf("ChangesSrc.Decode: missing provider %s for %s", ais.ProviderAddr, ais.Addr) + } + schema, ok := p.Actions[ais.Addr.Action.Action.Type] + if !ok { + return nil, fmt.Errorf("ChangesSrc.Decode: missing schema for %s", ais.Addr.Action.Action.Type) + } + + action, err := ais.Decode(&schema) if err != nil { return nil, err } @@ -563,11 +572,24 @@ type ActionInvocationInstanceSrc struct { ActionTriggerBlockIndex int ActionsListIndex int + ConfigValue DynamicValue + ProviderAddr addrs.AbsProviderConfig } // Decode unmarshals the raw representation of any linked resources. -func (acs *ActionInvocationInstanceSrc) Decode() (*ActionInvocationInstance, error) { +func (acs *ActionInvocationInstanceSrc) Decode(schema *providers.ActionSchema) (*ActionInvocationInstance, error) { + + ty := cty.DynamicPseudoType + if schema != nil { + ty = schema.ConfigSchema.ImpliedType() + } + + config, err := acs.ConfigValue.Decode(ty) + if err != nil { + return nil, fmt.Errorf("error decoding 'config' value: %s", err) + } + ai := &ActionInvocationInstance{ Addr: acs.Addr, TriggeringResourceAddr: acs.TriggeringResourceAddr, @@ -575,6 +597,7 @@ func (acs *ActionInvocationInstanceSrc) Decode() (*ActionInvocationInstance, err ActionTriggerBlockIndex: acs.ActionTriggerBlockIndex, ActionsListIndex: acs.ActionsListIndex, ProviderAddr: acs.ProviderAddr, + ConfigValue: config, } return ai, nil } @@ -584,5 +607,6 @@ func (acs *ActionInvocationInstanceSrc) DeepCopy() *ActionInvocationInstanceSrc return acs } ret := *acs + ret.ConfigValue = ret.ConfigValue.Copy() return &ret } diff --git a/internal/plans/planproto/planfile.pb.go b/internal/plans/planproto/planfile.pb.go index 9de1d98b4f..e04f010d2e 100644 --- a/internal/plans/planproto/planfile.pb.go +++ b/internal/plans/planproto/planfile.pb.go @@ -1654,10 +1654,11 @@ type ActionInvocationInstance struct { // apply it. Provider string `protobuf:"bytes,2,opt,name=provider,proto3" json:"provider,omitempty"` LinkedResources []*ResourceInstanceActionChange `protobuf:"bytes,3,rep,name=linked_resources,json=linkedResources,proto3" json:"linked_resources,omitempty"` - TriggeringResourceAddr string `protobuf:"bytes,4,opt,name=triggering_resource_addr,json=triggeringResourceAddr,proto3" json:"triggering_resource_addr,omitempty"` - TriggerEvent ActionTriggerEvent `protobuf:"varint,5,opt,name=trigger_event,json=triggerEvent,proto3,enum=tfplan.ActionTriggerEvent" json:"trigger_event,omitempty"` - ActionTriggerBlockIndex int64 `protobuf:"varint,6,opt,name=action_trigger_block_index,json=actionTriggerBlockIndex,proto3" json:"action_trigger_block_index,omitempty"` - ActionsListIndex int64 `protobuf:"varint,7,opt,name=actions_list_index,json=actionsListIndex,proto3" json:"actions_list_index,omitempty"` + ConfigValue *DynamicValue `protobuf:"bytes,4,opt,name=config_value,json=configValue,proto3" json:"config_value,omitempty"` + TriggeringResourceAddr string `protobuf:"bytes,5,opt,name=triggering_resource_addr,json=triggeringResourceAddr,proto3" json:"triggering_resource_addr,omitempty"` + TriggerEvent ActionTriggerEvent `protobuf:"varint,6,opt,name=trigger_event,json=triggerEvent,proto3,enum=tfplan.ActionTriggerEvent" json:"trigger_event,omitempty"` + ActionTriggerBlockIndex int64 `protobuf:"varint,7,opt,name=action_trigger_block_index,json=actionTriggerBlockIndex,proto3" json:"action_trigger_block_index,omitempty"` + ActionsListIndex int64 `protobuf:"varint,8,opt,name=actions_list_index,json=actionsListIndex,proto3" json:"actions_list_index,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1713,6 +1714,13 @@ func (x *ActionInvocationInstance) GetLinkedResources() []*ResourceInstanceActio return nil } +func (x *ActionInvocationInstance) GetConfigValue() *DynamicValue { + if x != nil { + return x.ConfigValue + } + return nil +} + func (x *ActionInvocationInstance) GetTriggeringResourceAddr() string { if x != nil { return x.TriggeringResourceAddr @@ -2233,7 +2241,7 @@ var file_planfile_proto_rawDesc = string([]byte{ 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x44, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x06, - 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x81, 0x03, 0x0a, 0x18, 0x41, 0x63, 0x74, 0x69, 0x6f, + 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0xba, 0x03, 0x0a, 0x18, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, @@ -2243,95 +2251,98 @@ var file_planfile_proto_rawDesc = string([]byte{ 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0f, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x18, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x69, - 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x69, - 0x6e, 0x67, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x64, 0x64, 0x72, 0x12, 0x3f, - 0x0a, 0x0d, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x41, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x52, 0x0c, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, - 0x3b, 0x0a, 0x1a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, - 0x72, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x17, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x69, 0x67, 0x67, - 0x65, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x2c, 0x0a, 0x12, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x64, - 0x65, 0x78, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x7b, 0x0a, 0x1c, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, - 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x1f, - 0x0a, 0x0b, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x12, - 0x26, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, - 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2a, 0x31, 0x0a, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x12, - 0x0a, 0x0a, 0x06, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x44, - 0x45, 0x53, 0x54, 0x52, 0x4f, 0x59, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x52, 0x45, 0x46, 0x52, - 0x45, 0x53, 0x48, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x02, 0x2a, 0x94, 0x01, 0x0a, 0x06, 0x41, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4f, 0x50, 0x10, 0x00, 0x12, - 0x0a, 0x0a, 0x06, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x52, - 0x45, 0x41, 0x44, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, - 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x05, 0x12, 0x16, 0x0a, - 0x12, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4e, 0x5f, 0x43, 0x52, 0x45, - 0x41, 0x54, 0x45, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x5f, - 0x54, 0x48, 0x45, 0x4e, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x07, 0x12, 0x0a, 0x0a, - 0x06, 0x46, 0x4f, 0x52, 0x47, 0x45, 0x54, 0x10, 0x08, 0x12, 0x16, 0x0a, 0x12, 0x43, 0x52, 0x45, - 0x41, 0x54, 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4e, 0x5f, 0x46, 0x4f, 0x52, 0x47, 0x45, 0x54, 0x10, - 0x09, 0x2a, 0xc8, 0x03, 0x0a, 0x1c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61, 0x73, - 0x6f, 0x6e, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, - 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, - 0x54, 0x41, 0x49, 0x4e, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x45, 0x50, - 0x4c, 0x41, 0x43, 0x45, 0x5f, 0x42, 0x59, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x10, - 0x02, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x5f, 0x42, 0x45, 0x43, - 0x41, 0x55, 0x53, 0x45, 0x5f, 0x43, 0x41, 0x4e, 0x4e, 0x4f, 0x54, 0x5f, 0x55, 0x50, 0x44, 0x41, - 0x54, 0x45, 0x10, 0x03, 0x12, 0x25, 0x0a, 0x21, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, - 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x4e, 0x4f, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, - 0x43, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x04, 0x12, 0x23, 0x0a, 0x1f, 0x44, - 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x57, 0x52, - 0x4f, 0x4e, 0x47, 0x5f, 0x52, 0x45, 0x50, 0x45, 0x54, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x05, - 0x12, 0x1e, 0x0a, 0x1a, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, - 0x53, 0x45, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x49, 0x4e, 0x44, 0x45, 0x58, 0x10, 0x06, - 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, - 0x53, 0x45, 0x5f, 0x45, 0x41, 0x43, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x07, 0x12, 0x1c, 0x0a, - 0x18, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, - 0x4e, 0x4f, 0x5f, 0x4d, 0x4f, 0x44, 0x55, 0x4c, 0x45, 0x10, 0x08, 0x12, 0x17, 0x0a, 0x13, 0x52, - 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x5f, 0x42, 0x59, 0x5f, 0x54, 0x52, 0x49, 0x47, 0x47, 0x45, - 0x52, 0x53, 0x10, 0x09, 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x42, 0x45, 0x43, - 0x41, 0x55, 0x53, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, - 0x4f, 0x57, 0x4e, 0x10, 0x0a, 0x12, 0x23, 0x0a, 0x1f, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x42, 0x45, - 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x44, 0x45, 0x50, 0x45, 0x4e, 0x44, 0x45, 0x4e, 0x43, 0x59, - 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x0b, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, - 0x41, 0x44, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, - 0x5f, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, 0x10, 0x0d, 0x12, 0x21, 0x0a, 0x1d, 0x44, 0x45, 0x4c, - 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x4e, 0x4f, 0x5f, 0x4d, - 0x4f, 0x56, 0x45, 0x5f, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x10, 0x0c, 0x2a, 0x9b, 0x01, 0x0a, - 0x0e, 0x44, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, - 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, - 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x55, - 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x53, 0x4f, - 0x55, 0x52, 0x43, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, - 0x4f, 0x57, 0x4e, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, - 0x52, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, - 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x42, 0x53, 0x45, 0x4e, 0x54, 0x5f, 0x50, 0x52, 0x45, - 0x52, 0x45, 0x51, 0x10, 0x04, 0x12, 0x13, 0x0a, 0x0f, 0x44, 0x45, 0x46, 0x45, 0x52, 0x52, 0x45, - 0x44, 0x5f, 0x50, 0x52, 0x45, 0x52, 0x45, 0x51, 0x10, 0x05, 0x2a, 0xa1, 0x01, 0x0a, 0x12, 0x41, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x45, 0x56, 0x45, - 0x4e, 0x54, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x42, 0x45, 0x46, 0x4f, 0x52, 0x45, 0x5f, 0x43, - 0x45, 0x52, 0x41, 0x54, 0x45, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x41, 0x46, 0x54, 0x45, 0x52, - 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x42, 0x45, 0x46, - 0x4f, 0x52, 0x45, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x03, 0x12, 0x10, 0x0a, 0x0c, - 0x41, 0x46, 0x54, 0x45, 0x52, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x04, 0x12, 0x12, - 0x0a, 0x0e, 0x42, 0x45, 0x46, 0x4f, 0x52, 0x45, 0x5f, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4f, 0x59, - 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x46, 0x54, 0x45, 0x52, 0x5f, 0x44, 0x45, 0x53, 0x54, - 0x52, 0x4f, 0x59, 0x10, 0x06, 0x12, 0x07, 0x0a, 0x03, 0x43, 0x4c, 0x49, 0x10, 0x07, 0x42, 0x39, - 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, - 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, - 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x61, 0x6e, 0x73, 0x2f, - 0x70, 0x6c, 0x61, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x72, 0x63, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x61, 0x6e, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x38, 0x0a, + 0x18, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x16, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x41, 0x64, 0x64, 0x72, 0x12, 0x3f, 0x0a, 0x0d, 0x74, 0x72, 0x69, 0x67, 0x67, + 0x65, 0x72, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, + 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, + 0x69, 0x67, 0x67, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0c, 0x74, 0x72, 0x69, 0x67, + 0x67, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x3b, 0x0a, 0x1a, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x17, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x2c, 0x0a, 0x12, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x10, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x22, 0x7b, 0x0a, 0x1c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x70, 0x6f, 0x73, + 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, + 0x70, 0x6f, 0x73, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, + 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x2a, 0x31, 0x0a, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x4f, 0x52, 0x4d, + 0x41, 0x4c, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4f, 0x59, 0x10, + 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x52, 0x45, 0x46, 0x52, 0x45, 0x53, 0x48, 0x5f, 0x4f, 0x4e, 0x4c, + 0x59, 0x10, 0x02, 0x2a, 0x94, 0x01, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x08, + 0x0a, 0x04, 0x4e, 0x4f, 0x4f, 0x50, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x52, 0x45, 0x41, + 0x54, 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x52, 0x45, 0x41, 0x44, 0x10, 0x02, 0x12, 0x0a, + 0x0a, 0x06, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, + 0x4c, 0x45, 0x54, 0x45, 0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, + 0x5f, 0x54, 0x48, 0x45, 0x4e, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x06, 0x12, 0x16, + 0x0a, 0x12, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4e, 0x5f, 0x44, 0x45, + 0x4c, 0x45, 0x54, 0x45, 0x10, 0x07, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x4f, 0x52, 0x47, 0x45, 0x54, + 0x10, 0x08, 0x12, 0x16, 0x0a, 0x12, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x48, 0x45, + 0x4e, 0x5f, 0x46, 0x4f, 0x52, 0x47, 0x45, 0x54, 0x10, 0x09, 0x2a, 0xc8, 0x03, 0x0a, 0x1c, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x08, 0x0a, 0x04, 0x4e, + 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, + 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x54, 0x41, 0x49, 0x4e, 0x54, 0x45, 0x44, + 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x5f, 0x42, 0x59, + 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x10, 0x02, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, + 0x50, 0x4c, 0x41, 0x43, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x43, 0x41, + 0x4e, 0x4e, 0x4f, 0x54, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x03, 0x12, 0x25, 0x0a, + 0x21, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, + 0x4e, 0x4f, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, + 0x49, 0x47, 0x10, 0x04, 0x12, 0x23, 0x0a, 0x1f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, + 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x57, 0x52, 0x4f, 0x4e, 0x47, 0x5f, 0x52, 0x45, 0x50, + 0x45, 0x54, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x05, 0x12, 0x1e, 0x0a, 0x1a, 0x44, 0x45, 0x4c, + 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x43, 0x4f, 0x55, 0x4e, + 0x54, 0x5f, 0x49, 0x4e, 0x44, 0x45, 0x58, 0x10, 0x06, 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x45, 0x4c, + 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x45, 0x41, 0x43, 0x48, + 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x07, 0x12, 0x1c, 0x0a, 0x18, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, + 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x4e, 0x4f, 0x5f, 0x4d, 0x4f, 0x44, 0x55, + 0x4c, 0x45, 0x10, 0x08, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x5f, + 0x42, 0x59, 0x5f, 0x54, 0x52, 0x49, 0x47, 0x47, 0x45, 0x52, 0x53, 0x10, 0x09, 0x12, 0x1f, 0x0a, + 0x1b, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x43, 0x4f, + 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x0a, 0x12, 0x23, + 0x0a, 0x1f, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x44, + 0x45, 0x50, 0x45, 0x4e, 0x44, 0x45, 0x4e, 0x43, 0x59, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, + 0x47, 0x10, 0x0b, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x42, 0x45, 0x43, 0x41, + 0x55, 0x53, 0x45, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x5f, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, + 0x10, 0x0d, 0x12, 0x21, 0x0a, 0x1d, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, 0x43, + 0x41, 0x55, 0x53, 0x45, 0x5f, 0x4e, 0x4f, 0x5f, 0x4d, 0x4f, 0x56, 0x45, 0x5f, 0x54, 0x41, 0x52, + 0x47, 0x45, 0x54, 0x10, 0x0c, 0x2a, 0x9b, 0x01, 0x0a, 0x0e, 0x44, 0x65, 0x66, 0x65, 0x72, 0x72, + 0x65, 0x64, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x56, 0x41, + 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4e, 0x43, + 0x45, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, + 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x43, 0x4f, + 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x02, 0x12, 0x1b, + 0x0a, 0x17, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, + 0x47, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x41, + 0x42, 0x53, 0x45, 0x4e, 0x54, 0x5f, 0x50, 0x52, 0x45, 0x52, 0x45, 0x51, 0x10, 0x04, 0x12, 0x13, + 0x0a, 0x0f, 0x44, 0x45, 0x46, 0x45, 0x52, 0x52, 0x45, 0x44, 0x5f, 0x50, 0x52, 0x45, 0x52, 0x45, + 0x51, 0x10, 0x05, 0x2a, 0xa1, 0x01, 0x0a, 0x12, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, + 0x69, 0x67, 0x67, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x4e, + 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x11, 0x0a, + 0x0d, 0x42, 0x45, 0x46, 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x45, 0x52, 0x41, 0x54, 0x45, 0x10, 0x01, + 0x12, 0x10, 0x0a, 0x0c, 0x41, 0x46, 0x54, 0x45, 0x52, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, + 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x42, 0x45, 0x46, 0x4f, 0x52, 0x45, 0x5f, 0x55, 0x50, 0x44, + 0x41, 0x54, 0x45, 0x10, 0x03, 0x12, 0x10, 0x0a, 0x0c, 0x41, 0x46, 0x54, 0x45, 0x52, 0x5f, 0x55, + 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e, 0x42, 0x45, 0x46, 0x4f, 0x52, + 0x45, 0x5f, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4f, 0x59, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d, 0x41, + 0x46, 0x54, 0x45, 0x52, 0x5f, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4f, 0x59, 0x10, 0x06, 0x12, 0x07, + 0x0a, 0x03, 0x43, 0x4c, 0x49, 0x10, 0x07, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, + 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x61, 0x6e, 0x73, 0x2f, 0x70, 0x6c, 0x61, 0x6e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, }) var ( @@ -2413,17 +2424,18 @@ var file_planfile_proto_depIdxs = []int32{ 17, // 32: tfplan.Importing.identity:type_name -> tfplan.DynamicValue 3, // 33: tfplan.Deferred.reason:type_name -> tfplan.DeferredReason 22, // 34: tfplan.ActionInvocationInstance.linked_resources:type_name -> tfplan.ResourceInstanceActionChange - 4, // 35: tfplan.ActionInvocationInstance.trigger_event:type_name -> tfplan.ActionTriggerEvent - 11, // 36: tfplan.ResourceInstanceActionChange.change:type_name -> tfplan.Change - 17, // 37: tfplan.Plan.VariablesEntry.value:type_name -> tfplan.DynamicValue - 18, // 38: tfplan.Plan.resource_attr.attr:type_name -> tfplan.Path - 5, // 39: tfplan.CheckResults.ObjectResult.status:type_name -> tfplan.CheckResults.Status - 17, // 40: tfplan.Path.Step.element_key:type_name -> tfplan.DynamicValue - 41, // [41:41] is the sub-list for method output_type - 41, // [41:41] is the sub-list for method input_type - 41, // [41:41] is the sub-list for extension type_name - 41, // [41:41] is the sub-list for extension extendee - 0, // [0:41] is the sub-list for field type_name + 17, // 35: tfplan.ActionInvocationInstance.config_value:type_name -> tfplan.DynamicValue + 4, // 36: tfplan.ActionInvocationInstance.trigger_event:type_name -> tfplan.ActionTriggerEvent + 11, // 37: tfplan.ResourceInstanceActionChange.change:type_name -> tfplan.Change + 17, // 38: tfplan.Plan.VariablesEntry.value:type_name -> tfplan.DynamicValue + 18, // 39: tfplan.Plan.resource_attr.attr:type_name -> tfplan.Path + 5, // 40: tfplan.CheckResults.ObjectResult.status:type_name -> tfplan.CheckResults.Status + 17, // 41: tfplan.Path.Step.element_key:type_name -> tfplan.DynamicValue + 42, // [42:42] is the sub-list for method output_type + 42, // [42:42] is the sub-list for method input_type + 42, // [42:42] is the sub-list for extension type_name + 42, // [42:42] is the sub-list for extension extendee + 0, // [0:42] is the sub-list for field type_name } func init() { file_planfile_proto_init() } diff --git a/internal/plans/planproto/planfile.proto b/internal/plans/planproto/planfile.proto index 3875680460..fd8b0cd460 100644 --- a/internal/plans/planproto/planfile.proto +++ b/internal/plans/planproto/planfile.proto @@ -449,11 +449,12 @@ message ActionInvocationInstance { string provider = 2; repeated ResourceInstanceActionChange linked_resources = 3; + DynamicValue config_value = 4; - string triggering_resource_addr = 4; - ActionTriggerEvent trigger_event = 5; - int64 action_trigger_block_index = 6; - int64 actions_list_index = 7; + string triggering_resource_addr = 5; + ActionTriggerEvent trigger_event = 6; + int64 action_trigger_block_index = 7; + int64 actions_list_index = 8; } message ResourceInstanceActionChange { diff --git a/internal/schemarepo/schemas.go b/internal/schemarepo/schemas.go index b076d80c02..4cccd1f3af 100644 --- a/internal/schemarepo/schemas.go +++ b/internal/schemarepo/schemas.go @@ -53,3 +53,11 @@ func (ss *Schemas) ResourceTypeConfig(provider addrs.Provider, resourceMode addr func (ss *Schemas) ProvisionerConfig(name string) *configschema.Block { return ss.Provisioners[name] } + +func (ss *Schemas) ActionTypeConfig(provider addrs.Provider, actionType string) providers.ActionSchema { + ps := ss.ProviderSchema(provider) + if ps.Actions == nil { + return providers.ActionSchema{} + } + return ps.Actions[actionType] +} diff --git a/internal/terraform/node_action_apply.go b/internal/terraform/node_action_apply.go index 2050edc61d..47a6423359 100644 --- a/internal/terraform/node_action_apply.go +++ b/internal/terraform/node_action_apply.go @@ -11,13 +11,14 @@ import ( "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/dag" "github.com/hashicorp/terraform/internal/plans" + "github.com/hashicorp/terraform/internal/plans/objchange" "github.com/hashicorp/terraform/internal/providers" "github.com/hashicorp/terraform/internal/tfdiags" ) type nodeActionApply struct { TriggeringResourceaddrs addrs.AbsResourceInstance - ActionInvocations []*plans.ActionInvocationInstance + ActionInvocations []*plans.ActionInvocationInstanceSrc } var ( @@ -41,11 +42,11 @@ func (n *nodeActionApply) Execute(ctx EvalContext, _ walkOperation) (diags tfdia return invokeActions(ctx, n.ActionInvocations) } -func invokeActions(ctx EvalContext, actionInvocations []*plans.ActionInvocationInstance) tfdiags.Diagnostics { +func invokeActions(ctx EvalContext, actionInvocations []*plans.ActionInvocationInstanceSrc) tfdiags.Diagnostics { var diags tfdiags.Diagnostics // First we order the action invocations by their trigger block index and events list index. // This way we have the correct order of execution. - orderedActionInvocations := make([]*plans.ActionInvocationInstance, len(actionInvocations)) + orderedActionInvocations := make([]*plans.ActionInvocationInstanceSrc, len(actionInvocations)) copy(orderedActionInvocations, actionInvocations) sort.Slice(orderedActionInvocations, func(i, j int) bool { if orderedActionInvocations[i].ActionTriggerBlockIndex == orderedActionInvocations[j].ActionTriggerBlockIndex { @@ -78,7 +79,7 @@ func invokeActions(ctx EvalContext, actionInvocations []*plans.ActionInvocationI for i, actionData := range orderedActionData { ai := orderedActionInvocations[i] - provider, _, err := getProvider(ctx, actionData.ProviderAddr) + provider, schema, err := getProvider(ctx, actionData.ProviderAddr) if err != nil { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, @@ -88,9 +89,42 @@ func invokeActions(ctx EvalContext, actionInvocations []*plans.ActionInvocationI return diags } + actionSchema, ok := schema.Actions[ai.Addr.Action.Action.Type] + if !ok { + // This should have been caught earlier + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + fmt.Sprintf("Action %s not found in provider schema", ai.Addr), + fmt.Sprintf("The action %s was not found in the provider schema for %s", ai.Addr.Action.Action.Type, actionData.ProviderAddr), + )) + return diags + } + + ais, err := ai.Decode(&actionSchema) + if err != nil { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + fmt.Sprintf("Failed to decode action data for %s", ai.Addr), + err.Error(), + )) + } + // We don't want to send the marks, but all marks are okay in the context of an action invocation. unmarkedConfigValue, _ := actionData.ConfigValue.UnmarkDeep() + // Validate that what we planned matches the action data we have. + errs := objchange.AssertObjectCompatible(actionSchema.ConfigSchema, ais.ConfigValue, unmarkedConfigValue) + for _, err := range errs { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Provider produced inconsistent final plan", + fmt.Sprintf( + "When expanding the plan for %s to include new values learned so far during apply, provider %q produced an invalid new value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", + ai.Addr, actionData.ProviderAddr.Provider.String(), tfdiags.FormatError(err), + ), + )) + } + hookIdentity := HookActionIdentity{ Addr: ai.Addr, TriggeringResourceAddr: ai.TriggeringResourceAddr, diff --git a/internal/terraform/node_resource_apply_instance.go b/internal/terraform/node_resource_apply_instance.go index 1de5131fc2..27c9101231 100644 --- a/internal/terraform/node_resource_apply_instance.go +++ b/internal/terraform/node_resource_apply_instance.go @@ -37,7 +37,7 @@ type NodeApplyableResourceInstance struct { // that this node represents, which the node itself must therefore ignore. forceReplace []addrs.AbsResourceInstance - beforeActionInvocations []*plans.ActionInvocationInstance + beforeActionInvocations []*plans.ActionInvocationInstanceSrc } var ( diff --git a/internal/terraform/transform_diff.go b/internal/terraform/transform_diff.go index c4824c909c..e696ba8d4d 100644 --- a/internal/terraform/transform_diff.go +++ b/internal/terraform/transform_diff.go @@ -86,20 +86,11 @@ func (t *DiffTransformer) Transform(g *Graph) error { // to be run as part of the apply phase. // The after-triggered action invocations will be run as part of a separate node // that will be connected to the resource instance nodes. - runBeforeNode := addrs.MakeMap[addrs.AbsResourceInstance, []*plans.ActionInvocationInstance]() - runAfterNode := addrs.MakeMap[addrs.AbsResourceInstance, []*plans.ActionInvocationInstance]() - for _, aiSrc := range changes.ActionInvocations { - ai, err := aiSrc.Decode() - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid action invocation", - fmt.Sprintf("The plan contains an invalid action invocation for %s: %s", aiSrc.Addr, err), - )) - return diags.Err() - } + runBeforeNode := addrs.MakeMap[addrs.AbsResourceInstance, []*plans.ActionInvocationInstanceSrc]() + runAfterNode := addrs.MakeMap[addrs.AbsResourceInstance, []*plans.ActionInvocationInstanceSrc]() + for _, ai := range changes.ActionInvocations { - var targetMap addrs.Map[addrs.AbsResourceInstance, []*plans.ActionInvocationInstance] + var targetMap addrs.Map[addrs.AbsResourceInstance, []*plans.ActionInvocationInstanceSrc] switch ai.TriggerEvent { case configs.BeforeCreate, configs.BeforeUpdate, configs.BeforeDestroy: targetMap = runBeforeNode @@ -109,7 +100,7 @@ func (t *DiffTransformer) Transform(g *Graph) error { panic("I don't know when to run this action invocation") } - basis := []*plans.ActionInvocationInstance{} + basis := []*plans.ActionInvocationInstanceSrc{} if targetMap.Has(ai.TriggeringResourceAddr) { basis = targetMap.Get(ai.TriggeringResourceAddr) }