From 3785619f93188b41ed9d2000b7e28a6b8b325715 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 15 Jun 2022 18:00:20 -0700 Subject: [PATCH] core: Use the new checks package for condition tracking The "checks" package is an expansion what we previously called plans.Conditions to accommodate a new requirement that we be able to track which checks we're expecting to run even if we don't actually get around to running them, which will be helpful when we start using checks as part of our module testing story because test reporting tools appreciate there being a relatively consistent set of test cases from one run to the next. So far this should be essentially a no-op change from an external functionality standpoint, aside from some minor adjustments to how we report some of the error and warning cases from condition evaluation in light of the fact that the "checks" package can now track errors as a different outcome than a failure of a valid check. As is often the case with anything which changes what we track in the EvalContext and persist between plan and apply, Terraform Core is pretty brittle and so this had knock-on effects elsewhere too. Again, the goal is for these changes to not create any material externally-visible difference, and just to accommodate the new assumption that there will always be a "checks" object available for tracking during a graph walk. --- internal/addrs/check.go | 73 ++- internal/checks/state.go | 24 +- internal/checks/state_init.go | 6 + internal/checks/state_report.go | 64 ++ internal/command/jsonplan/plan.go | 32 +- internal/plans/conditions.go | 53 -- .../plans/internal/planproto/planfile.pb.go | 600 +++++++++--------- .../plans/internal/planproto/planfile.proto | 51 +- internal/plans/plan.go | 10 +- internal/plans/planfile/planfile_test.go | 2 +- internal/plans/planfile/tfplan.go | 115 ++-- internal/plans/planfile/tfplan_test.go | 42 +- internal/states/checks.go | 99 +++ internal/states/state.go | 13 + internal/states/state_deepcopy.go | 3 +- internal/states/sync.go | 19 + internal/terraform/context_apply.go | 10 +- internal/terraform/context_apply_test.go | 1 + internal/terraform/context_plan.go | 4 +- internal/terraform/context_plan2_test.go | 50 +- internal/terraform/context_walk.go | 31 +- internal/terraform/eval_conditions.go | 195 ++++-- internal/terraform/eval_context.go | 7 +- internal/terraform/eval_context_builtin.go | 7 +- internal/terraform/eval_context_mock.go | 11 +- internal/terraform/graph_walk_context.go | 5 +- internal/terraform/node_output.go | 19 + internal/terraform/node_output_test.go | 5 + internal/terraform/node_resource_plan.go | 13 + 29 files changed, 982 insertions(+), 582 deletions(-) delete mode 100644 internal/plans/conditions.go create mode 100644 internal/states/checks.go diff --git a/internal/addrs/check.go b/internal/addrs/check.go index eae7fcda66..39e124d70e 100644 --- a/internal/addrs/check.go +++ b/internal/addrs/check.go @@ -1,6 +1,12 @@ package addrs -import "fmt" +import ( + "fmt" + + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hclsyntax" + "github.com/hashicorp/terraform/internal/tfdiags" +) // Check is the address of a check rule within a checkable object. // @@ -138,3 +144,68 @@ var ( _ ConfigCheckable = ConfigResource{} _ ConfigCheckable = ConfigOutputValue{} ) + +// ParseCheckableStr attempts to parse the given string as a Checkable address. +// +// This should be the opposite of Checkable.String for any Checkable address +// type. +// +// We do not typically expect users to write out checkable addresses as input, +// but we use them as part of some of our wire formats for persisting check +// results between runs. +func ParseCheckableStr(src string) (Checkable, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + + traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(src), "", hcl.InitialPos) + diags = diags.Append(parseDiags) + if parseDiags.HasErrors() { + return nil, diags + } + + path, remain, diags := parseModuleInstancePrefix(traversal) + if diags.HasErrors() { + return nil, diags + } + + if remain.IsRelative() { + // (relative means that there's either nothing left or what's next isn't an identifier) + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid checkable address", + Detail: "Module path must be followed by either a resource instance address or an output value address.", + Subject: remain.SourceRange().Ptr(), + }) + return nil, diags + } + + switch remain.RootName() { + case "output": + if len(remain) != 2 { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid checkable address", + Detail: "Output address must have only one attribute part after the keyword 'output', giving the name of the output value.", + Subject: remain.SourceRange().Ptr(), + }) + return nil, diags + } + if step, ok := remain[1].(hcl.TraverseAttr); !ok { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid checkable address", + Detail: "Output address must have only one attribute part after the keyword 'output', giving the name of the output value.", + Subject: remain.SourceRange().Ptr(), + }) + return nil, diags + } else { + return OutputValue{Name: step.Name}.Absolute(path), diags + } + default: + riAddr, moreDiags := parseResourceInstanceUnderModule(path, remain) + diags = diags.Append(moreDiags) + if diags.HasErrors() { + return nil, diags + } + return riAddr, diags + } +} diff --git a/internal/checks/state.go b/internal/checks/state.go index f8712e68c0..14c9629739 100644 --- a/internal/checks/state.go +++ b/internal/checks/state.go @@ -15,12 +15,24 @@ import ( // otherwise be treated as a read-only snapshot of the status of checks // at a particular moment. // +// The checks State tracks a few different concepts: +// - configuration objects: items in the configuration which statically +// declare some checks associated with zero or more checkable objects. +// - checkable objects: dynamically-determined objects that are each +// associated with one configuration object. +// - checks: a single check that is declared as part of a configuration +// object and then resolved once for each of its associated checkable +// objects. +// - check statuses: the current state of a particular check associated +// with a particular checkable object. +// // This container type is concurrency-safe for both reads and writes through // its various methods. type State struct { mu sync.Mutex - statuses addrs.Map[addrs.ConfigCheckable, *configCheckableState] + statuses addrs.Map[addrs.ConfigCheckable, *configCheckableState] + failureMsgs addrs.Map[addrs.Check, string] } // configCheckableState is an internal part of type State that represents @@ -274,6 +286,16 @@ func (c *State) AllCheckStatuses() addrs.Map[addrs.Check, Status] { return ret } +// CheckFailureMessage gets the failure message associated with the given +// check, if any. +// +// Only failed checks (StatusFail) can have error messages. The result is the +// empty string if the given check either didn't fail or failed without an +// error message. +func (c *State) CheckFailureMessage(addr addrs.Check) string { + return c.failureMsgs.Get(addr) +} + func summarizeCheckStatuses(errorCount, failCount, unknownCount int) Status { switch { case errorCount > 0: diff --git a/internal/checks/state_init.go b/internal/checks/state_init.go index d7c59c8c51..6714243b5c 100644 --- a/internal/checks/state_init.go +++ b/internal/checks/state_init.go @@ -7,6 +7,12 @@ import ( func initialStatuses(cfg *configs.Config) addrs.Map[addrs.ConfigCheckable, *configCheckableState] { ret := addrs.MakeMap[addrs.ConfigCheckable, *configCheckableState]() + if cfg == nil { + // This should not happen in normal use, but can arise in some + // unit tests that are not working with a full configuration and + // don't care about checks. + return ret + } collectInitialStatuses(ret, cfg) diff --git a/internal/checks/state_report.go b/internal/checks/state_report.go index 1940492539..63725d4af9 100644 --- a/internal/checks/state_report.go +++ b/internal/checks/state_report.go @@ -51,6 +51,43 @@ func (c *State) ReportCheckableObjects(configAddr addrs.ConfigCheckable, objectA } } +// ReportRestoredCheckableObjects is the interface by which Terraform Core +// should reload all of the checkable object addresses it previously determined +// during the planning phase in order to re-evaluate their results during +// the apply phase using updated data. +// +// If any of the given objects don't have a corresponding configuration object +// known to this check state then this function will panic. +func (c *State) ReportRestoredCheckableObjects(objectAddrs addrs.Set[addrs.Checkable]) { + c.mu.Lock() + defer c.mu.Unlock() + + // This is a similar principle as for ReportCheckableObjects except that + // we don't do the consistency checks that would normally ensure we only + // get one ReportCheckableObjects call per ConfigCheckable. + + for _, objectAddr := range objectAddrs { + configAddr := objectAddr.ConfigCheckable() + st, ok := c.statuses.GetOk(configAddr) + if !ok { + panic(fmt.Sprintf("checkable objects report for unknown configuration object %s", configAddr)) + } + + checks := make(map[addrs.CheckType][]Status, len(st.checkTypes)) + for checkType, count := range st.checkTypes { + // NOTE: This is intentionally a slice of count of the zero value + // of Status, which is StatusUnknown to represent that we don't + // yet have a report for that particular check. + checks[checkType] = make([]Status, count) + } + + if st.objects.Elems == nil { + st.objects = addrs.MakeMap[addrs.Checkable, map[addrs.CheckType][]Status]() + } + st.objects.Put(objectAddr, checks) + } +} + // ReportCheckResult is the interface by which Terraform Core should tell the // State object the result of a specific check for an object that was // previously registered with ReportCheckableObjects. @@ -65,6 +102,32 @@ func (c *State) ReportCheckResult(objectAddr addrs.Checkable, checkType addrs.Ch c.mu.Lock() defer c.mu.Unlock() + c.reportCheckResult(objectAddr, checkType, index, status) +} + +// ReportCheckFailure is a more specialized version of ReportCheckResult which +// captures a failure outcome in particular, giving the opportunity to capture +// an author-specified error message string along with the failure. +// +// This always records the given check as having StatusFail. Don't use this for +// situations where the check condition was itself invalid, because that +// should be represented by StatusError instead, and the error signalled via +// diagnostics as normal. +func (c *State) ReportCheckFailure(objectAddr addrs.Checkable, checkType addrs.CheckType, index int, errorMessage string) { + c.mu.Lock() + defer c.mu.Unlock() + + c.reportCheckResult(objectAddr, checkType, index, StatusFail) + if c.failureMsgs.Elems == nil { + c.failureMsgs = addrs.MakeMap[addrs.Check, string]() + } + checkAddr := addrs.NewCheck(objectAddr, checkType, index) + c.failureMsgs.Put(checkAddr, errorMessage) +} + +// reportCheckResult is shared between both ReportCheckResult and +// ReportCheckFailure, and assumes its caller already holds the mutex. +func (c *State) reportCheckResult(objectAddr addrs.Checkable, checkType addrs.CheckType, index int, status Status) { configAddr := objectAddr.ConfigCheckable() st, ok := c.statuses.GetOk(configAddr) @@ -85,4 +148,5 @@ func (c *State) ReportCheckResult(objectAddr addrs.Checkable, checkType addrs.Ch } checks[checkType][index] = status + } diff --git a/internal/command/jsonplan/plan.go b/internal/command/jsonplan/plan.go index d180fb7de1..498697c1c2 100644 --- a/internal/command/jsonplan/plan.go +++ b/internal/command/jsonplan/plan.go @@ -9,6 +9,7 @@ import ( ctyjson "github.com/zclconf/go-cty/cty/json" "github.com/hashicorp/terraform/internal/addrs" + "github.com/hashicorp/terraform/internal/checks" "github.com/hashicorp/terraform/internal/command/jsonconfig" "github.com/hashicorp/terraform/internal/command/jsonstate" "github.com/hashicorp/terraform/internal/configs" @@ -180,9 +181,9 @@ func Marshal( } // output.Conditions - err = output.marshalConditionResults(p.Conditions) + err = output.marshalCheckResults(p.Checks) if err != nil { - return nil, fmt.Errorf("error in marshaling condition results: %s", err) + return nil, fmt.Errorf("error in marshaling check results: %s", err) } // output.PriorState @@ -501,21 +502,30 @@ func (p *plan) marshalOutputChanges(changes *plans.Changes) error { return nil } -func (p *plan) marshalConditionResults(conditions plans.Conditions) error { - for addr, c := range conditions { +func (p *plan) marshalCheckResults(results *states.CheckResults) error { + if results == nil { + return nil + } + for _, result := range results.Results { cr := conditionResult{ - checkAddress: addr, - Address: c.Address.String(), - Type: c.Type.String(), - ErrorMessage: c.ErrorMessage, + checkAddress: result.CheckAddr.String(), + Address: result.CheckAddr.Container.String(), + Type: result.CheckAddr.Type.String(), + ErrorMessage: result.ErrorMessage, } - if c.Result.IsKnown() { - cr.Result = c.Result.True() - } else { + switch result.Status { + case checks.StatusPass: + cr.Result = true + case checks.StatusFail: + cr.Result = false + case checks.StatusError: + cr.Result = false + case checks.StatusUnknown: cr.Unknown = true } p.Conditions = append(p.Conditions, cr) } + sort.Slice(p.Conditions, func(i, j int) bool { return p.Conditions[i].checkAddress < p.Conditions[j].checkAddress }) diff --git a/internal/plans/conditions.go b/internal/plans/conditions.go deleted file mode 100644 index 0af0f4af8d..0000000000 --- a/internal/plans/conditions.go +++ /dev/null @@ -1,53 +0,0 @@ -package plans - -import ( - "sync" - - "github.com/hashicorp/terraform/internal/addrs" - "github.com/zclconf/go-cty/cty" -) - -// Conditions describes a set of results for condition blocks evaluated during -// the planning process. In normal operation, each result will either represent -// a passed check (Result is cty.True) or a deferred check (Result is -// cty.UnknownVal(cty.Bool)). Failing checks result in errors, except in -// refresh-only mode. -// -// The map key is a string representation of the check rule address, which is -// globally unique. Condition blocks can be evaluated multiple times during the -// planning operation, so we must be able to update an existing result value. -type Conditions map[string]*ConditionResult - -type ConditionResult struct { - Address addrs.Checkable - Result cty.Value - Type addrs.CheckType - ErrorMessage string -} - -func NewConditions() Conditions { - return make(Conditions) -} - -func (c Conditions) SyncWrapper() *ConditionsSync { - return &ConditionsSync{ - results: c, - } -} - -// ConditionsSync is a wrapper around a Conditions that provides a -// concurrency-safe interface to add or update a condition result value. -type ConditionsSync struct { - lock sync.Mutex - results Conditions -} - -func (cs *ConditionsSync) SetResult(addr addrs.Check, result *ConditionResult) { - if cs == nil { - panic("SetResult on nil Conditions") - } - cs.lock.Lock() - defer cs.lock.Unlock() - - cs.results[addr.String()] = result -} diff --git a/internal/plans/internal/planproto/planfile.pb.go b/internal/plans/internal/planproto/planfile.pb.go index a15559b92e..cd7a41c485 100644 --- a/internal/plans/internal/planproto/planfile.pb.go +++ b/internal/plans/internal/planproto/planfile.pb.go @@ -213,25 +213,25 @@ func (ResourceInstanceActionReason) EnumDescriptor() ([]byte, []int) { return file_planfile_proto_rawDescGZIP(), []int{2} } -// ConditionType defines the type of condition block used to generate a result. -type ConditionType int32 +// CheckType defines the type of check block used to generate a check result. +type CheckType int32 const ( - ConditionType_INVALID ConditionType = 0 - ConditionType_RESOURCE_PRECONDITION ConditionType = 1 - ConditionType_RESOURCE_POSTCONDITION ConditionType = 2 - ConditionType_OUTPUT_PRECONDITION ConditionType = 3 + CheckType_INVALID CheckType = 0 + CheckType_RESOURCE_PRECONDITION CheckType = 1 + CheckType_RESOURCE_POSTCONDITION CheckType = 2 + CheckType_OUTPUT_PRECONDITION CheckType = 3 ) -// Enum value maps for ConditionType. +// Enum value maps for CheckType. var ( - ConditionType_name = map[int32]string{ + CheckType_name = map[int32]string{ 0: "INVALID", 1: "RESOURCE_PRECONDITION", 2: "RESOURCE_POSTCONDITION", 3: "OUTPUT_PRECONDITION", } - ConditionType_value = map[string]int32{ + CheckType_value = map[string]int32{ "INVALID": 0, "RESOURCE_PRECONDITION": 1, "RESOURCE_POSTCONDITION": 2, @@ -239,33 +239,86 @@ var ( } ) -func (x ConditionType) Enum() *ConditionType { - p := new(ConditionType) +func (x CheckType) Enum() *CheckType { + p := new(CheckType) *p = x return p } -func (x ConditionType) String() string { +func (x CheckType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } -func (ConditionType) Descriptor() protoreflect.EnumDescriptor { +func (CheckType) Descriptor() protoreflect.EnumDescriptor { return file_planfile_proto_enumTypes[3].Descriptor() } -func (ConditionType) Type() protoreflect.EnumType { +func (CheckType) Type() protoreflect.EnumType { return &file_planfile_proto_enumTypes[3] } -func (x ConditionType) Number() protoreflect.EnumNumber { +func (x CheckType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } -// Deprecated: Use ConditionType.Descriptor instead. -func (ConditionType) EnumDescriptor() ([]byte, []int) { +// Deprecated: Use CheckType.Descriptor instead. +func (CheckType) EnumDescriptor() ([]byte, []int) { return file_planfile_proto_rawDescGZIP(), []int{3} } +// CheckStatus describes the status of a particular check at the completion of the plan. +type CheckStatus int32 + +const ( + CheckStatus_UNKNOWN CheckStatus = 0 + CheckStatus_PASS CheckStatus = 1 + CheckStatus_FAIL CheckStatus = 2 + CheckStatus_ERROR CheckStatus = 3 +) + +// Enum value maps for CheckStatus. +var ( + CheckStatus_name = map[int32]string{ + 0: "UNKNOWN", + 1: "PASS", + 2: "FAIL", + 3: "ERROR", + } + CheckStatus_value = map[string]int32{ + "UNKNOWN": 0, + "PASS": 1, + "FAIL": 2, + "ERROR": 3, + } +) + +func (x CheckStatus) Enum() *CheckStatus { + p := new(CheckStatus) + *p = x + return p +} + +func (x CheckStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (CheckStatus) Descriptor() protoreflect.EnumDescriptor { + return file_planfile_proto_enumTypes[4].Descriptor() +} + +func (CheckStatus) Type() protoreflect.EnumType { + return &file_planfile_proto_enumTypes[4] +} + +func (x CheckStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use CheckStatus.Descriptor instead. +func (CheckStatus) EnumDescriptor() ([]byte, []int) { + return file_planfile_proto_rawDescGZIP(), []int{4} +} + // Plan is the root message type for the tfplan file type Plan struct { state protoimpl.MessageState @@ -303,10 +356,12 @@ type Plan struct { // outputs that are not changing, as context for detecting inconsistencies // at apply time. OutputChanges []*OutputChange `protobuf:"bytes,4,rep,name=output_changes,json=outputChanges,proto3" json:"output_changes,omitempty"` - // An undordered set of condition block results for the entire - // configuration. This set includes unknown results for conditions which - // can only be evaluated at apply time. - ConditionResults []*ConditionResult `protobuf:"bytes,19,rep,name=condition_results,json=conditionResults,proto3" json:"condition_results,omitempty"` + // An unordered set of check results for the entire configuration. + // + // This set should cover the full set of checks we expect to resolve but + // some of them may have unknown results if they cannot be decided until + // apply time. + CheckResults []*CheckResult `protobuf:"bytes,19,rep,name=check_results,json=checkResults,proto3" json:"check_results,omitempty"` // An unordered set of target addresses to include when applying. If no // target addresses are present, the plan applies to the whole // configuration. @@ -400,9 +455,9 @@ func (x *Plan) GetOutputChanges() []*OutputChange { return nil } -func (x *Plan) GetConditionResults() []*ConditionResult { +func (x *Plan) GetCheckResults() []*CheckResult { if x != nil { - return x.ConditionResults + return x.CheckResults } return nil } @@ -794,34 +849,27 @@ func (x *OutputChange) GetSensitive() bool { return false } -type ConditionResult struct { +type CheckResult struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Address of the object containing the condition. Addr string `protobuf:"bytes,1,opt,name=addr,proto3" json:"addr,omitempty"` - // Globally-unique address of the condition block. This is a run-specific - // identifier and is only stored in the plan in order to reconstruct the - // set of condition results. - ConditionAddr string `protobuf:"bytes,2,opt,name=condition_addr,json=conditionAddr,proto3" json:"condition_addr,omitempty"` - // Result indicates the value of the condition expression, which is - // true if the condition succeeds, false if it fails, and unknown if the - // condition depends on values which are only known at apply time. - // - // Types that are assignable to Result: - // - // *ConditionResult_Value - // *ConditionResult_Unknown - Result isConditionResult_Result `protobuf_oneof:"result"` // Type of the condition block. - Type ConditionType `protobuf:"varint,5,opt,name=type,proto3,enum=tfplan.ConditionType" json:"type,omitempty"` - // Custom error message for a failing condition. - ErrorMessage string `protobuf:"bytes,6,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` + Type CheckType `protobuf:"varint,2,opt,name=type,proto3,enum=tfplan.CheckType" json:"type,omitempty"` + // Index of the condition block within the object and the CheckType. + // Indices are not consistent between runs if the configuration has + // changed, so indices are not correlatable between runs. + Index int64 `protobuf:"varint,3,opt,name=index,proto3" json:"index,omitempty"` + // The status of the check at the completion of the planning step. + Status CheckStatus `protobuf:"varint,4,opt,name=status,proto3,enum=tfplan.CheckStatus" json:"status,omitempty"` + // Optional custom error message for a failing condition. + ErrorMessage string `protobuf:"bytes,5,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` } -func (x *ConditionResult) Reset() { - *x = ConditionResult{} +func (x *CheckResult) Reset() { + *x = CheckResult{} if protoimpl.UnsafeEnabled { mi := &file_planfile_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -829,13 +877,13 @@ func (x *ConditionResult) Reset() { } } -func (x *ConditionResult) String() string { +func (x *CheckResult) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ConditionResult) ProtoMessage() {} +func (*CheckResult) ProtoMessage() {} -func (x *ConditionResult) ProtoReflect() protoreflect.Message { +func (x *CheckResult) ProtoReflect() protoreflect.Message { mi := &file_planfile_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -847,76 +895,46 @@ func (x *ConditionResult) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ConditionResult.ProtoReflect.Descriptor instead. -func (*ConditionResult) Descriptor() ([]byte, []int) { +// Deprecated: Use CheckResult.ProtoReflect.Descriptor instead. +func (*CheckResult) Descriptor() ([]byte, []int) { return file_planfile_proto_rawDescGZIP(), []int{5} } -func (x *ConditionResult) GetAddr() string { +func (x *CheckResult) GetAddr() string { if x != nil { return x.Addr } return "" } -func (x *ConditionResult) GetConditionAddr() string { - if x != nil { - return x.ConditionAddr - } - return "" -} - -func (m *ConditionResult) GetResult() isConditionResult_Result { - if m != nil { - return m.Result - } - return nil -} - -func (x *ConditionResult) GetValue() bool { - if x, ok := x.GetResult().(*ConditionResult_Value); ok { - return x.Value - } - return false -} - -func (x *ConditionResult) GetUnknown() bool { - if x, ok := x.GetResult().(*ConditionResult_Unknown); ok { - return x.Unknown - } - return false -} - -func (x *ConditionResult) GetType() ConditionType { +func (x *CheckResult) GetType() CheckType { if x != nil { return x.Type } - return ConditionType_INVALID + return CheckType_INVALID } -func (x *ConditionResult) GetErrorMessage() string { +func (x *CheckResult) GetIndex() int64 { + if x != nil { + return x.Index + } + return 0 +} + +func (x *CheckResult) GetStatus() CheckStatus { + if x != nil { + return x.Status + } + return CheckStatus_UNKNOWN +} + +func (x *CheckResult) GetErrorMessage() string { if x != nil { return x.ErrorMessage } return "" } -type isConditionResult_Result interface { - isConditionResult_Result() -} - -type ConditionResult_Value struct { - Value bool `protobuf:"varint,3,opt,name=value,proto3,oneof"` -} - -type ConditionResult_Unknown struct { - Unknown bool `protobuf:"varint,4,opt,name=unknown,proto3,oneof"` -} - -func (*ConditionResult_Value) isConditionResult_Result() {} - -func (*ConditionResult_Unknown) isConditionResult_Result() {} - // DynamicValue represents a value whose type is not decided until runtime, // often based on schema information obtained from a plugin. // @@ -1170,7 +1188,7 @@ var File_planfile_proto protoreflect.FileDescriptor var file_planfile_proto_rawDesc = []byte{ 0x0a, 0x0e, 0x70, 0x6c, 0x61, 0x6e, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x12, 0x06, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x22, 0xb2, 0x06, 0x0a, 0x04, 0x50, 0x6c, 0x61, + 0x12, 0x06, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x22, 0xa6, 0x06, 0x0a, 0x04, 0x50, 0x6c, 0x61, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x07, 0x75, 0x69, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0c, 0x2e, 0x74, @@ -1191,160 +1209,160 @@ var file_planfile_proto_rawDesc = []byte{ 0x3b, 0x0a, 0x0e, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0d, 0x6f, - 0x75, 0x74, 0x70, 0x75, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x44, 0x0a, 0x11, - 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, - 0x2e, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x64, 0x64, - 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x41, 0x64, 0x64, 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x72, - 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x73, 0x18, 0x10, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x11, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, - 0x41, 0x64, 0x64, 0x72, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, - 0x72, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x10, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x18, 0x0d, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x42, 0x61, 0x63, - 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x4b, 0x0a, - 0x13, 0x72, 0x65, 0x6c, 0x65, 0x76, 0x61, 0x6e, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x66, 0x70, - 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x52, 0x12, 0x72, 0x65, 0x6c, 0x65, 0x76, 0x61, 0x6e, 0x74, - 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x1a, 0x52, 0x0a, 0x0e, 0x56, 0x61, - 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2a, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 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, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4d, - 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x12, - 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x20, 0x0a, 0x04, 0x61, - 0x74, 0x74, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, - 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x61, 0x74, 0x74, 0x72, 0x22, 0x69, 0x0a, - 0x07, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x06, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 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, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x77, 0x6f, - 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, - 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0xe4, 0x01, 0x0a, 0x06, 0x43, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x41, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x06, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 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, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x16, 0x62, 0x65, 0x66, - 0x6f, 0x72, 0x65, 0x5f, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, - 0x74, 0x68, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, - 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x14, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x53, - 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x40, 0x0a, - 0x15, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, - 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, - 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x13, 0x61, 0x66, 0x74, 0x65, - 0x72, 0x53, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x73, 0x22, - 0xd3, 0x02, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, - 0x64, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x22, - 0x0a, 0x0d, 0x70, 0x72, 0x65, 0x76, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, - 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x65, 0x76, 0x52, 0x75, 0x6e, 0x41, 0x64, - 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x6b, 0x65, - 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x65, 0x64, - 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, - 0x26, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x09, 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, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, - 0x74, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, - 0x65, 0x12, 0x37, 0x0a, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x72, 0x65, - 0x70, 0x6c, 0x61, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, - 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, - 0x72, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x12, 0x49, 0x0a, 0x0d, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x24, 0x2e, 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, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x0c, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x68, 0x0a, 0x0c, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x43, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x18, 0x02, 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, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x22, - 0xda, 0x01, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x0d, + 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x13, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0c, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x66, 0x6f, 0x72, + 0x63, 0x65, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x73, + 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x52, 0x65, 0x70, + 0x6c, 0x61, 0x63, 0x65, 0x41, 0x64, 0x64, 0x72, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x65, 0x72, + 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0e, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, + 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, + 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, + 0x64, 0x12, 0x4b, 0x0a, 0x13, 0x72, 0x65, 0x6c, 0x65, 0x76, 0x61, 0x6e, 0x74, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x52, 0x12, 0x72, 0x65, 0x6c, 0x65, + 0x76, 0x61, 0x6e, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x1a, 0x52, + 0x0a, 0x0e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 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, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x1a, 0x4d, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, + 0x20, 0x0a, 0x04, 0x61, 0x74, 0x74, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, + 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x61, 0x74, 0x74, + 0x72, 0x22, 0x69, 0x0a, 0x07, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x12, 0x2c, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 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, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, + 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0xe4, 0x01, 0x0a, + 0x06, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, + 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x2c, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 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, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x42, 0x0a, + 0x16, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x5f, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, + 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, + 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x14, 0x62, 0x65, 0x66, + 0x6f, 0x72, 0x65, 0x53, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, + 0x73, 0x12, 0x40, 0x0a, 0x15, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x6e, 0x73, 0x69, + 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x13, + 0x61, 0x66, 0x74, 0x65, 0x72, 0x53, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, + 0x74, 0x68, 0x73, 0x22, 0xd3, 0x02, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, + 0x64, 0x72, 0x12, 0x22, 0x0a, 0x0d, 0x70, 0x72, 0x65, 0x76, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x61, + 0x64, 0x64, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x65, 0x76, 0x52, + 0x75, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x65, + 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x70, + 0x6f, 0x73, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x09, 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, 0x12, 0x18, 0x0a, 0x07, 0x70, + 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, + 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x64, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0f, 0x72, + 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x12, 0x49, + 0x0a, 0x0d, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 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, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x0c, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x68, 0x0a, 0x0c, 0x4f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, + 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 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, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, + 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, + 0x69, 0x76, 0x65, 0x22, 0xb0, 0x01, 0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x64, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0d, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x12, 0x16, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1a, 0x0a, 0x07, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, - 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x07, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, - 0x77, 0x6e, 0x12, 0x29, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, - 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x42, 0x08, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x28, 0x0a, 0x0c, - 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, - 0x6d, 0x73, 0x67, 0x70, 0x61, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, - 0x73, 0x67, 0x70, 0x61, 0x63, 0x6b, 0x22, 0xa5, 0x01, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, - 0x27, 0x0a, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, - 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x2e, 0x53, 0x74, 0x65, - 0x70, 0x52, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x1a, 0x74, 0x0a, 0x04, 0x53, 0x74, 0x65, 0x70, - 0x12, 0x27, 0x0a, 0x0e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0d, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x65, 0x6c, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 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, 0x48, 0x00, 0x52, 0x0a, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4b, - 0x65, 0x79, 0x42, 0x0a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 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, 0x70, 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, 0x2a, 0x86, 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, 0x2a, 0x6c, 0x0a, 0x0d, - 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, - 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x45, - 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x50, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x44, 0x49, 0x54, - 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, - 0x45, 0x5f, 0x50, 0x4f, 0x53, 0x54, 0x43, 0x4f, 0x4e, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x10, - 0x02, 0x12, 0x17, 0x0a, 0x13, 0x4f, 0x55, 0x54, 0x50, 0x55, 0x54, 0x5f, 0x50, 0x52, 0x45, 0x43, - 0x4f, 0x4e, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x42, 0x42, 0x5a, 0x40, 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, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x61, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x25, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, + 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x12, 0x2b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x28, 0x0a, 0x0c, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, + 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x73, 0x67, 0x70, 0x61, 0x63, + 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x73, 0x67, 0x70, 0x61, 0x63, 0x6b, + 0x22, 0xa5, 0x01, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, 0x27, 0x0a, 0x05, 0x73, 0x74, 0x65, + 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, + 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x2e, 0x53, 0x74, 0x65, 0x70, 0x52, 0x05, 0x73, 0x74, 0x65, + 0x70, 0x73, 0x1a, 0x74, 0x0a, 0x04, 0x53, 0x74, 0x65, 0x70, 0x12, 0x27, 0x0a, 0x0e, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x00, 0x52, 0x0d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6b, + 0x65, 0x79, 0x18, 0x02, 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, 0x48, 0x00, + 0x52, 0x0a, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x42, 0x0a, 0x0a, 0x08, + 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 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, 0x70, 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, 0x2a, 0x86, 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, 0x2a, 0x68, 0x0a, 0x09, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, + 0x12, 0x19, 0x0a, 0x15, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x50, 0x52, 0x45, + 0x43, 0x4f, 0x4e, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x52, + 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x50, 0x4f, 0x53, 0x54, 0x43, 0x4f, 0x4e, 0x44, + 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x17, 0x0a, 0x13, 0x4f, 0x55, 0x54, 0x50, 0x55, + 0x54, 0x5f, 0x50, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x03, + 0x2a, 0x39, 0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, + 0x50, 0x41, 0x53, 0x53, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x41, 0x49, 0x4c, 0x10, 0x02, + 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x42, 0x42, 0x5a, 0x40, 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, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x61, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1359,53 +1377,55 @@ func file_planfile_proto_rawDescGZIP() []byte { return file_planfile_proto_rawDescData } -var file_planfile_proto_enumTypes = make([]protoimpl.EnumInfo, 4) +var file_planfile_proto_enumTypes = make([]protoimpl.EnumInfo, 5) var file_planfile_proto_msgTypes = make([]protoimpl.MessageInfo, 11) var file_planfile_proto_goTypes = []interface{}{ (Mode)(0), // 0: tfplan.Mode (Action)(0), // 1: tfplan.Action (ResourceInstanceActionReason)(0), // 2: tfplan.ResourceInstanceActionReason - (ConditionType)(0), // 3: tfplan.ConditionType - (*Plan)(nil), // 4: tfplan.Plan - (*Backend)(nil), // 5: tfplan.Backend - (*Change)(nil), // 6: tfplan.Change - (*ResourceInstanceChange)(nil), // 7: tfplan.ResourceInstanceChange - (*OutputChange)(nil), // 8: tfplan.OutputChange - (*ConditionResult)(nil), // 9: tfplan.ConditionResult - (*DynamicValue)(nil), // 10: tfplan.DynamicValue - (*Path)(nil), // 11: tfplan.Path - nil, // 12: tfplan.Plan.VariablesEntry - (*PlanResourceAttr)(nil), // 13: tfplan.Plan.resource_attr - (*Path_Step)(nil), // 14: tfplan.Path.Step + (CheckType)(0), // 3: tfplan.CheckType + (CheckStatus)(0), // 4: tfplan.CheckStatus + (*Plan)(nil), // 5: tfplan.Plan + (*Backend)(nil), // 6: tfplan.Backend + (*Change)(nil), // 7: tfplan.Change + (*ResourceInstanceChange)(nil), // 8: tfplan.ResourceInstanceChange + (*OutputChange)(nil), // 9: tfplan.OutputChange + (*CheckResult)(nil), // 10: tfplan.CheckResult + (*DynamicValue)(nil), // 11: tfplan.DynamicValue + (*Path)(nil), // 12: tfplan.Path + nil, // 13: tfplan.Plan.VariablesEntry + (*PlanResourceAttr)(nil), // 14: tfplan.Plan.resource_attr + (*Path_Step)(nil), // 15: tfplan.Path.Step } var file_planfile_proto_depIdxs = []int32{ 0, // 0: tfplan.Plan.ui_mode:type_name -> tfplan.Mode - 12, // 1: tfplan.Plan.variables:type_name -> tfplan.Plan.VariablesEntry - 7, // 2: tfplan.Plan.resource_changes:type_name -> tfplan.ResourceInstanceChange - 7, // 3: tfplan.Plan.resource_drift:type_name -> tfplan.ResourceInstanceChange - 8, // 4: tfplan.Plan.output_changes:type_name -> tfplan.OutputChange - 9, // 5: tfplan.Plan.condition_results:type_name -> tfplan.ConditionResult - 5, // 6: tfplan.Plan.backend:type_name -> tfplan.Backend - 13, // 7: tfplan.Plan.relevant_attributes:type_name -> tfplan.Plan.resource_attr - 10, // 8: tfplan.Backend.config:type_name -> tfplan.DynamicValue + 13, // 1: tfplan.Plan.variables:type_name -> tfplan.Plan.VariablesEntry + 8, // 2: tfplan.Plan.resource_changes:type_name -> tfplan.ResourceInstanceChange + 8, // 3: tfplan.Plan.resource_drift:type_name -> tfplan.ResourceInstanceChange + 9, // 4: tfplan.Plan.output_changes:type_name -> tfplan.OutputChange + 10, // 5: tfplan.Plan.check_results:type_name -> tfplan.CheckResult + 6, // 6: tfplan.Plan.backend:type_name -> tfplan.Backend + 14, // 7: tfplan.Plan.relevant_attributes:type_name -> tfplan.Plan.resource_attr + 11, // 8: tfplan.Backend.config:type_name -> tfplan.DynamicValue 1, // 9: tfplan.Change.action:type_name -> tfplan.Action - 10, // 10: tfplan.Change.values:type_name -> tfplan.DynamicValue - 11, // 11: tfplan.Change.before_sensitive_paths:type_name -> tfplan.Path - 11, // 12: tfplan.Change.after_sensitive_paths:type_name -> tfplan.Path - 6, // 13: tfplan.ResourceInstanceChange.change:type_name -> tfplan.Change - 11, // 14: tfplan.ResourceInstanceChange.required_replace:type_name -> tfplan.Path + 11, // 10: tfplan.Change.values:type_name -> tfplan.DynamicValue + 12, // 11: tfplan.Change.before_sensitive_paths:type_name -> tfplan.Path + 12, // 12: tfplan.Change.after_sensitive_paths:type_name -> tfplan.Path + 7, // 13: tfplan.ResourceInstanceChange.change:type_name -> tfplan.Change + 12, // 14: tfplan.ResourceInstanceChange.required_replace:type_name -> tfplan.Path 2, // 15: tfplan.ResourceInstanceChange.action_reason:type_name -> tfplan.ResourceInstanceActionReason - 6, // 16: tfplan.OutputChange.change:type_name -> tfplan.Change - 3, // 17: tfplan.ConditionResult.type:type_name -> tfplan.ConditionType - 14, // 18: tfplan.Path.steps:type_name -> tfplan.Path.Step - 10, // 19: tfplan.Plan.VariablesEntry.value:type_name -> tfplan.DynamicValue - 11, // 20: tfplan.Plan.resource_attr.attr:type_name -> tfplan.Path - 10, // 21: tfplan.Path.Step.element_key:type_name -> tfplan.DynamicValue - 22, // [22:22] is the sub-list for method output_type - 22, // [22:22] is the sub-list for method input_type - 22, // [22:22] is the sub-list for extension type_name - 22, // [22:22] is the sub-list for extension extendee - 0, // [0:22] is the sub-list for field type_name + 7, // 16: tfplan.OutputChange.change:type_name -> tfplan.Change + 3, // 17: tfplan.CheckResult.type:type_name -> tfplan.CheckType + 4, // 18: tfplan.CheckResult.status:type_name -> tfplan.CheckStatus + 15, // 19: tfplan.Path.steps:type_name -> tfplan.Path.Step + 11, // 20: tfplan.Plan.VariablesEntry.value:type_name -> tfplan.DynamicValue + 12, // 21: tfplan.Plan.resource_attr.attr:type_name -> tfplan.Path + 11, // 22: tfplan.Path.Step.element_key:type_name -> tfplan.DynamicValue + 23, // [23:23] is the sub-list for method output_type + 23, // [23:23] is the sub-list for method input_type + 23, // [23:23] is the sub-list for extension type_name + 23, // [23:23] is the sub-list for extension extendee + 0, // [0:23] is the sub-list for field type_name } func init() { file_planfile_proto_init() } @@ -1475,7 +1495,7 @@ func file_planfile_proto_init() { } } file_planfile_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ConditionResult); i { + switch v := v.(*CheckResult); i { case 0: return &v.state case 1: @@ -1535,10 +1555,6 @@ func file_planfile_proto_init() { } } } - file_planfile_proto_msgTypes[5].OneofWrappers = []interface{}{ - (*ConditionResult_Value)(nil), - (*ConditionResult_Unknown)(nil), - } file_planfile_proto_msgTypes[10].OneofWrappers = []interface{}{ (*Path_Step_AttributeName)(nil), (*Path_Step_ElementKey)(nil), @@ -1548,7 +1564,7 @@ func file_planfile_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_planfile_proto_rawDesc, - NumEnums: 4, + NumEnums: 5, NumMessages: 11, NumExtensions: 0, NumServices: 0, diff --git a/internal/plans/internal/planproto/planfile.proto b/internal/plans/internal/planproto/planfile.proto index f12dab4fd4..3a96f8290a 100644 --- a/internal/plans/internal/planproto/planfile.proto +++ b/internal/plans/internal/planproto/planfile.proto @@ -44,10 +44,12 @@ message Plan { // at apply time. repeated OutputChange output_changes = 4; - // An undordered set of condition block results for the entire - // configuration. This set includes unknown results for conditions which - // can only be evaluated at apply time. - repeated ConditionResult condition_results = 19; + // An unordered set of check results for the entire configuration. + // + // This set should cover the full set of checks we expect to resolve but + // some of them may have unknown results if they cannot be decided until + // apply time. + repeated CheckResult check_results = 19; // An unordered set of target addresses to include when applying. If no // target addresses are present, the plan applies to the whole @@ -215,36 +217,39 @@ message OutputChange { bool sensitive = 3; } -// ConditionType defines the type of condition block used to generate a result. -enum ConditionType { +// CheckType defines the type of check block used to generate a check result. +enum CheckType { INVALID = 0; RESOURCE_PRECONDITION = 1; RESOURCE_POSTCONDITION = 2; OUTPUT_PRECONDITION = 3; } -message ConditionResult { +// CheckStatus describes the status of a particular check at the completion of the plan. +enum CheckStatus { + UNKNOWN = 0; + PASS = 1; + FAIL = 2; + ERROR = 3; +} + +message CheckResult { // Address of the object containing the condition. string addr = 1; - // Globally-unique address of the condition block. This is a run-specific - // identifier and is only stored in the plan in order to reconstruct the - // set of condition results. - string condition_addr = 2; - - // Result indicates the value of the condition expression, which is - // true if the condition succeeds, false if it fails, and unknown if the - // condition depends on values which are only known at apply time. - oneof result { - bool value = 3; - bool unknown = 4; - } - // Type of the condition block. - ConditionType type = 5; + CheckType type = 2; - // Custom error message for a failing condition. - string error_message = 6; + // Index of the condition block within the object and the CheckType. + // Indices are not consistent between runs if the configuration has + // changed, so indices are not correlatable between runs. + int64 index = 3; + + // The status of the check at the completion of the planning step. + CheckStatus status = 4; + + // Optional custom error message for a failing condition. + string error_message = 5; } // DynamicValue represents a value whose type is not decided until runtime, diff --git a/internal/plans/plan.go b/internal/plans/plan.go index 323aaa8c99..98f462390c 100644 --- a/internal/plans/plan.go +++ b/internal/plans/plan.go @@ -32,12 +32,20 @@ type Plan struct { VariableValues map[string]DynamicValue Changes *Changes - Conditions Conditions DriftedResources []*ResourceInstanceChangeSrc TargetAddrs []addrs.Targetable ForceReplaceAddrs []addrs.AbsResourceInstance Backend Backend + // Checks captures a snapshot of the (probably-incomplete) check results + // at the end of the planning process. + // + // If this plan is applyable (that is, if the planning process completed + // without errors) then the set of checks here should be complete even + // though some of them will likely have StatusUnknown where the check + // condition depends on values we won't know until the apply step. + Checks *states.CheckResults + // RelevantAttributes is a set of resource instance addresses and // attributes that are either directly affected by proposed changes or may // have indirectly contributed to them via references in expressions. diff --git a/internal/plans/planfile/planfile_test.go b/internal/plans/planfile/planfile_test.go index 96c91343c8..af3615cfcf 100644 --- a/internal/plans/planfile/planfile_test.go +++ b/internal/plans/planfile/planfile_test.go @@ -49,7 +49,6 @@ func TestRoundtrip(t *testing.T) { // Minimal plan too, since the serialization of the tfplan portion of the // file is tested more fully in tfplan_test.go . planIn := &plans.Plan{ - Conditions: plans.Conditions{}, Changes: &plans.Changes{ Resources: []*plans.ResourceInstanceChangeSrc{}, Outputs: []*plans.OutputChangeSrc{}, @@ -63,6 +62,7 @@ func TestRoundtrip(t *testing.T) { Config: plans.DynamicValue([]byte("config placeholder")), Workspace: "default", }, + Checks: &states.CheckResults{}, // Due to some historical oddities in how we've changed modelling over // time, we also include the states (without the corresponding file diff --git a/internal/plans/planfile/tfplan.go b/internal/plans/planfile/tfplan.go index add4ffa7cc..1920b27a30 100644 --- a/internal/plans/planfile/tfplan.go +++ b/internal/plans/planfile/tfplan.go @@ -8,6 +8,7 @@ import ( "google.golang.org/protobuf/proto" "github.com/hashicorp/terraform/internal/addrs" + "github.com/hashicorp/terraform/internal/checks" "github.com/hashicorp/terraform/internal/lang/globalref" "github.com/hashicorp/terraform/internal/lang/marks" "github.com/hashicorp/terraform/internal/plans" @@ -59,7 +60,7 @@ func readTfplan(r io.Reader) (*plans.Plan, error) { Resources: []*plans.ResourceInstanceChangeSrc{}, }, DriftedResources: []*plans.ResourceInstanceChangeSrc{}, - Conditions: make(plans.Conditions), + Checks: &states.CheckResults{}, } switch rawPlan.UiMode { @@ -90,41 +91,48 @@ func readTfplan(r io.Reader) (*plans.Plan, error) { }) } - for _, rawCR := range rawPlan.ConditionResults { - conditionAddr := rawCR.ConditionAddr - cr := &plans.ConditionResult{ - ErrorMessage: rawCR.ErrorMessage, - } - switch r := rawCR.Result.(type) { - case *planproto.ConditionResult_Value: - cr.Result = cty.BoolVal(r.Value) - case *planproto.ConditionResult_Unknown: - cr.Result = cty.UnknownVal(cty.Bool) + for _, rawCR := range rawPlan.CheckResults { + cr := &states.CheckResult{} + switch rawCR.Status { + case planproto.CheckStatus_UNKNOWN: + cr.Status = checks.StatusUnknown + case planproto.CheckStatus_PASS: + cr.Status = checks.StatusPass + case planproto.CheckStatus_FAIL: + cr.Status = checks.StatusFail + case planproto.CheckStatus_ERROR: + cr.Status = checks.StatusError + default: + return nil, fmt.Errorf("check for %s as unsupported status %#v", rawCR.Addr, rawCR.Status) } var diags tfdiags.Diagnostics + var checkType addrs.CheckType + var objectAddr addrs.Checkable switch rawCR.Type { - case planproto.ConditionType_OUTPUT_PRECONDITION: - cr.Type = addrs.OutputPrecondition - cr.Address, diags = addrs.ParseAbsOutputValueStr(rawCR.Addr) + case planproto.CheckType_OUTPUT_PRECONDITION: + checkType = addrs.OutputPrecondition + objectAddr, diags = addrs.ParseAbsOutputValueStr(rawCR.Addr) if diags.HasErrors() { return nil, diags.Err() } - case planproto.ConditionType_RESOURCE_PRECONDITION: - cr.Type = addrs.ResourcePrecondition - cr.Address, diags = addrs.ParseAbsResourceInstanceStr(rawCR.Addr) + case planproto.CheckType_RESOURCE_PRECONDITION: + checkType = addrs.ResourcePrecondition + objectAddr, diags = addrs.ParseAbsResourceInstanceStr(rawCR.Addr) if diags.HasErrors() { return nil, diags.Err() } - case planproto.ConditionType_RESOURCE_POSTCONDITION: - cr.Type = addrs.ResourcePostcondition - cr.Address, diags = addrs.ParseAbsResourceInstanceStr(rawCR.Addr) + case planproto.CheckType_RESOURCE_POSTCONDITION: + checkType = addrs.ResourcePostcondition + objectAddr, diags = addrs.ParseAbsResourceInstanceStr(rawCR.Addr) if diags.HasErrors() { return nil, diags.Err() } default: - return nil, fmt.Errorf("condition result %s has unsupported type %s", rawCR.ConditionAddr, rawCR.Type) + return nil, fmt.Errorf("check for %s has unsupported type %s", rawCR.Addr, rawCR.Type) } - plan.Conditions[conditionAddr] = cr + cr.CheckAddr = addrs.NewCheck(objectAddr, checkType, int(rawCR.Index)) + cr.ErrorMessage = rawCR.ErrorMessage + plan.Checks.Results = append(plan.Checks.Results, cr) } for _, rawRC := range rawPlan.ResourceChanges { @@ -403,11 +411,11 @@ func writeTfplan(plan *plans.Plan, w io.Writer) error { Version: tfplanFormatVersion, TerraformVersion: version.String(), - Variables: map[string]*planproto.DynamicValue{}, - OutputChanges: []*planproto.OutputChange{}, - ConditionResults: []*planproto.ConditionResult{}, - ResourceChanges: []*planproto.ResourceInstanceChange{}, - ResourceDrift: []*planproto.ResourceInstanceChange{}, + Variables: map[string]*planproto.DynamicValue{}, + OutputChanges: []*planproto.OutputChange{}, + CheckResults: []*planproto.CheckResult{}, + ResourceChanges: []*planproto.ResourceInstanceChange{}, + ResourceDrift: []*planproto.ResourceInstanceChange{}, } switch plan.UIMode { @@ -446,33 +454,38 @@ func writeTfplan(plan *plans.Plan, w io.Writer) error { }) } - for addr, cr := range plan.Conditions { - pcr := &planproto.ConditionResult{ - Addr: cr.Address.String(), - ConditionAddr: addr, - ErrorMessage: cr.ErrorMessage, - } - if cr.Result.IsKnown() { - pcr.Result = &planproto.ConditionResult_Value{ - Value: cr.Result.True(), + if plan.Checks != nil { + for _, cr := range plan.Checks.Results { + pcr := &planproto.CheckResult{ + Addr: cr.CheckAddr.Container.String(), + Index: int64(cr.CheckAddr.Index), + ErrorMessage: cr.ErrorMessage, } - } else { - pcr.Result = &planproto.ConditionResult_Unknown{ - Unknown: true, + switch cr.Status { + case checks.StatusUnknown: + pcr.Status = planproto.CheckStatus_UNKNOWN + case checks.StatusPass: + pcr.Status = planproto.CheckStatus_PASS + case checks.StatusFail: + pcr.Status = planproto.CheckStatus_FAIL + case checks.StatusError: + pcr.Status = planproto.CheckStatus_ERROR + default: + return fmt.Errorf("condition result %s has unsupported status %s", cr.CheckAddr, cr.Status) + } + switch cr.CheckAddr.Type { + case addrs.OutputPrecondition: + pcr.Type = planproto.CheckType_OUTPUT_PRECONDITION + case addrs.ResourcePrecondition: + pcr.Type = planproto.CheckType_RESOURCE_PRECONDITION + case addrs.ResourcePostcondition: + pcr.Type = planproto.CheckType_RESOURCE_POSTCONDITION + default: + return fmt.Errorf("condition result %s has unsupported type %s", cr.CheckAddr, cr.CheckAddr.Type) } - } - switch cr.Type { - case addrs.OutputPrecondition: - pcr.Type = planproto.ConditionType_OUTPUT_PRECONDITION - case addrs.ResourcePrecondition: - pcr.Type = planproto.ConditionType_RESOURCE_PRECONDITION - case addrs.ResourcePostcondition: - pcr.Type = planproto.ConditionType_RESOURCE_POSTCONDITION - default: - return fmt.Errorf("condition result %s has unsupported type %s", addr, cr.Type) - } - rawPlan.ConditionResults = append(rawPlan.ConditionResults, pcr) + rawPlan.CheckResults = append(rawPlan.CheckResults, pcr) + } } for _, rc := range plan.Changes.Resources { diff --git a/internal/plans/planfile/tfplan_test.go b/internal/plans/planfile/tfplan_test.go index 9e24d7de6e..ea96d7ec4e 100644 --- a/internal/plans/planfile/tfplan_test.go +++ b/internal/plans/planfile/tfplan_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform/internal/lang/globalref" "github.com/hashicorp/terraform/internal/lang/marks" "github.com/hashicorp/terraform/internal/plans" + "github.com/hashicorp/terraform/internal/states" ) func TestTFPlanRoundTrip(t *testing.T) { @@ -22,32 +23,6 @@ func TestTFPlanRoundTrip(t *testing.T) { VariableValues: map[string]plans.DynamicValue{ "foo": mustNewDynamicValueStr("foo value"), }, - Conditions: plans.Conditions{ - "test_thing.woot[0].preconditions[0]": &plans.ConditionResult{ - Address: addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "test_thing", - Name: "woot", - }.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance), - Result: cty.False, - Type: addrs.ResourcePrecondition, - ErrorMessage: "Invalid thing: too much woot.", - }, - "test_thing.woot[0].postconditions[0]": &plans.ConditionResult{ - Address: addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "test_thing", - Name: "woot", - }.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance), - Result: cty.UnknownVal(cty.Bool), - Type: addrs.ResourcePostcondition, - }, - "output.bar.preconditions[0]": &plans.ConditionResult{ - Address: addrs.OutputValue{Name: "bar"}.Absolute(addrs.RootModuleInstance), - Result: cty.True, - Type: addrs.OutputPrecondition, - }, - }, Changes: &plans.Changes{ Outputs: []*plans.OutputChangeSrc{ { @@ -195,6 +170,21 @@ func TestTFPlanRoundTrip(t *testing.T) { Attr: cty.GetAttrPath("boop").Index(cty.NumberIntVal(1)), }, }, + Checks: &states.CheckResults{ + Results: []*states.CheckResult{ + { + CheckAddr: addrs.NewCheck( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_thing", + Name: "woot", + }.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance), + addrs.ResourcePostcondition, + 0, + ), + }, + }, + }, TargetAddrs: []addrs.Targetable{ addrs.Resource{ Mode: addrs.ManagedResourceMode, diff --git a/internal/states/checks.go b/internal/states/checks.go new file mode 100644 index 0000000000..b02712e69e --- /dev/null +++ b/internal/states/checks.go @@ -0,0 +1,99 @@ +package states + +import ( + "github.com/hashicorp/terraform/internal/addrs" + "github.com/hashicorp/terraform/internal/checks" +) + +// CheckResults represents a snapshot of the status of a set of checks declared +// in configuration, updated after each Terraform Core run that changes the state +// or remote system in a way that might impact the check results. +// +// Unlike a checks.State, this type only tracks the leaf check results and +// doesn't retain any information about how the checks were declared in +// configuration. That's because this subset of the data is intended to survive +// from one run to the next, and the next run will probably have a changed +// configuration anyway and so it's only meaningful to consider changes +// to the presence of checks and their statuses between runs. +type CheckResults struct { + Results []*CheckResult +} + +// Check is the state of a single check, inside the Checks struct. +type CheckResult struct { + CheckAddr addrs.Check + Status checks.Status + + // If Status is checks.StatusError then there might also be an error + // message describing what problem the check detected. + ErrorMessage string +} + +// NewCheckResults constructs a new states.CheckResults object that is a +// snapshot of the check statuses recorded in the given checks.State object. +// +// This should be called only after a Terraform Core run has completed and +// recorded any results from running the checks in the given object. +func NewCheckResults(source *checks.State) *CheckResults { + statuses := source.AllCheckStatuses() + if statuses.Len() == 0 { + return &CheckResults{} + } + + results := make([]*CheckResult, 0, statuses.Len()) + for _, elem := range statuses.Elems { + errMsg := source.CheckFailureMessage(elem.Key) + results = append(results, &CheckResult{ + CheckAddr: elem.Key, + Status: elem.Value, + ErrorMessage: errMsg, + }) + } + + return &CheckResults{results} +} + +// AllCheckedObjects returns a set of all of the objects that have at least +// one check in the set of results. +func (r *CheckResults) AllCheckedObjects() addrs.Set[addrs.Checkable] { + if r == nil || len(r.Results) == 0 { + return nil + } + ret := addrs.MakeSet[addrs.Checkable]() + for _, result := range r.Results { + ret.Add(result.CheckAddr.Container) + } + return ret +} + +// GetCheckResults scans over the checks and returns the first one that +// has the given address, or nil if there is no such check. +// +// In main code we shouldn't typically need to look up individual checks +// like this, since we'll usually be reporting check results in an aggregate +// form, but determining the result of a particular check is useful in our +// internal unit tests, and so this is here primarily for that purpose. +func (r *CheckResults) GetCheckResult(addr addrs.Check) *CheckResult { + for _, result := range r.Results { + if addrs.Equivalent(result.CheckAddr, addr) { + return result + } + } + return nil +} + +func (r *CheckResults) DeepCopy() *CheckResults { + if r == nil { + return nil + } + if len(r.Results) == 0 { + return &CheckResults{} + } + + // Everything inside CheckResult is either a value type or is + // treated as immutable by convention, so we don't need to + // copy any deeper. + results := make([]*CheckResult, len(r.Results)) + copy(results, r.Results) + return &CheckResults{results} +} diff --git a/internal/states/state.go b/internal/states/state.go index 90c73c1158..9992f93caf 100644 --- a/internal/states/state.go +++ b/internal/states/state.go @@ -25,6 +25,19 @@ type State struct { // Modules contains the state for each module. The keys in this map are // an implementation detail and must not be used by outside callers. Modules map[string]*Module + + // CheckResults contains a snapshot of the statuses of checks at the + // end of the most recent update to the state. Callers might compare + // checks between runs to see if e.g. a previously-failing check has + // been fixed since the last run, or similar. + // + // CheckResults can be nil to indicate that there are no check results + // from the previous run at all, which is subtly different than the + // previous run having affirmatively recorded that there are no checks + // to run. For example, if this object was created from a state snapshot + // created by a version of Terraform that didn't yet support checks + // then this field will be nil. + CheckResults *CheckResults } // NewState constructs a minimal empty state, containing an empty root module. diff --git a/internal/states/state_deepcopy.go b/internal/states/state_deepcopy.go index f2cb3a0b4d..b8498d53ff 100644 --- a/internal/states/state_deepcopy.go +++ b/internal/states/state_deepcopy.go @@ -29,7 +29,8 @@ func (s *State) DeepCopy() *State { modules[k] = m.DeepCopy() } return &State{ - Modules: modules, + Modules: modules, + CheckResults: s.CheckResults.DeepCopy(), } } diff --git a/internal/states/sync.go b/internal/states/sync.go index f219842795..d48fa75518 100644 --- a/internal/states/sync.go +++ b/internal/states/sync.go @@ -5,6 +5,7 @@ import ( "sync" "github.com/hashicorp/terraform/internal/addrs" + "github.com/hashicorp/terraform/internal/checks" "github.com/zclconf/go-cty/cty" ) @@ -457,6 +458,24 @@ func (s *SyncState) RemovePlannedResourceInstanceObjects() { } } +// DiscardCheckResults discards any previously-recorded check results, with +// the intent of preventing any references to them after they have become +// stale due to starting (but possibly not completing) an update. +func (s *SyncState) DiscardCheckResults() { + s.lock.Lock() + s.state.CheckResults = nil + s.lock.Unlock() +} + +// RecordCheckResults replaces any check results already recorded in the state +// with a new set taken from the given check state object. +func (s *SyncState) RecordCheckResults(checkState *checks.State) { + newResults := NewCheckResults(checkState) + s.lock.Lock() + s.state.CheckResults = newResults + s.lock.Unlock() +} + // Lock acquires an explicit lock on the state, allowing direct read and write // access to the returned state object. The caller must call Unlock once // access is no longer needed, and then immediately discard the state pointer diff --git a/internal/terraform/context_apply.go b/internal/terraform/context_apply.go index f2d5faca68..2ca1b87f8c 100644 --- a/internal/terraform/context_apply.go +++ b/internal/terraform/context_apply.go @@ -35,11 +35,19 @@ func (c *Context) Apply(plan *plans.Plan, config *configs.Config) (*states.State Config: config, InputState: workingState, Changes: plan.Changes, - Conditions: plan.Conditions, + + // We need to propagate the results of deciding which objects will + // have checks from the plan phase, since we won't re-run the + // instance expansion logic during the apply phase. + KnownCheckableObjects: plan.Checks.AllCheckedObjects(), }) diags = diags.Append(walker.NonFatalDiagnostics) diags = diags.Append(walkDiags) + // After the walk is finished, we capture a simplified snapshot of the + // check result data as part of the new state. + walker.State.RecordCheckResults(walker.Checks) + newState := walker.State.Close() if plan.UIMode == plans.DestroyMode && !diags.HasErrors() { // NOTE: This is a vestigial violation of the rule that we mustn't diff --git a/internal/terraform/context_apply_test.go b/internal/terraform/context_apply_test.go index 4bdffe70c1..c3e3beecbc 100644 --- a/internal/terraform/context_apply_test.go +++ b/internal/terraform/context_apply_test.go @@ -2640,6 +2640,7 @@ func TestContext2Apply_orphanResource(t *testing.T) { // The state should now be _totally_ empty, with just an empty root module // (since that always exists) and no resources at all. want = states.NewState() + want.CheckResults = &states.CheckResults{} if !cmp.Equal(state, want) { t.Fatalf("wrong state after step 2\ngot: %swant: %s", spew.Sdump(state), spew.Sdump(want)) } diff --git a/internal/terraform/context_plan.go b/internal/terraform/context_plan.go index 66e22d90bf..abd8d2adbe 100644 --- a/internal/terraform/context_plan.go +++ b/internal/terraform/context_plan.go @@ -500,12 +500,10 @@ func (c *Context) planWalk(config *configs.Config, prevRunState *states.State, o // If we get here then we should definitely have a non-nil "graph", which // we can now walk. changes := plans.NewChanges() - conditions := plans.NewConditions() walker, walkDiags := c.walk(graph, walkOp, &graphWalkOpts{ Config: config, InputState: prevRunState, Changes: changes, - Conditions: conditions, MoveResults: moveResults, }) diags = diags.Append(walker.NonFatalDiagnostics) @@ -539,10 +537,10 @@ func (c *Context) planWalk(config *configs.Config, prevRunState *states.State, o plan := &plans.Plan{ UIMode: opts.Mode, Changes: changes, - Conditions: conditions, DriftedResources: driftedResources, PrevRunState: prevRunState, PriorState: priorState, + Checks: states.NewCheckResults(walker.Checks), // Other fields get populated by Context.Plan after we return } diff --git a/internal/terraform/context_plan2_test.go b/internal/terraform/context_plan2_test.go index 238616c7d5..88995a8df5 100644 --- a/internal/terraform/context_plan2_test.go +++ b/internal/terraform/context_plan2_test.go @@ -11,6 +11,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/google/go-cmp/cmp" "github.com/hashicorp/terraform/internal/addrs" + "github.com/hashicorp/terraform/internal/checks" "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/lang/marks" "github.com/hashicorp/terraform/internal/plans" @@ -2909,6 +2910,7 @@ resource "test_resource" "a" { t.Fatalf("unexpected %s change for %s", res.Action, res.Addr) } } + addr := mustResourceInstanceAddr("data.test_data_source.a") wantCheckTypes := []addrs.CheckType{ addrs.ResourcePrecondition, @@ -2916,15 +2918,14 @@ resource "test_resource" "a" { } for _, ty := range wantCheckTypes { checkAddr := addr.Check(ty, 0) - if result, ok := plan.Conditions[checkAddr.String()]; !ok { + if gotResult := plan.Checks.GetCheckResult(checkAddr); gotResult == nil { t.Errorf("no condition result for %s", checkAddr) } else { - wantResult := &plans.ConditionResult{ - Address: addr, - Result: cty.True, - Type: ty, + wantResult := &states.CheckResult{ + CheckAddr: checkAddr, + Status: checks.StatusPass, } - if diff := cmp.Diff(wantResult, result, valueComparer); diff != "" { + if diff := cmp.Diff(wantResult, gotResult, valueComparer); diff != "" { t.Errorf("wrong condition result for %s\n%s", checkAddr, diff) } } @@ -3034,16 +3035,15 @@ resource "test_resource" "a" { } addr := mustResourceInstanceAddr("data.test_data_source.a") checkAddr := addr.Check(addrs.ResourcePostcondition, 0) - if result, ok := plan.Conditions[checkAddr.String()]; !ok { + if gotResult := plan.Checks.GetCheckResult(checkAddr); gotResult == nil { t.Errorf("no condition result for %s", checkAddr) } else { - wantResult := &plans.ConditionResult{ - Address: addr, - Result: cty.False, - Type: addrs.ResourcePostcondition, + wantResult := &states.CheckResult{ + CheckAddr: checkAddr, + Status: checks.StatusFail, ErrorMessage: "Results cannot be empty.", } - if diff := cmp.Diff(wantResult, result, valueComparer); diff != "" { + if diff := cmp.Diff(wantResult, gotResult, valueComparer); diff != "" { t.Errorf("wrong condition result\n%s", diff) } } @@ -3130,15 +3130,14 @@ output "a" { t.Errorf("wrong planned action\ngot: %s\nwant: %s", got, want) } checkAddr := addr.Check(addrs.OutputPrecondition, 0) - if result, ok := plan.Conditions[checkAddr.String()]; !ok { + if gotResult := plan.Checks.GetCheckResult(checkAddr); gotResult == nil { t.Errorf("no condition result for %s", checkAddr) } else { - wantResult := &plans.ConditionResult{ - Address: addr, - Result: cty.True, - Type: addrs.OutputPrecondition, + wantResult := &states.CheckResult{ + CheckAddr: checkAddr, + Status: checks.StatusPass, } - if diff := cmp.Diff(wantResult, result, valueComparer); diff != "" { + if diff := cmp.Diff(wantResult, gotResult, valueComparer); diff != "" { t.Errorf("wrong condition result\n%s", diff) } } @@ -3191,16 +3190,15 @@ output "a" { t.Errorf("wrong planned action\ngot: %s\nwant: %s", got, want) } checkAddr := addr.Check(addrs.OutputPrecondition, 0) - if result, ok := plan.Conditions[checkAddr.String()]; !ok { + if gotResult := plan.Checks.GetCheckResult(checkAddr); gotResult == nil { t.Errorf("no condition result for %s", checkAddr) } else { - wantResult := &plans.ConditionResult{ - Address: addr, - Result: cty.False, - Type: addrs.OutputPrecondition, + wantResult := &states.CheckResult{ + CheckAddr: checkAddr, + Status: checks.StatusFail, ErrorMessage: "Wrong boop.", } - if diff := cmp.Diff(wantResult, result, valueComparer); diff != "" { + if diff := cmp.Diff(wantResult, gotResult, valueComparer); diff != "" { t.Errorf("wrong condition result\n%s", diff) } } @@ -3330,11 +3328,11 @@ output "a" { for _, diag := range diags { desc := diag.Description() if desc.Summary == "Module output value precondition failed" { - if got, want := desc.Detail, "The error message included a sensitive value, so it will not be displayed."; !strings.Contains(got, want) { + if got, want := desc.Detail, "This check failed, but has an invalid error message as described in the other accompanying messages."; !strings.Contains(got, want) { t.Errorf("unexpected detail\ngot: %s\nwant to contain %q", got, want) } } else if desc.Summary == "Error message refers to sensitive values" { - if got, want := desc.Detail, "The error expression used to explain this condition refers to sensitive values."; !strings.Contains(got, want) { + if got, want := desc.Detail, "The error expression used to explain this condition refers to sensitive values, so Terraform will not display the resulting message."; !strings.Contains(got, want) { t.Errorf("unexpected detail\ngot: %s\nwant to contain %q", got, want) } } else { diff --git a/internal/terraform/context_walk.go b/internal/terraform/context_walk.go index cc61331cbb..199facc35f 100644 --- a/internal/terraform/context_walk.go +++ b/internal/terraform/context_walk.go @@ -3,6 +3,8 @@ package terraform import ( "log" + "github.com/hashicorp/terraform/internal/addrs" + "github.com/hashicorp/terraform/internal/checks" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/instances" "github.com/hashicorp/terraform/internal/plans" @@ -21,9 +23,15 @@ import ( type graphWalkOpts struct { InputState *states.State Changes *plans.Changes - Conditions plans.Conditions Config *configs.Config + // KnownCheckableObjects is a set of objects that we know ahead of time + // ought to have checks run against them during this graph walk. + // Use this to propagate the decision about which objects are checkable + // from the plan phase into the apply phase, so that the apply phase + // doesn't need to recalcuate it. + KnownCheckableObjects addrs.Set[addrs.Checkable] + MoveResults refactoring.MoveResults } @@ -78,9 +86,19 @@ func (c *Context) graphWalker(operation walkOperation, opts *graphWalkOpts) *Con refreshState = inputState.DeepCopy().SyncWrapper() prevRunState = inputState.DeepCopy().SyncWrapper() + // For both of our new states we'll discard the previous run's + // check results, since we can still refer to them from the + // prevRunState object if we need to. + state.DiscardCheckResults() + refreshState.DiscardCheckResults() + default: state = inputState.DeepCopy().SyncWrapper() // Only plan-like walks use refreshState and prevRunState + + // Discard the input state's check results, because we should create + // a new set as a result of the graph walk. + state.DiscardCheckResults() } changes := opts.Changes @@ -92,17 +110,14 @@ func (c *Context) graphWalker(operation walkOperation, opts *graphWalkOpts) *Con // afterwards. changes = plans.NewChanges() } - conditions := opts.Conditions - if conditions == nil { - // This fallback conditions object is in place for the same reason as - // the changes object above: to avoid crashes for non-plan walks. - conditions = plans.NewConditions() - } if opts.Config == nil { panic("Context.graphWalker call without Config") } + checkState := checks.NewState(opts.Config) + checkState.ReportRestoredCheckableObjects(opts.KnownCheckableObjects) + return &ContextGraphWalker{ Context: c, State: state, @@ -110,7 +125,7 @@ func (c *Context) graphWalker(operation walkOperation, opts *graphWalkOpts) *Con RefreshState: refreshState, PrevRunState: prevRunState, Changes: changes.SyncWrapper(), - Conditions: conditions.SyncWrapper(), + Checks: checkState, InstanceExpander: instances.NewExpander(), MoveResults: opts.MoveResults, Operation: operation, diff --git a/internal/terraform/eval_conditions.go b/internal/terraform/eval_conditions.go index 3d9545bb18..690188a670 100644 --- a/internal/terraform/eval_conditions.go +++ b/internal/terraform/eval_conditions.go @@ -10,11 +10,11 @@ import ( "github.com/zclconf/go-cty/cty/convert" "github.com/hashicorp/terraform/internal/addrs" + "github.com/hashicorp/terraform/internal/checks" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/instances" "github.com/hashicorp/terraform/internal/lang" "github.com/hashicorp/terraform/internal/lang/marks" - "github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/tfdiags" ) @@ -30,6 +30,16 @@ import ( func evalCheckRules(typ addrs.CheckType, rules []*configs.CheckRule, ctx EvalContext, self addrs.Checkable, keyData instances.RepetitionData, diagSeverity tfdiags.Severity) tfdiags.Diagnostics { var diags tfdiags.Diagnostics + checkState := ctx.Checks() + if !checkState.ConfigHasChecks(self.ConfigCheckable()) { + // We have nothing to do if this object doesn't have any checks, + // but the "rules" slice should agree that we don't. + if ct := len(rules); ct != 0 { + panic(fmt.Sprintf("check state says that %s should have no rules, but it has %d", self, ct)) + } + return diags + } + if len(rules) == 0 { // Nothing to do return nil @@ -38,17 +48,26 @@ func evalCheckRules(typ addrs.CheckType, rules []*configs.CheckRule, ctx EvalCon severity := diagSeverity.ToHCL() for i, rule := range rules { - checkAddr := self.Check(typ, i) - - conditionResult, ruleDiags := evalCheckRule(typ, rule, ctx, self, keyData, severity) + result, ruleDiags := evalCheckRule(typ, rule, ctx, self, keyData, severity) diags = diags.Append(ruleDiags) - ctx.Conditions().SetResult(checkAddr, conditionResult) + + log.Printf("[TRACE] evalCheckRules: %s status is now %s", self, result.Status) + if result.Status == checks.StatusFail { + checkState.ReportCheckFailure(self, typ, i, result.FailureMessage) + } else { + checkState.ReportCheckResult(self, typ, i, result.Status) + } } return diags } -func evalCheckRule(typ addrs.CheckType, rule *configs.CheckRule, ctx EvalContext, self addrs.Checkable, keyData instances.RepetitionData, severity hcl.DiagnosticSeverity) (*plans.ConditionResult, tfdiags.Diagnostics) { +type checkResult struct { + Status checks.Status + FailureMessage string +} + +func evalCheckRule(typ addrs.CheckType, rule *configs.CheckRule, ctx EvalContext, self addrs.Checkable, keyData instances.RepetitionData, severity hcl.DiagnosticSeverity) (checkResult, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics const errInvalidCondition = "Invalid condition result" @@ -58,12 +77,6 @@ func evalCheckRule(typ addrs.CheckType, rule *configs.CheckRule, ctx EvalContext diags = diags.Append(moreDiags) refs = append(refs, moreRefs...) - conditionResult := &plans.ConditionResult{ - Address: self, - Result: cty.UnknownVal(cty.Bool), - Type: typ, - } - var selfReference addrs.Referenceable // Only resource postconditions can refer to self if typ == addrs.ResourcePostcondition { @@ -79,104 +92,146 @@ func evalCheckRule(typ addrs.CheckType, rule *configs.CheckRule, ctx EvalContext hclCtx, moreDiags := scope.EvalContext(refs) diags = diags.Append(moreDiags) - result, hclDiags := rule.Condition.Value(hclCtx) + resultVal, hclDiags := rule.Condition.Value(hclCtx) diags = diags.Append(hclDiags) - errorValue, errorDiags := rule.ErrorMessage.Value(hclCtx) - diags = diags.Append(errorDiags) + // NOTE: Intentionally not passing the caller's selected severity in here, + // because this reports errors in the configuration itself, not the failure + // of an otherwise-valid condition. + errorMessage, moreDiags := evalCheckErrorMessage(rule.ErrorMessage, hclCtx) + diags = diags.Append(moreDiags) if diags.HasErrors() { log.Printf("[TRACE] evalCheckRule: %s: %s", typ, diags.Err().Error()) } - if !result.IsKnown() { + if !resultVal.IsKnown() { // We'll wait until we've learned more, then. - return conditionResult, diags + return checkResult{Status: checks.StatusUnknown}, diags } - - if result.IsNull() { + if resultVal.IsNull() { + // NOTE: Intentionally not passing the caller's selected severity in here, + // because this reports errors in the configuration itself, not the failure + // of an otherwise-valid condition. diags = diags.Append(&hcl.Diagnostic{ - Severity: severity, + Severity: hcl.DiagError, Summary: errInvalidCondition, Detail: "Condition expression must return either true or false, not null.", Subject: rule.Condition.Range().Ptr(), Expression: rule.Condition, EvalContext: hclCtx, }) - conditionResult.Result = cty.False - conditionResult.ErrorMessage = "Condition expression must return either true or false, not null." - return conditionResult, diags + return checkResult{Status: checks.StatusError}, diags } var err error - result, err = convert.Convert(result, cty.Bool) + resultVal, err = convert.Convert(resultVal, cty.Bool) if err != nil { + // NOTE: Intentionally not passing the caller's selected severity in here, + // because this reports errors in the configuration itself, not the failure + // of an otherwise-valid condition. detail := fmt.Sprintf("Invalid condition result value: %s.", tfdiags.FormatError(err)) diags = diags.Append(&hcl.Diagnostic{ - Severity: severity, + Severity: hcl.DiagError, Summary: errInvalidCondition, Detail: detail, Subject: rule.Condition.Range().Ptr(), Expression: rule.Condition, EvalContext: hclCtx, }) - conditionResult.Result = cty.False - conditionResult.ErrorMessage = detail - return conditionResult, diags + return checkResult{Status: checks.StatusError}, diags } // The condition result may be marked if the expression refers to a // sensitive value. - result, _ = result.Unmark() - conditionResult.Result = result + resultVal, _ = resultVal.Unmark() - if result.True() { - return conditionResult, diags + status := checks.StatusForCtyValue(resultVal) + + if status != checks.StatusFail { + return checkResult{Status: status}, diags } - var errorMessage string - if !errorDiags.HasErrors() && errorValue.IsKnown() && !errorValue.IsNull() { - var err error - errorValue, err = convert.Convert(errorValue, cty.String) - if err != nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: severity, - Summary: "Invalid error message", - Detail: fmt.Sprintf("Unsuitable value for error message: %s.", tfdiags.FormatError(err)), - Subject: rule.ErrorMessage.Range().Ptr(), - Expression: rule.ErrorMessage, - EvalContext: hclCtx, - }) - } else { - if marks.Has(errorValue, marks.Sensitive) { - diags = diags.Append(&hcl.Diagnostic{ - Severity: severity, - - Summary: "Error message refers to sensitive values", - Detail: `The error expression used to explain this condition refers to sensitive values. Terraform will not display the resulting message. - -You can correct this by removing references to sensitive values, or by carefully using the nonsensitive() function if the expression will not reveal the sensitive data.`, - - Subject: rule.ErrorMessage.Range().Ptr(), - Expression: rule.ErrorMessage, - EvalContext: hclCtx, - }) - errorMessage = "The error message included a sensitive value, so it will not be displayed." - } else { - errorMessage = strings.TrimSpace(errorValue.AsString()) - } - } - } - if errorMessage == "" { - errorMessage = "Failed to evaluate condition error message." + errorMessageForDiags := errorMessage + if errorMessageForDiags == "" { + errorMessageForDiags = "This check failed, but has an invalid error message as described in the other accompanying messages." } diags = diags.Append(&hcl.Diagnostic{ + // The caller gets to choose the severity of this one, because we + // treat condition failures as warnings in the presence of + // certain special planning options. Severity: severity, Summary: fmt.Sprintf("%s failed", typ.Description()), - Detail: errorMessage, + Detail: errorMessageForDiags, Subject: rule.Condition.Range().Ptr(), Expression: rule.Condition, EvalContext: hclCtx, }) - conditionResult.ErrorMessage = errorMessage - return conditionResult, diags + + return checkResult{ + Status: status, + FailureMessage: errorMessage, + }, diags +} + +// evalCheckErrorMessage makes a best effort to evaluate the given expression, +// as an error message string. +// +// It will either return a non-empty message string or it'll return diagnostics +// with either errors or warnings that explain why the given expression isn't +// acceptable. +func evalCheckErrorMessage(expr hcl.Expression, hclCtx *hcl.EvalContext) (string, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + + val, hclDiags := expr.Value(hclCtx) + diags = diags.Append(hclDiags) + if hclDiags.HasErrors() { + return "", diags + } + + val, err := convert.Convert(val, cty.String) + if err != nil { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid error message", + Detail: fmt.Sprintf("Unsuitable value for error message: %s.", tfdiags.FormatError(err)), + Subject: expr.Range().Ptr(), + Expression: expr, + EvalContext: hclCtx, + }) + return "", diags + } + if !val.IsKnown() { + return "", diags + } + if val.IsNull() { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid error message", + Detail: "Unsuitable value for error message: must not be null.", + Subject: expr.Range().Ptr(), + Expression: expr, + EvalContext: hclCtx, + }) + return "", diags + } + + val, valMarks := val.Unmark() + if _, sensitive := valMarks[marks.Sensitive]; sensitive { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagWarning, + Summary: "Error message refers to sensitive values", + Detail: `The error expression used to explain this condition refers to sensitive values, so Terraform will not display the resulting message. + +You can correct this by removing references to sensitive values, or by carefully using the nonsensitive() function if the expression will not reveal the sensitive data.`, + Subject: expr.Range().Ptr(), + Expression: expr, + EvalContext: hclCtx, + }) + return "", diags + } + + // NOTE: We've discarded any other marks the string might have been carrying, + // aside from the sensitive mark. + + return strings.TrimSpace(val.AsString()), diags } diff --git a/internal/terraform/eval_context.go b/internal/terraform/eval_context.go index 165500ddae..fedf223051 100644 --- a/internal/terraform/eval_context.go +++ b/internal/terraform/eval_context.go @@ -3,6 +3,7 @@ package terraform import ( "github.com/hashicorp/hcl/v2" "github.com/hashicorp/terraform/internal/addrs" + "github.com/hashicorp/terraform/internal/checks" "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/instances" "github.com/hashicorp/terraform/internal/lang" @@ -164,9 +165,9 @@ type EvalContext interface { // the global state. State() *states.SyncState - // Conditions returns the writer object that can be used to store condition - // block results as they are evaluated. - Conditions() *plans.ConditionsSync + // Checks returns the object that tracks the state of any custom checks + // declared in the configuration. + Checks() *checks.State // RefreshState returns a wrapper object that provides safe concurrent // access to the state used to store the most recently refreshed resource diff --git a/internal/terraform/eval_context_builtin.go b/internal/terraform/eval_context_builtin.go index 2b2f394fdf..d66b9dd080 100644 --- a/internal/terraform/eval_context_builtin.go +++ b/internal/terraform/eval_context_builtin.go @@ -6,6 +6,7 @@ import ( "log" "sync" + "github.com/hashicorp/terraform/internal/checks" "github.com/hashicorp/terraform/internal/instances" "github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/providers" @@ -66,7 +67,7 @@ type BuiltinEvalContext struct { ProvisionerLock *sync.Mutex ChangesValue *plans.ChangesSync StateValue *states.SyncState - ConditionsValue *plans.ConditionsSync + ChecksValue *checks.State RefreshStateValue *states.SyncState PrevRunStateValue *states.SyncState InstanceExpanderValue *instances.Expander @@ -482,8 +483,8 @@ func (ctx *BuiltinEvalContext) State() *states.SyncState { return ctx.StateValue } -func (ctx *BuiltinEvalContext) Conditions() *plans.ConditionsSync { - return ctx.ConditionsValue +func (ctx *BuiltinEvalContext) Checks() *checks.State { + return ctx.ChecksValue } func (ctx *BuiltinEvalContext) RefreshState() *states.SyncState { diff --git a/internal/terraform/eval_context_mock.go b/internal/terraform/eval_context_mock.go index 508d981985..24159ef955 100644 --- a/internal/terraform/eval_context_mock.go +++ b/internal/terraform/eval_context_mock.go @@ -4,6 +4,7 @@ import ( "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/terraform/internal/addrs" + "github.com/hashicorp/terraform/internal/checks" "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/instances" "github.com/hashicorp/terraform/internal/lang" @@ -133,8 +134,8 @@ type MockEvalContext struct { StateCalled bool StateState *states.SyncState - ConditionsCalled bool - ConditionsConditions *plans.ConditionsSync + ChecksCalled bool + ChecksState *checks.State RefreshStateCalled bool RefreshStateState *states.SyncState @@ -374,9 +375,9 @@ func (c *MockEvalContext) State() *states.SyncState { return c.StateState } -func (c *MockEvalContext) Conditions() *plans.ConditionsSync { - c.ConditionsCalled = true - return c.ConditionsConditions +func (c *MockEvalContext) Checks() *checks.State { + c.ChecksCalled = true + return c.ChecksState } func (c *MockEvalContext) RefreshState() *states.SyncState { diff --git a/internal/terraform/graph_walk_context.go b/internal/terraform/graph_walk_context.go index 9472189e43..8060954059 100644 --- a/internal/terraform/graph_walk_context.go +++ b/internal/terraform/graph_walk_context.go @@ -7,6 +7,7 @@ import ( "github.com/zclconf/go-cty/cty" "github.com/hashicorp/terraform/internal/addrs" + "github.com/hashicorp/terraform/internal/checks" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/instances" @@ -29,7 +30,7 @@ type ContextGraphWalker struct { RefreshState *states.SyncState // Used for safe concurrent access to state PrevRunState *states.SyncState // Used for safe concurrent access to state Changes *plans.ChangesSync // Used for safe concurrent writes to changes - Conditions *plans.ConditionsSync // Used for safe concurrent writes to conditions + Checks *checks.State // Used for safe concurrent writes of checkable objects and their check results InstanceExpander *instances.Expander // Tracks our gradual expansion of module and resource instances MoveResults refactoring.MoveResults // Read-only record of earlier processing of move statements Operation walkOperation @@ -99,7 +100,7 @@ func (w *ContextGraphWalker) EvalContext() EvalContext { ProvisionerCache: w.provisionerCache, ProvisionerLock: &w.provisionerLock, ChangesValue: w.Changes, - ConditionsValue: w.Conditions, + ChecksValue: w.Checks, StateValue: w.State, RefreshStateValue: w.RefreshState, PrevRunStateValue: w.PrevRunState, diff --git a/internal/terraform/node_output.go b/internal/terraform/node_output.go index 55b0717524..5ce32c2445 100644 --- a/internal/terraform/node_output.go +++ b/internal/terraform/node_output.go @@ -54,9 +54,22 @@ func (n *nodeExpandOutput) DynamicExpand(ctx EvalContext) (*Graph, error) { expander := ctx.InstanceExpander() changes := ctx.Changes() + // If this is an output value that participates in custom condition checks + // (i.e. it has preconditions or postconditions) then the check state + // wants to know the addresses of the checkable objects so that it can + // treat them as unknown status if we encounter an error before actually + // visiting the checks. + var checkableAddrs addrs.Set[addrs.Checkable] + if checkState := ctx.Checks(); checkState.ConfigHasChecks(n.Addr.InModule(n.Module)) { + checkableAddrs = addrs.MakeSet[addrs.Checkable]() + } + var g Graph for _, module := range expander.ExpandModule(n.Module) { absAddr := n.Addr.Absolute(module) + if checkableAddrs != nil { + checkableAddrs.Add(absAddr) + } // Find any recorded change for this output var change *plans.OutputChangeSrc @@ -83,6 +96,12 @@ func (n *nodeExpandOutput) DynamicExpand(ctx EvalContext) (*Graph, error) { log.Printf("[TRACE] Expanding output: adding %s as %T", o.Addr.String(), o) g.Add(o) } + + if checkableAddrs != nil { + checkState := ctx.Checks() + checkState.ReportCheckableObjects(n.Addr.InModule(n.Module), checkableAddrs) + } + return &g, nil } diff --git a/internal/terraform/node_output_test.go b/internal/terraform/node_output_test.go index 892427c77c..80d60539e8 100644 --- a/internal/terraform/node_output_test.go +++ b/internal/terraform/node_output_test.go @@ -8,6 +8,7 @@ import ( "github.com/zclconf/go-cty/cty" "github.com/hashicorp/terraform/internal/addrs" + "github.com/hashicorp/terraform/internal/checks" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/lang/marks" "github.com/hashicorp/terraform/internal/states" @@ -17,6 +18,7 @@ func TestNodeApplyableOutputExecute_knownValue(t *testing.T) { ctx := new(MockEvalContext) ctx.StateState = states.NewState().SyncWrapper() ctx.RefreshStateState = states.NewState().SyncWrapper() + ctx.ChecksState = checks.NewState(nil) config := &configs.Output{Name: "map-output"} addr := addrs.OutputValue{Name: config.Name}.Absolute(addrs.RootModuleInstance) @@ -65,6 +67,7 @@ func TestNodeApplyableOutputExecute_noState(t *testing.T) { func TestNodeApplyableOutputExecute_invalidDependsOn(t *testing.T) { ctx := new(MockEvalContext) ctx.StateState = states.NewState().SyncWrapper() + ctx.ChecksState = checks.NewState(nil) config := &configs.Output{ Name: "map-output", @@ -95,6 +98,7 @@ func TestNodeApplyableOutputExecute_invalidDependsOn(t *testing.T) { func TestNodeApplyableOutputExecute_sensitiveValueNotOutput(t *testing.T) { ctx := new(MockEvalContext) ctx.StateState = states.NewState().SyncWrapper() + ctx.ChecksState = checks.NewState(nil) config := &configs.Output{Name: "map-output"} addr := addrs.OutputValue{Name: config.Name}.Absolute(addrs.RootModuleInstance) @@ -116,6 +120,7 @@ func TestNodeApplyableOutputExecute_sensitiveValueNotOutput(t *testing.T) { func TestNodeApplyableOutputExecute_sensitiveValueAndOutput(t *testing.T) { ctx := new(MockEvalContext) ctx.StateState = states.NewState().SyncWrapper() + ctx.ChecksState = checks.NewState(nil) config := &configs.Output{ Name: "map-output", diff --git a/internal/terraform/node_resource_plan.go b/internal/terraform/node_resource_plan.go index d478bc3ce0..c99654e3b0 100644 --- a/internal/terraform/node_resource_plan.go +++ b/internal/terraform/node_resource_plan.go @@ -320,6 +320,19 @@ func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) { state := ctx.State().Lock() defer ctx.State().Unlock() + // If this is a resource that participates in custom condition checks + // (i.e. it has preconditions or postconditions) then the check state + // wants to know the addresses of the checkable objects so that it can + // treat them as unknown status if we encounter an error before actually + // visiting the checks. + if checkState := ctx.Checks(); checkState.ConfigHasChecks(n.NodeAbstractResource.Addr) { + checkableAddrs := addrs.MakeSet[addrs.Checkable]() + for _, addr := range instanceAddrs { + checkableAddrs.Add(addr) + } + checkState.ReportCheckableObjects(n.NodeAbstractResource.Addr, checkableAddrs) + } + // The concrete resource factory we'll use concreteResource := func(a *NodeAbstractResourceInstance) dag.Vertex { // check if this node is being imported first