From 3ec4e9bbb1145e3b4e3be84be2f59836efdd4fe7 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 8 Feb 2023 16:46:43 -0800 Subject: [PATCH 01/17] providers: Allow providers to "defer" certain requests For any request that can occur during the planning phase there is a chance that either a resource configuration or its associated provider configuration will contain unknown values that are placeholders for results of operations that haven't yet completed. Ideally a provider would be able to just do its best to predict the outcome in spite of the partial information, but in practice that isn't always possible. In those more complex situations it's better to let the provider explicitly decline to complete the operation and have Terraform Core defer it for a future run when there's hopefully more information available due to having applied other changes upstream. This commit does not yet introduce the idea of "deferred changes" into Terraform Core, so as a temporary step Terraform Core will just return an error if a provider tries to defer anything. In future commits we'll teach Terraform Core how to handle this more gracefully by saving partial results into the plan as "deferred changes" and then continuing on to downstream resources to try to gather as much information as possible to help the user understand the likely effects of those deferred actions. --- docs/plugin-protocol/tfplugin6.5.proto | 534 +++++ internal/plugin6/convert/deferred.go | 29 + internal/plugin6/grpc_provider.go | 44 +- internal/providers/deferred.go | 68 + internal/providers/provider.go | 35 + .../node_resource_abstract_instance.go | 41 + internal/terraform/node_resource_import.go | 14 + internal/terraform/upgrade_resource_state.go | 14 + internal/tfplugin6/tfplugin6.pb.go | 1711 +++++++++++------ internal/tfplugin6/tfplugin6.proto | 2 +- 10 files changed, 1858 insertions(+), 634 deletions(-) create mode 100644 docs/plugin-protocol/tfplugin6.5.proto create mode 100644 internal/plugin6/convert/deferred.go create mode 100644 internal/providers/deferred.go diff --git a/docs/plugin-protocol/tfplugin6.5.proto b/docs/plugin-protocol/tfplugin6.5.proto new file mode 100644 index 000000000000..b12f0d63ff38 --- /dev/null +++ b/docs/plugin-protocol/tfplugin6.5.proto @@ -0,0 +1,534 @@ +// Terraform Plugin RPC protocol version 6.5 +// +// This file defines version 6.5 of the RPC protocol. To implement a plugin +// against this protocol, copy this definition into your own codebase and +// use protoc to generate stubs for your target language. +// +// This file will not be updated. Any minor versions of protocol 6 to follow +// should copy this file and modify the copy while maintaing backwards +// compatibility. Breaking changes, if any are required, will come +// in a subsequent major version with its own separate proto definition. +// +// Note that only the proto files included in a release tag of Terraform are +// official protocol releases. Proto files taken from other commits may include +// incomplete changes or features that did not make it into a final release. +// In all reasonable cases, plugin developers should take the proto file from +// the tag of the most recent release of Terraform, and not from the main +// branch or any other development branch. +// +syntax = "proto3"; +option go_package = "github.com/hashicorp/terraform/internal/tfplugin6"; + +package tfplugin6; + +// DynamicValue is an opaque encoding of terraform data, with the field name +// indicating the encoding scheme used. +message DynamicValue { + bytes msgpack = 1; + bytes json = 2; +} + +message Diagnostic { + enum Severity { + INVALID = 0; + ERROR = 1; + WARNING = 2; + } + Severity severity = 1; + string summary = 2; + string detail = 3; + AttributePath attribute = 4; +} + +message AttributePath { + message Step { + oneof selector { + // Set "attribute_name" to represent looking up an attribute + // in the current object value. + string attribute_name = 1; + // Set "element_key_*" to represent looking up an element in + // an indexable collection type. + string element_key_string = 2; + int64 element_key_int = 3; + } + } + repeated Step steps = 1; +} + +message StopProvider { + message Request { + } + message Response { + string Error = 1; + } +} + +// RawState holds the stored state for a resource to be upgraded by the +// provider. It can be in one of two formats, the current json encoded format +// in bytes, or the legacy flatmap format as a map of strings. +message RawState { + bytes json = 1; + map flatmap = 2; +} + +enum StringKind { + PLAIN = 0; + MARKDOWN = 1; +} + +// Schema is the configuration schema for a Resource or Provider. +message Schema { + message Block { + int64 version = 1; + repeated Attribute attributes = 2; + repeated NestedBlock block_types = 3; + string description = 4; + StringKind description_kind = 5; + bool deprecated = 6; + } + + message Attribute { + string name = 1; + bytes type = 2; + Object nested_type = 10; + string description = 3; + bool required = 4; + bool optional = 5; + bool computed = 6; + bool sensitive = 7; + StringKind description_kind = 8; + bool deprecated = 9; + } + + message NestedBlock { + enum NestingMode { + INVALID = 0; + SINGLE = 1; + LIST = 2; + SET = 3; + MAP = 4; + GROUP = 5; + } + + string type_name = 1; + Block block = 2; + NestingMode nesting = 3; + int64 min_items = 4; + int64 max_items = 5; + } + + message Object { + enum NestingMode { + INVALID = 0; + SINGLE = 1; + LIST = 2; + SET = 3; + MAP = 4; + } + + repeated Attribute attributes = 1; + NestingMode nesting = 3; + + // MinItems and MaxItems were never used in the protocol, and have no + // effect on validation. + int64 min_items = 4 [deprecated = true]; + int64 max_items = 5 [deprecated = true]; + } + + // The version of the schema. + // Schemas are versioned, so that providers can upgrade a saved resource + // state when the schema is changed. + int64 version = 1; + + // Block is the top level configuration block for this schema. + Block block = 2; +} + +// ServerCapabilities allows providers to communicate extra information +// regarding supported protocol features. This is used to indicate +// availability of certain forward-compatible changes which may be optional +// in a major protocol version, but cannot be tested for directly. +message ServerCapabilities { + // The plan_destroy capability signals that a provider expects a call + // to PlanResourceChange when a resource is going to be destroyed. + bool plan_destroy = 1; + + // The get_provider_schema_optional capability indicates that this + // provider does not require calling GetProviderSchema to operate + // normally, and the caller can used a cached copy of the provider's + // schema. + bool get_provider_schema_optional = 2; +} + +// DeferredAction represents a reason why a particular action cannot be taken +// until Terraform can provide concrete values instead of unknown value +// placeholders. +message DeferredAction { + oneof reason { + // other_reason is a fallback reason to use if none of the other + // specific reasons seem appropriate. Terraform will show a generic + // message in the UI in this case, acknowledging that the operation + // was deferred but with no specific reason why. Terraform Core will + // also treat any unrecognized reason type defined in later versions + // of this protocol as if they were other_reason. + OtherReason other_reason = 1; + + // provider_config_unknown represents that the action was deferred + // because a necessary part of the corresponding provider configuration + // is not yet known enough to proceed. + ConfigUnknown provider_config_unknown = 2; + + // resource_config_unknown represents that the action was deferred + // because a necessary part of the resource configuration + // is not yet known enough to proceed. + ConfigUnknown resource_config_unknown = 3; + } + + message OtherReason {} + message ConfigUnknown { + // The path to the attribute whose unknown value caused the deferral. + // Provide as precise a path as possible, but it's okay to describe + // a shorter path if e.g. the problem is inside a set element and + // therefore not fully addressable as an AttributePath. + AttributePath attribute = 1; + } +} + +service Provider { + //////// Information about what a provider supports/expects + + // GetMetadata returns upfront information about server capabilities and + // supported resource types without requiring the server to instantiate all + // schema information, which may be memory intensive. This RPC is optional, + // where clients may receive an unimplemented RPC error. Clients should + // ignore the error and call the GetProviderSchema RPC as a fallback. + rpc GetMetadata(GetMetadata.Request) returns (GetMetadata.Response); + + // GetSchema returns schema information for the provider, data resources, + // and managed resources. + rpc GetProviderSchema(GetProviderSchema.Request) returns (GetProviderSchema.Response); + rpc ValidateProviderConfig(ValidateProviderConfig.Request) returns (ValidateProviderConfig.Response); + rpc ValidateResourceConfig(ValidateResourceConfig.Request) returns (ValidateResourceConfig.Response); + rpc ValidateDataResourceConfig(ValidateDataResourceConfig.Request) returns (ValidateDataResourceConfig.Response); + rpc UpgradeResourceState(UpgradeResourceState.Request) returns (UpgradeResourceState.Response); + + //////// One-time initialization, called before other functions below + rpc ConfigureProvider(ConfigureProvider.Request) returns (ConfigureProvider.Response); + + //////// Managed Resource Lifecycle + rpc ReadResource(ReadResource.Request) returns (ReadResource.Response); + rpc PlanResourceChange(PlanResourceChange.Request) returns (PlanResourceChange.Response); + rpc ApplyResourceChange(ApplyResourceChange.Request) returns (ApplyResourceChange.Response); + rpc ImportResourceState(ImportResourceState.Request) returns (ImportResourceState.Response); + + rpc ReadDataSource(ReadDataSource.Request) returns (ReadDataSource.Response); + + //////// Graceful Shutdown + rpc StopProvider(StopProvider.Request) returns (StopProvider.Response); +} + +message GetMetadata { + message Request { + } + + message Response { + ServerCapabilities server_capabilities = 1; + repeated Diagnostic diagnostics = 2; + repeated DataSourceMetadata data_sources = 3; + repeated ResourceMetadata resources = 4; + } + + message DataSourceMetadata { + string type_name = 1; + } + + message ResourceMetadata { + string type_name = 1; + } +} + +message GetProviderSchema { + message Request { + } + message Response { + Schema provider = 1; + map resource_schemas = 2; + map data_source_schemas = 3; + repeated Diagnostic diagnostics = 4; + Schema provider_meta = 5; + ServerCapabilities server_capabilities = 6; + } +} + +message ValidateProviderConfig { + message Request { + DynamicValue config = 1; + } + message Response { + repeated Diagnostic diagnostics = 2; + } +} + +message UpgradeResourceState { + // Request is the message that is sent to the provider during the + // UpgradeResourceState RPC. + // + // This message intentionally does not include configuration data as any + // configuration-based or configuration-conditional changes should occur + // during the PlanResourceChange RPC. Additionally, the configuration is + // not guaranteed to exist (in the case of resource destruction), be wholly + // known, nor match the given prior state, which could lead to unexpected + // provider behaviors for practitioners. + message Request { + string type_name = 1; + + // version is the schema_version number recorded in the state file + int64 version = 2; + + // raw_state is the raw states as stored for the resource. Core does + // not have access to the schema of prior_version, so it's the + // provider's responsibility to interpret this value using the + // appropriate older schema. The raw_state will be the json encoded + // state, or a legacy flat-mapped format. + RawState raw_state = 3; + + // Terraform Core will set this if it will honor elements of + // "deferred" in the response. If this field is not set then + // a provider must return error diagnostics whenever it has + // insufficient information to upgrade the state date to the + // latest schema version. + bool defer_allowed = 4; + } + message Response { + // new_state is a msgpack-encoded data structure that, when interpreted with + // the _current_ schema for this resource type, is functionally equivalent to + // that which was given in prior_state_raw. + DynamicValue upgraded_state = 1; + + // diagnostics describes any errors encountered during migration that could not + // be safely resolved, and warnings about any possibly-risky assumptions made + // in the upgrade process. + repeated Diagnostic diagnostics = 2; + + // If the request set defer_allowed to true then the provider may + // annotate a response with one or more deferral reasons, in which + // case Terraform Core will treat the object as completely unknown + // while planning downstream actions. Providers should avoid deferring + // an upgrade if at all possible, by implementing upgrade logic entirely + // within the provider and not accessing any external system. + // Don't set this if the request did not have defer_allowed set; + // instead return error diagnostics explaining why upgrading is + // impossible. + repeated DeferredAction deferred = 3; + } +} + +message ValidateResourceConfig { + message Request { + string type_name = 1; + DynamicValue config = 2; + } + message Response { + repeated Diagnostic diagnostics = 1; + } +} + +message ValidateDataResourceConfig { + message Request { + string type_name = 1; + DynamicValue config = 2; + } + message Response { + repeated Diagnostic diagnostics = 1; + } +} + +message ConfigureProvider { + message Request { + string terraform_version = 1; + DynamicValue config = 2; + } + message Response { + repeated Diagnostic diagnostics = 1; + } +} + +message ReadResource { + // Request is the message that is sent to the provider during the + // ReadResource RPC. + // + // This message intentionally does not include configuration data as any + // configuration-based or configuration-conditional changes should occur + // during the PlanResourceChange RPC. Additionally, the configuration is + // not guaranteed to be wholly known nor match the given prior state, which + // could lead to unexpected provider behaviors for practitioners. + message Request { + string type_name = 1; + DynamicValue current_state = 2; + bytes private = 3; + DynamicValue provider_meta = 4; + + // Terraform Core will set this if it will honor elements of + // "deferred" in the response. If this field is not set then + // a provider must return error diagnostics whenever it has + // insufficient information to retrieve the current state + // of the object. + bool defer_allowed = 5; + } + message Response { + DynamicValue new_state = 1; + repeated Diagnostic diagnostics = 2; + bytes private = 3; + + // If the request set defer_allowed to true then the provider may + // annotate a response with one or more deferral reasons, in which + // case Terraform Core will still use the partial "new_state" value + // to plan downstream actions but will then defer them until a + // future run where a similar ReadResource request can return + // a complete result. + // Don't set this if the request did not have defer_allowed set; + // instead return error diagnostics explaining why reading is + // impossible. + repeated DeferredAction deferred = 4; + } +} + +message PlanResourceChange { + message Request { + string type_name = 1; + DynamicValue prior_state = 2; + DynamicValue proposed_new_state = 3; + DynamicValue config = 4; + bytes prior_private = 5; + DynamicValue provider_meta = 6; + + // Terraform Core will set this if it will honor elements of + // "deferred" in the response. If this field is not set then + // a provider must return error diagnostics whenever it has + // insufficient information to fully plan the change. + bool defer_allowed = 7; + } + + message Response { + DynamicValue planned_state = 1; + repeated AttributePath requires_replace = 2; + bytes planned_private = 3; + repeated Diagnostic diagnostics = 4; + + // If the request set defer_allowed to true then the provider may + // annotate a response with one or more deferral reasons, in which + // case Terraform Core will still present the planned action to the + // user for review as a deferred action but will not actually apply + // the change without first asking for a new plan with more information. + // Don't set this if the request did not have defer_allowed set; + // instead return error diagnostics explaining why planning is + // impossible. + repeated DeferredAction deferred = 6; + + // This may be set only by the helper/schema "SDK" in the main Terraform + // repository, to request that Terraform Core >=0.12 permit additional + // inconsistencies that can result from the legacy SDK type system + // and its imprecise mapping to the >=0.12 type system. + // The change in behavior implied by this flag makes sense only for the + // specific details of the legacy SDK type system, and are not a general + // mechanism to avoid proper type handling in providers. + // + // ==== DO NOT USE THIS ==== + // ==== THIS MUST BE LEFT UNSET IN ALL OTHER SDKS ==== + // ==== DO NOT USE THIS ==== + bool legacy_type_system = 5; + } +} + +message ApplyResourceChange { + message Request { + string type_name = 1; + DynamicValue prior_state = 2; + DynamicValue planned_state = 3; + DynamicValue config = 4; + bytes planned_private = 5; + DynamicValue provider_meta = 6; + } + message Response { + DynamicValue new_state = 1; + bytes private = 2; + repeated Diagnostic diagnostics = 3; + + // This may be set only by the helper/schema "SDK" in the main Terraform + // repository, to request that Terraform Core >=0.12 permit additional + // inconsistencies that can result from the legacy SDK type system + // and its imprecise mapping to the >=0.12 type system. + // The change in behavior implied by this flag makes sense only for the + // specific details of the legacy SDK type system, and are not a general + // mechanism to avoid proper type handling in providers. + // + // ==== DO NOT USE THIS ==== + // ==== THIS MUST BE LEFT UNSET IN ALL OTHER SDKS ==== + // ==== DO NOT USE THIS ==== + bool legacy_type_system = 4; + } +} + +message ImportResourceState { + message Request { + string type_name = 1; + string id = 2; + + // Terraform Core will set this if it will honor elements of + // "deferred" in the response. If this field is not set then + // a provider must return error diagnostics whenever it has + // insufficient information to import the requested object. + bool defer_allowed = 4; + } + + message ImportedResource { + string type_name = 1; + DynamicValue state = 2; + bytes private = 3; + } + + message Response { + repeated ImportedResource imported_resources = 1; + repeated Diagnostic diagnostics = 2; + + // If the request set defer_allowed to true then the provider may + // annotate a response with one or more deferral reasons, in which + // case Terraform Core will treat this object as entirely unknown + // during downstream planning and defer the import until a later + // run when there's hopefully more information available. + // + // Don't set this if the request did not have defer_allowed set; + // instead return error diagnostics explaining why importing is + // impossible. + repeated DeferredAction deferred = 3; + } +} + +message ReadDataSource { + message Request { + string type_name = 1; + DynamicValue config = 2; + DynamicValue provider_meta = 3; + + // Terraform Core will set this if it will honor elements of + // "deferred" in the response. If this field is not set then + // a provider must return error diagnostics whenever it has + // insufficient information to fetch the requested remote object. + bool defer_allowed = 4; + } + message Response { + DynamicValue state = 1; + repeated Diagnostic diagnostics = 2; + + // If the request set defer_allowed to true then the provider may + // annotate a response with one or more deferral reasons, in which + // case Terraform Core will still use the partial "state" value + // to plan downstream actions but will then defer them until a + // future run where a similar ReadDataSource request can return + // a complete result. + // Don't set this if the request did not have defer_allowed set; + // instead return error diagnostics explaining why reading is + // impossible. + repeated DeferredAction deferred = 3; + } +} diff --git a/internal/plugin6/convert/deferred.go b/internal/plugin6/convert/deferred.go new file mode 100644 index 000000000000..f4f2ee5f3904 --- /dev/null +++ b/internal/plugin6/convert/deferred.go @@ -0,0 +1,29 @@ +package convert + +import ( + "github.com/hashicorp/terraform/internal/providers" + "github.com/hashicorp/terraform/internal/tfplugin6" +) + +func DeferredReasonFromProto(protoReason *tfplugin6.DeferredAction) providers.DeferredReason { + if protoReason == nil { + // Should never happen, but we'll treat it as an "other" reason + // for robustness against oddly-behaving providers. + return providers.NewDeferredReasonOther() + } + + switch protoReason := protoReason.Reason.(type) { + case *tfplugin6.DeferredAction_ProviderConfigUnknown: + return providers.NewDeferredReasonUnknownProviderConfig( + AttributePathToPath(protoReason.ProviderConfigUnknown.Attribute), + ) + case *tfplugin6.DeferredAction_ResourceConfigUnknown: + return providers.NewDeferredReasonUnknownResourceConfig( + AttributePathToPath(protoReason.ResourceConfigUnknown.Attribute), + ) + default: + // Fallback for all unrecognized reasons, in case later protocol + // versions define additional ones. + return providers.NewDeferredReasonOther() + } +} diff --git a/internal/plugin6/grpc_provider.go b/internal/plugin6/grpc_provider.go index 70e65cd7f9ca..912114733c48 100644 --- a/internal/plugin6/grpc_provider.go +++ b/internal/plugin6/grpc_provider.go @@ -285,6 +285,7 @@ func (p *GRPCProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequ Json: r.RawStateJSON, Flatmap: r.RawStateFlatmap, }, + DeferAllowed: true, } protoResp, err := p.client.UpgradeResourceState(p.ctx, protoReq) @@ -307,6 +308,13 @@ func (p *GRPCProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequ } resp.UpgradedState = state + if len(protoResp.Deferred) != 0 { + resp.Deferred = make([]providers.DeferredReason, len(protoResp.Deferred)) + for i, raw := range protoResp.Deferred { + resp.Deferred[i] = convert.DeferredReasonFromProto(raw) + } + } + return resp } @@ -381,6 +389,7 @@ func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp provi TypeName: r.TypeName, CurrentState: &proto6.DynamicValue{Msgpack: mp}, Private: r.Private, + DeferAllowed: true, } if metaSchema.Block != nil { @@ -407,6 +416,13 @@ func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp provi resp.NewState = state resp.Private = protoResp.Private + if len(protoResp.Deferred) != 0 { + resp.Deferred = make([]providers.DeferredReason, len(protoResp.Deferred)) + for i, raw := range protoResp.Deferred { + resp.Deferred[i] = convert.DeferredReasonFromProto(raw) + } + } + return resp } @@ -460,6 +476,7 @@ func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) Config: &proto6.DynamicValue{Msgpack: configMP}, ProposedNewState: &proto6.DynamicValue{Msgpack: propMP}, PriorPrivate: r.PriorPrivate, + DeferAllowed: true, } if metaSchema.Block != nil { @@ -489,6 +506,13 @@ func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) resp.RequiresReplace = append(resp.RequiresReplace, convert.AttributePathToPath(p)) } + if len(protoResp.Deferred) != 0 { + resp.Deferred = make([]providers.DeferredReason, len(protoResp.Deferred)) + for i, raw := range protoResp.Deferred { + resp.Deferred[i] = convert.DeferredReasonFromProto(raw) + } + } + resp.PlannedPrivate = protoResp.PlannedPrivate resp.LegacyTypeSystem = protoResp.LegacyTypeSystem @@ -577,8 +601,9 @@ func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateReques } protoReq := &proto6.ImportResourceState_Request{ - TypeName: r.TypeName, - Id: r.ID, + TypeName: r.TypeName, + Id: r.ID, + DeferAllowed: true, } protoResp, err := p.client.ImportResourceState(p.ctx, protoReq) @@ -588,6 +613,13 @@ func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateReques } resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) + if len(protoResp.Deferred) != 0 { + resp.Deferred = make([]providers.DeferredReason, len(protoResp.Deferred)) + for i, raw := range protoResp.Deferred { + resp.Deferred[i] = convert.DeferredReasonFromProto(raw) + } + } + for _, imported := range protoResp.ImportedResources { resource := providers.ImportedResource{ TypeName: imported.TypeName, @@ -639,6 +671,7 @@ func (p *GRPCProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp p Config: &proto6.DynamicValue{ Msgpack: config, }, + DeferAllowed: true, } if metaSchema.Block != nil { @@ -664,6 +697,13 @@ func (p *GRPCProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp p } resp.State = state + if len(protoResp.Deferred) != 0 { + resp.Deferred = make([]providers.DeferredReason, len(protoResp.Deferred)) + for i, raw := range protoResp.Deferred { + resp.Deferred[i] = convert.DeferredReasonFromProto(raw) + } + } + return resp } diff --git a/internal/providers/deferred.go b/internal/providers/deferred.go new file mode 100644 index 000000000000..cc67814911dd --- /dev/null +++ b/internal/providers/deferred.go @@ -0,0 +1,68 @@ +package providers + +import ( + "github.com/zclconf/go-cty/cty" +) + +// DeferredReason describes one reason why a particular action cannot be +// completed fully in the current run, and must therefore be deferred to +// a later run where hopefully more informationis available. +// +// This is a sealed interface type whose full set of implementations lives +// inside this package. Future versions of this package may include additional +// implementations, so callers should use [DeferredReasonOther] as a placeholder +// if they encounter a [DeferredReason] type that they don't recognize. +type DeferredReason interface { + deferredReasonSigil() +} + +func NewDeferredReasonOther() DeferredReason { + return DeferredReasonOther{} +} + +func NewDeferredReasonUnknownProviderConfig(attributePath cty.Path) DeferredReason { + return DeferredReasonUnknownProviderConfig{ + AttributePath: attributePath, + } +} + +func NewDeferredReasonUnknownResourceConfig(attributePath cty.Path) DeferredReason { + return DeferredReasonUnknownResourceConfig{ + AttributePath: attributePath, + } +} + +// DeferredReasonOther is the [DeferredReason] implementation to use when +// none of the others are applicable, and also a good placeholder to use if +// a caller encounters a reason type it doesn't recognize. +type DeferredReasonOther struct{} + +func (DeferredReasonOther) deferredReasonSigil() {} + +// DeferredReasonUnknownProviderConfig is the [DeferredReason] implementation +// to represent that an unknown attribute value in the configuration of the +// provider responsible for the action prevents full planning or execution +// of the action. +type DeferredReasonUnknownProviderConfig struct { + // AttributePath is a path to the closest possible attribute to the one + // whose unknown value caused the problem, which Terraform might then use + // to highlight the particular attribute somehow in the UI. + // This path is resolved within the provider configuration, not within + // the configuration of a resource belonging to the provider. + AttributePath cty.Path +} + +func (DeferredReasonUnknownProviderConfig) deferredReasonSigil() {} + +// DeferredReasonUnknownProviderConfig is the [DeferredReason] implementation +// to represent that an unknown attribute value in the configuration of the +// resource an action relates to prevents full planning or execution of the +// action. +type DeferredReasonUnknownResourceConfig struct { + // AttributePath is a path to the closest possible attribute to the one + // whose unknown value caused the problem, which Terraform might then use + // to highlight the particular attribute somehow in the UI. + AttributePath cty.Path +} + +func (DeferredReasonUnknownResourceConfig) deferredReasonSigil() {} diff --git a/internal/providers/provider.go b/internal/providers/provider.go index bfaf2daa4026..ea5adb2ca4f2 100644 --- a/internal/providers/provider.go +++ b/internal/providers/provider.go @@ -189,6 +189,13 @@ type UpgradeResourceStateResponse struct { // Diagnostics contains any warnings or errors from the method call. Diagnostics tfdiags.Diagnostics + + // Deferred is a list of reasons why this action must be deferred to + // a later run when there's hopefully more information available. If this + // list has one or more items then Terraform will place this change and + // anything that depends on it into the "deferred changes" bucket, and + // therefore exclude it from the set of proposed changes for this run. + Deferred []DeferredReason } type ConfigureProviderRequest struct { @@ -234,6 +241,13 @@ type ReadResourceResponse struct { // Private is an opaque blob that will be stored in state along with the // resource. It is intended only for interpretation by the provider itself. Private []byte + + // Deferred is a list of reasons why this action must be deferred to + // a later run when there's hopefully more information available. If this + // list has one or more items then Terraform will place this change and + // anything that depends on it into the "deferred changes" bucket, and + // therefore exclude it from the set of proposed changes for this run. + Deferred []DeferredReason } type PlanResourceChangeRequest struct { @@ -283,6 +297,13 @@ type PlanResourceChangeResponse struct { // Diagnostics contains any warnings or errors from the method call. Diagnostics tfdiags.Diagnostics + // Deferred is a list of reasons why this action must be deferred to + // a later run when there's hopefully more information available. If this + // list has one or more items then Terraform will place this change and + // anything that depends on it into the "deferred changes" bucket, and + // therefore exclude it from the set of proposed changes for this run. + Deferred []DeferredReason + // LegacyTypeSystem is set only if the provider is using the legacy SDK // whose type system cannot be precisely mapped into the Terraform type // system. We use this to bypass certain consistency checks that would @@ -357,6 +378,13 @@ type ImportResourceStateResponse struct { // Diagnostics contains any warnings or errors from the method call. Diagnostics tfdiags.Diagnostics + + // Deferred is a list of reasons why this action must be deferred to + // a later run when there's hopefully more information available. If this + // list has one or more items then Terraform will place this change and + // anything that depends on it into the "deferred changes" bucket, and + // therefore exclude it from the set of proposed changes for this run. + Deferred []DeferredReason } // ImportedResource represents an object being imported into Terraform with the @@ -416,4 +444,11 @@ type ReadDataSourceResponse struct { // Diagnostics contains any warnings or errors from the method call. Diagnostics tfdiags.Diagnostics + + // Deferred is a list of reasons why this action must be deferred to + // a later run when there's hopefully more information available. If this + // list has one or more items then Terraform will place any change + // that depends on this result into the "deferred changes" bucket, and + // therefore exclude it from the set of proposed changes for this run. + Deferred []DeferredReason } diff --git a/internal/terraform/node_resource_abstract_instance.go b/internal/terraform/node_resource_abstract_instance.go index d3e969d6296e..056aa69a196a 100644 --- a/internal/terraform/node_resource_abstract_instance.go +++ b/internal/terraform/node_resource_abstract_instance.go @@ -643,6 +643,19 @@ func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey state if n.Config != nil { resp.Diagnostics = resp.Diagnostics.InConfigBody(n.Config.Config, n.Addr.String()) } + if len(resp.Deferred) != 0 { + // We don't yet support deferrals really, so for now we'll just treat + // them as errors. This is only a temporary shortcut and should not + // ship in any stable Terraform release. + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Not enough information to read", + Detail: fmt.Sprintf( + "The configuration of either %s or its provider contains unknown values that prevent planning.", + n.Addr.String(), + ), + }) + } diags = diags.Append(resp.Diagnostics) if diags.HasErrors() { @@ -898,6 +911,20 @@ func (n *NodeAbstractResourceInstance) plan( }) } diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config, n.Addr.String())) + if len(resp.Deferred) != 0 { + // We don't yet support deferrals really, so for now we'll just treat + // them as errors. This is only a temporary shortcut and should not + // ship in any stable Terraform release. + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Not enough information to plan", + Detail: fmt.Sprintf( + "The configuration of either %s or its provider contains unknown values that prevent planning.", + n.Addr.String(), + ), + Subject: config.DeclRange.Ptr(), + }) + } if diags.HasErrors() { return nil, nil, keyData, diags } @@ -1566,6 +1593,20 @@ func (n *NodeAbstractResourceInstance) readDataSource(ctx EvalContext, configVal }) } diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config, n.Addr.String())) + if len(resp.Deferred) != 0 { + // We don't yet support deferrals really, so for now we'll just treat + // them as errors. This is only a temporary shortcut and should not + // ship in any stable Terraform release. + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Not enough information to read", + Detail: fmt.Sprintf( + "The configuration of either %s or its provider contains unknown values that prevent planning.", + n.Addr.String(), + ), + Subject: config.DeclRange.Ptr(), + }) + } if diags.HasErrors() { return newVal, diags } diff --git a/internal/terraform/node_resource_import.go b/internal/terraform/node_resource_import.go index 608cbabee2f5..7222def22a7a 100644 --- a/internal/terraform/node_resource_import.go +++ b/internal/terraform/node_resource_import.go @@ -7,6 +7,7 @@ import ( "fmt" "log" + "github.com/hashicorp/hcl/v2" "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/providers" "github.com/hashicorp/terraform/internal/states" @@ -95,6 +96,19 @@ func (n *graphNodeImportState) Execute(ctx EvalContext, op walkOperation) (diags ID: n.ID, }) diags = diags.Append(resp.Diagnostics) + if len(resp.Deferred) != 0 { + // We don't yet support deferrals really, so for now we'll just treat + // them as errors. This is only a temporary shortcut and should not + // ship in any stable Terraform release. + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Not enough information to import", + Detail: fmt.Sprintf( + "The provider configuration for %s contains unknown values that prevent planning.", + n.Addr.String(), + ), + }) + } if diags.HasErrors() { return diags } diff --git a/internal/terraform/upgrade_resource_state.go b/internal/terraform/upgrade_resource_state.go index 659f991481e1..c81ed82130c4 100644 --- a/internal/terraform/upgrade_resource_state.go +++ b/internal/terraform/upgrade_resource_state.go @@ -8,6 +8,7 @@ import ( "fmt" "log" + "github.com/hashicorp/hcl/v2" "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/providers" @@ -92,6 +93,19 @@ func upgradeResourceState(addr addrs.AbsResourceInstance, provider providers.Int resp := provider.UpgradeResourceState(req) diags := resp.Diagnostics + if len(resp.Deferred) != 0 { + // We don't yet support deferrals really, so for now we'll just treat + // them as errors. This is only a temporary shortcut and should not + // ship in any stable Terraform release. + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Not enough information to upgrade", + Detail: fmt.Sprintf( + "The configuration of either %s or its provider contains unknown values that prevent planning.", + addr.String(), + ), + }) + } if diags.HasErrors() { return nil, diags } diff --git a/internal/tfplugin6/tfplugin6.pb.go b/internal/tfplugin6/tfplugin6.pb.go index 894a712a9eff..d30469896c27 100644 --- a/internal/tfplugin6/tfplugin6.pb.go +++ b/internal/tfplugin6/tfplugin6.pb.go @@ -1,9 +1,6 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -// Terraform Plugin RPC protocol version 6.4 +// Terraform Plugin RPC protocol version 6.5 // -// This file defines version 6.4 of the RPC protocol. To implement a plugin +// This file defines version 6.5 of the RPC protocol. To implement a plugin // against this protocol, copy this definition into your own codebase and // use protoc to generate stubs for your target language. // @@ -650,6 +647,116 @@ func (x *ServerCapabilities) GetGetProviderSchemaOptional() bool { return false } +// DeferredAction represents a reason why a particular action cannot be taken +// until Terraform can provide concrete values instead of unknown value +// placeholders. +type DeferredAction struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Reason: + // + // *DeferredAction_OtherReason_ + // *DeferredAction_ProviderConfigUnknown + // *DeferredAction_ResourceConfigUnknown + Reason isDeferredAction_Reason `protobuf_oneof:"reason"` +} + +func (x *DeferredAction) Reset() { + *x = DeferredAction{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeferredAction) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeferredAction) ProtoMessage() {} + +func (x *DeferredAction) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeferredAction.ProtoReflect.Descriptor instead. +func (*DeferredAction) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{7} +} + +func (m *DeferredAction) GetReason() isDeferredAction_Reason { + if m != nil { + return m.Reason + } + return nil +} + +func (x *DeferredAction) GetOtherReason() *DeferredAction_OtherReason { + if x, ok := x.GetReason().(*DeferredAction_OtherReason_); ok { + return x.OtherReason + } + return nil +} + +func (x *DeferredAction) GetProviderConfigUnknown() *DeferredAction_ConfigUnknown { + if x, ok := x.GetReason().(*DeferredAction_ProviderConfigUnknown); ok { + return x.ProviderConfigUnknown + } + return nil +} + +func (x *DeferredAction) GetResourceConfigUnknown() *DeferredAction_ConfigUnknown { + if x, ok := x.GetReason().(*DeferredAction_ResourceConfigUnknown); ok { + return x.ResourceConfigUnknown + } + return nil +} + +type isDeferredAction_Reason interface { + isDeferredAction_Reason() +} + +type DeferredAction_OtherReason_ struct { + // other_reason is a fallback reason to use if none of the other + // specific reasons seem appropriate. Terraform will show a generic + // message in the UI in this case, acknowledging that the operation + // was deferred but with no specific reason why. Terraform Core will + // also treat any unrecognized reason type defined in later versions + // of this protocol as if they were other_reason. + OtherReason *DeferredAction_OtherReason `protobuf:"bytes,1,opt,name=other_reason,json=otherReason,proto3,oneof"` +} + +type DeferredAction_ProviderConfigUnknown struct { + // provider_config_unknown represents that the action was deferred + // because a necessary part of the corresponding provider configuration + // is not yet known enough to proceed. + ProviderConfigUnknown *DeferredAction_ConfigUnknown `protobuf:"bytes,2,opt,name=provider_config_unknown,json=providerConfigUnknown,proto3,oneof"` +} + +type DeferredAction_ResourceConfigUnknown struct { + // resource_config_unknown represents that the action was deferred + // because a necessary part of the resource configuration + // is not yet known enough to proceed. + ResourceConfigUnknown *DeferredAction_ConfigUnknown `protobuf:"bytes,3,opt,name=resource_config_unknown,json=resourceConfigUnknown,proto3,oneof"` +} + +func (*DeferredAction_OtherReason_) isDeferredAction_Reason() {} + +func (*DeferredAction_ProviderConfigUnknown) isDeferredAction_Reason() {} + +func (*DeferredAction_ResourceConfigUnknown) isDeferredAction_Reason() {} + type GetMetadata struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -659,7 +766,7 @@ type GetMetadata struct { func (x *GetMetadata) Reset() { *x = GetMetadata{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[7] + mi := &file_tfplugin6_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -672,7 +779,7 @@ func (x *GetMetadata) String() string { func (*GetMetadata) ProtoMessage() {} func (x *GetMetadata) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[7] + mi := &file_tfplugin6_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -685,7 +792,7 @@ func (x *GetMetadata) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMetadata.ProtoReflect.Descriptor instead. func (*GetMetadata) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{7} + return file_tfplugin6_proto_rawDescGZIP(), []int{8} } type GetProviderSchema struct { @@ -697,7 +804,7 @@ type GetProviderSchema struct { func (x *GetProviderSchema) Reset() { *x = GetProviderSchema{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[8] + mi := &file_tfplugin6_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -710,7 +817,7 @@ func (x *GetProviderSchema) String() string { func (*GetProviderSchema) ProtoMessage() {} func (x *GetProviderSchema) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[8] + mi := &file_tfplugin6_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -723,7 +830,7 @@ func (x *GetProviderSchema) ProtoReflect() protoreflect.Message { // Deprecated: Use GetProviderSchema.ProtoReflect.Descriptor instead. func (*GetProviderSchema) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{8} + return file_tfplugin6_proto_rawDescGZIP(), []int{9} } type ValidateProviderConfig struct { @@ -735,7 +842,7 @@ type ValidateProviderConfig struct { func (x *ValidateProviderConfig) Reset() { *x = ValidateProviderConfig{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[9] + mi := &file_tfplugin6_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -748,7 +855,7 @@ func (x *ValidateProviderConfig) String() string { func (*ValidateProviderConfig) ProtoMessage() {} func (x *ValidateProviderConfig) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[9] + mi := &file_tfplugin6_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -761,7 +868,7 @@ func (x *ValidateProviderConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use ValidateProviderConfig.ProtoReflect.Descriptor instead. func (*ValidateProviderConfig) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{9} + return file_tfplugin6_proto_rawDescGZIP(), []int{10} } type UpgradeResourceState struct { @@ -773,7 +880,7 @@ type UpgradeResourceState struct { func (x *UpgradeResourceState) Reset() { *x = UpgradeResourceState{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[10] + mi := &file_tfplugin6_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -786,7 +893,7 @@ func (x *UpgradeResourceState) String() string { func (*UpgradeResourceState) ProtoMessage() {} func (x *UpgradeResourceState) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[10] + mi := &file_tfplugin6_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -799,7 +906,7 @@ func (x *UpgradeResourceState) ProtoReflect() protoreflect.Message { // Deprecated: Use UpgradeResourceState.ProtoReflect.Descriptor instead. func (*UpgradeResourceState) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{10} + return file_tfplugin6_proto_rawDescGZIP(), []int{11} } type ValidateResourceConfig struct { @@ -811,7 +918,7 @@ type ValidateResourceConfig struct { func (x *ValidateResourceConfig) Reset() { *x = ValidateResourceConfig{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[11] + mi := &file_tfplugin6_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -824,7 +931,7 @@ func (x *ValidateResourceConfig) String() string { func (*ValidateResourceConfig) ProtoMessage() {} func (x *ValidateResourceConfig) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[11] + mi := &file_tfplugin6_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -837,7 +944,7 @@ func (x *ValidateResourceConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use ValidateResourceConfig.ProtoReflect.Descriptor instead. func (*ValidateResourceConfig) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{11} + return file_tfplugin6_proto_rawDescGZIP(), []int{12} } type ValidateDataResourceConfig struct { @@ -849,7 +956,7 @@ type ValidateDataResourceConfig struct { func (x *ValidateDataResourceConfig) Reset() { *x = ValidateDataResourceConfig{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[12] + mi := &file_tfplugin6_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -862,7 +969,7 @@ func (x *ValidateDataResourceConfig) String() string { func (*ValidateDataResourceConfig) ProtoMessage() {} func (x *ValidateDataResourceConfig) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[12] + mi := &file_tfplugin6_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -875,7 +982,7 @@ func (x *ValidateDataResourceConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use ValidateDataResourceConfig.ProtoReflect.Descriptor instead. func (*ValidateDataResourceConfig) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{12} + return file_tfplugin6_proto_rawDescGZIP(), []int{13} } type ConfigureProvider struct { @@ -887,7 +994,7 @@ type ConfigureProvider struct { func (x *ConfigureProvider) Reset() { *x = ConfigureProvider{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[13] + mi := &file_tfplugin6_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -900,7 +1007,7 @@ func (x *ConfigureProvider) String() string { func (*ConfigureProvider) ProtoMessage() {} func (x *ConfigureProvider) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[13] + mi := &file_tfplugin6_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -913,7 +1020,7 @@ func (x *ConfigureProvider) ProtoReflect() protoreflect.Message { // Deprecated: Use ConfigureProvider.ProtoReflect.Descriptor instead. func (*ConfigureProvider) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{13} + return file_tfplugin6_proto_rawDescGZIP(), []int{14} } type ReadResource struct { @@ -925,7 +1032,7 @@ type ReadResource struct { func (x *ReadResource) Reset() { *x = ReadResource{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[14] + mi := &file_tfplugin6_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -938,7 +1045,7 @@ func (x *ReadResource) String() string { func (*ReadResource) ProtoMessage() {} func (x *ReadResource) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[14] + mi := &file_tfplugin6_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -951,7 +1058,7 @@ func (x *ReadResource) ProtoReflect() protoreflect.Message { // Deprecated: Use ReadResource.ProtoReflect.Descriptor instead. func (*ReadResource) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{14} + return file_tfplugin6_proto_rawDescGZIP(), []int{15} } type PlanResourceChange struct { @@ -963,7 +1070,7 @@ type PlanResourceChange struct { func (x *PlanResourceChange) Reset() { *x = PlanResourceChange{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[15] + mi := &file_tfplugin6_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -976,7 +1083,7 @@ func (x *PlanResourceChange) String() string { func (*PlanResourceChange) ProtoMessage() {} func (x *PlanResourceChange) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[15] + mi := &file_tfplugin6_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -989,7 +1096,7 @@ func (x *PlanResourceChange) ProtoReflect() protoreflect.Message { // Deprecated: Use PlanResourceChange.ProtoReflect.Descriptor instead. func (*PlanResourceChange) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{15} + return file_tfplugin6_proto_rawDescGZIP(), []int{16} } type ApplyResourceChange struct { @@ -1001,7 +1108,7 @@ type ApplyResourceChange struct { func (x *ApplyResourceChange) Reset() { *x = ApplyResourceChange{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[16] + mi := &file_tfplugin6_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1014,7 +1121,7 @@ func (x *ApplyResourceChange) String() string { func (*ApplyResourceChange) ProtoMessage() {} func (x *ApplyResourceChange) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[16] + mi := &file_tfplugin6_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1027,7 +1134,7 @@ func (x *ApplyResourceChange) ProtoReflect() protoreflect.Message { // Deprecated: Use ApplyResourceChange.ProtoReflect.Descriptor instead. func (*ApplyResourceChange) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{16} + return file_tfplugin6_proto_rawDescGZIP(), []int{17} } type ImportResourceState struct { @@ -1039,7 +1146,7 @@ type ImportResourceState struct { func (x *ImportResourceState) Reset() { *x = ImportResourceState{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[17] + mi := &file_tfplugin6_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1052,7 +1159,7 @@ func (x *ImportResourceState) String() string { func (*ImportResourceState) ProtoMessage() {} func (x *ImportResourceState) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[17] + mi := &file_tfplugin6_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1065,7 +1172,7 @@ func (x *ImportResourceState) ProtoReflect() protoreflect.Message { // Deprecated: Use ImportResourceState.ProtoReflect.Descriptor instead. func (*ImportResourceState) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{17} + return file_tfplugin6_proto_rawDescGZIP(), []int{18} } type ReadDataSource struct { @@ -1077,7 +1184,7 @@ type ReadDataSource struct { func (x *ReadDataSource) Reset() { *x = ReadDataSource{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[18] + mi := &file_tfplugin6_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1090,7 +1197,7 @@ func (x *ReadDataSource) String() string { func (*ReadDataSource) ProtoMessage() {} func (x *ReadDataSource) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[18] + mi := &file_tfplugin6_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1103,7 +1210,7 @@ func (x *ReadDataSource) ProtoReflect() protoreflect.Message { // Deprecated: Use ReadDataSource.ProtoReflect.Descriptor instead. func (*ReadDataSource) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{18} + return file_tfplugin6_proto_rawDescGZIP(), []int{19} } type AttributePath_Step struct { @@ -1122,7 +1229,7 @@ type AttributePath_Step struct { func (x *AttributePath_Step) Reset() { *x = AttributePath_Step{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[19] + mi := &file_tfplugin6_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1135,7 +1242,7 @@ func (x *AttributePath_Step) String() string { func (*AttributePath_Step) ProtoMessage() {} func (x *AttributePath_Step) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[19] + mi := &file_tfplugin6_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1214,7 +1321,7 @@ type StopProvider_Request struct { func (x *StopProvider_Request) Reset() { *x = StopProvider_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[20] + mi := &file_tfplugin6_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1227,7 +1334,7 @@ func (x *StopProvider_Request) String() string { func (*StopProvider_Request) ProtoMessage() {} func (x *StopProvider_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[20] + mi := &file_tfplugin6_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1254,7 +1361,7 @@ type StopProvider_Response struct { func (x *StopProvider_Response) Reset() { *x = StopProvider_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[21] + mi := &file_tfplugin6_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1267,7 +1374,7 @@ func (x *StopProvider_Response) String() string { func (*StopProvider_Response) ProtoMessage() {} func (x *StopProvider_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[21] + mi := &file_tfplugin6_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1306,7 +1413,7 @@ type Schema_Block struct { func (x *Schema_Block) Reset() { *x = Schema_Block{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[23] + mi := &file_tfplugin6_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1319,7 +1426,7 @@ func (x *Schema_Block) String() string { func (*Schema_Block) ProtoMessage() {} func (x *Schema_Block) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[23] + mi := &file_tfplugin6_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1397,7 +1504,7 @@ type Schema_Attribute struct { func (x *Schema_Attribute) Reset() { *x = Schema_Attribute{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[24] + mi := &file_tfplugin6_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1410,7 +1517,7 @@ func (x *Schema_Attribute) String() string { func (*Schema_Attribute) ProtoMessage() {} func (x *Schema_Attribute) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[24] + mi := &file_tfplugin6_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1511,7 +1618,7 @@ type Schema_NestedBlock struct { func (x *Schema_NestedBlock) Reset() { *x = Schema_NestedBlock{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[25] + mi := &file_tfplugin6_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1524,7 +1631,7 @@ func (x *Schema_NestedBlock) String() string { func (*Schema_NestedBlock) ProtoMessage() {} func (x *Schema_NestedBlock) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[25] + mi := &file_tfplugin6_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1594,7 +1701,7 @@ type Schema_Object struct { func (x *Schema_Object) Reset() { *x = Schema_Object{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[26] + mi := &file_tfplugin6_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1607,7 +1714,7 @@ func (x *Schema_Object) String() string { func (*Schema_Object) ProtoMessage() {} func (x *Schema_Object) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[26] + mi := &file_tfplugin6_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1653,6 +1760,95 @@ func (x *Schema_Object) GetMaxItems() int64 { return 0 } +type DeferredAction_OtherReason struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *DeferredAction_OtherReason) Reset() { + *x = DeferredAction_OtherReason{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeferredAction_OtherReason) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeferredAction_OtherReason) ProtoMessage() {} + +func (x *DeferredAction_OtherReason) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[28] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeferredAction_OtherReason.ProtoReflect.Descriptor instead. +func (*DeferredAction_OtherReason) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{7, 0} +} + +type DeferredAction_ConfigUnknown struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The path to the attribute whose unknown value caused the deferral. + // Provide as precise a path as possible, but it's okay to describe + // a shorter path if e.g. the problem is inside a set element and + // therefore not fully addressable as an AttributePath. + Attribute *AttributePath `protobuf:"bytes,1,opt,name=attribute,proto3" json:"attribute,omitempty"` +} + +func (x *DeferredAction_ConfigUnknown) Reset() { + *x = DeferredAction_ConfigUnknown{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeferredAction_ConfigUnknown) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeferredAction_ConfigUnknown) ProtoMessage() {} + +func (x *DeferredAction_ConfigUnknown) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[29] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeferredAction_ConfigUnknown.ProtoReflect.Descriptor instead. +func (*DeferredAction_ConfigUnknown) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{7, 1} +} + +func (x *DeferredAction_ConfigUnknown) GetAttribute() *AttributePath { + if x != nil { + return x.Attribute + } + return nil +} + type GetMetadata_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1662,7 +1858,7 @@ type GetMetadata_Request struct { func (x *GetMetadata_Request) Reset() { *x = GetMetadata_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[27] + mi := &file_tfplugin6_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1675,7 +1871,7 @@ func (x *GetMetadata_Request) String() string { func (*GetMetadata_Request) ProtoMessage() {} func (x *GetMetadata_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[27] + mi := &file_tfplugin6_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1688,7 +1884,7 @@ func (x *GetMetadata_Request) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMetadata_Request.ProtoReflect.Descriptor instead. func (*GetMetadata_Request) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{7, 0} + return file_tfplugin6_proto_rawDescGZIP(), []int{8, 0} } type GetMetadata_Response struct { @@ -1705,7 +1901,7 @@ type GetMetadata_Response struct { func (x *GetMetadata_Response) Reset() { *x = GetMetadata_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[28] + mi := &file_tfplugin6_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1718,7 +1914,7 @@ func (x *GetMetadata_Response) String() string { func (*GetMetadata_Response) ProtoMessage() {} func (x *GetMetadata_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[28] + mi := &file_tfplugin6_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1731,7 +1927,7 @@ func (x *GetMetadata_Response) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMetadata_Response.ProtoReflect.Descriptor instead. func (*GetMetadata_Response) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{7, 1} + return file_tfplugin6_proto_rawDescGZIP(), []int{8, 1} } func (x *GetMetadata_Response) GetServerCapabilities() *ServerCapabilities { @@ -1773,7 +1969,7 @@ type GetMetadata_DataSourceMetadata struct { func (x *GetMetadata_DataSourceMetadata) Reset() { *x = GetMetadata_DataSourceMetadata{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[29] + mi := &file_tfplugin6_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1786,7 +1982,7 @@ func (x *GetMetadata_DataSourceMetadata) String() string { func (*GetMetadata_DataSourceMetadata) ProtoMessage() {} func (x *GetMetadata_DataSourceMetadata) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[29] + mi := &file_tfplugin6_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1799,7 +1995,7 @@ func (x *GetMetadata_DataSourceMetadata) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMetadata_DataSourceMetadata.ProtoReflect.Descriptor instead. func (*GetMetadata_DataSourceMetadata) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{7, 2} + return file_tfplugin6_proto_rawDescGZIP(), []int{8, 2} } func (x *GetMetadata_DataSourceMetadata) GetTypeName() string { @@ -1820,7 +2016,7 @@ type GetMetadata_ResourceMetadata struct { func (x *GetMetadata_ResourceMetadata) Reset() { *x = GetMetadata_ResourceMetadata{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[30] + mi := &file_tfplugin6_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1833,7 +2029,7 @@ func (x *GetMetadata_ResourceMetadata) String() string { func (*GetMetadata_ResourceMetadata) ProtoMessage() {} func (x *GetMetadata_ResourceMetadata) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[30] + mi := &file_tfplugin6_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1846,7 +2042,7 @@ func (x *GetMetadata_ResourceMetadata) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMetadata_ResourceMetadata.ProtoReflect.Descriptor instead. func (*GetMetadata_ResourceMetadata) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{7, 3} + return file_tfplugin6_proto_rawDescGZIP(), []int{8, 3} } func (x *GetMetadata_ResourceMetadata) GetTypeName() string { @@ -1865,7 +2061,7 @@ type GetProviderSchema_Request struct { func (x *GetProviderSchema_Request) Reset() { *x = GetProviderSchema_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[31] + mi := &file_tfplugin6_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1878,7 +2074,7 @@ func (x *GetProviderSchema_Request) String() string { func (*GetProviderSchema_Request) ProtoMessage() {} func (x *GetProviderSchema_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[31] + mi := &file_tfplugin6_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1891,7 +2087,7 @@ func (x *GetProviderSchema_Request) ProtoReflect() protoreflect.Message { // Deprecated: Use GetProviderSchema_Request.ProtoReflect.Descriptor instead. func (*GetProviderSchema_Request) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{8, 0} + return file_tfplugin6_proto_rawDescGZIP(), []int{9, 0} } type GetProviderSchema_Response struct { @@ -1910,7 +2106,7 @@ type GetProviderSchema_Response struct { func (x *GetProviderSchema_Response) Reset() { *x = GetProviderSchema_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[32] + mi := &file_tfplugin6_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1923,7 +2119,7 @@ func (x *GetProviderSchema_Response) String() string { func (*GetProviderSchema_Response) ProtoMessage() {} func (x *GetProviderSchema_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[32] + mi := &file_tfplugin6_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1936,7 +2132,7 @@ func (x *GetProviderSchema_Response) ProtoReflect() protoreflect.Message { // Deprecated: Use GetProviderSchema_Response.ProtoReflect.Descriptor instead. func (*GetProviderSchema_Response) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{8, 1} + return file_tfplugin6_proto_rawDescGZIP(), []int{9, 1} } func (x *GetProviderSchema_Response) GetProvider() *Schema { @@ -1992,7 +2188,7 @@ type ValidateProviderConfig_Request struct { func (x *ValidateProviderConfig_Request) Reset() { *x = ValidateProviderConfig_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[35] + mi := &file_tfplugin6_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2005,7 +2201,7 @@ func (x *ValidateProviderConfig_Request) String() string { func (*ValidateProviderConfig_Request) ProtoMessage() {} func (x *ValidateProviderConfig_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[35] + mi := &file_tfplugin6_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2018,7 +2214,7 @@ func (x *ValidateProviderConfig_Request) ProtoReflect() protoreflect.Message { // Deprecated: Use ValidateProviderConfig_Request.ProtoReflect.Descriptor instead. func (*ValidateProviderConfig_Request) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{9, 0} + return file_tfplugin6_proto_rawDescGZIP(), []int{10, 0} } func (x *ValidateProviderConfig_Request) GetConfig() *DynamicValue { @@ -2039,7 +2235,7 @@ type ValidateProviderConfig_Response struct { func (x *ValidateProviderConfig_Response) Reset() { *x = ValidateProviderConfig_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[36] + mi := &file_tfplugin6_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2052,7 +2248,7 @@ func (x *ValidateProviderConfig_Response) String() string { func (*ValidateProviderConfig_Response) ProtoMessage() {} func (x *ValidateProviderConfig_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[36] + mi := &file_tfplugin6_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2065,7 +2261,7 @@ func (x *ValidateProviderConfig_Response) ProtoReflect() protoreflect.Message { // Deprecated: Use ValidateProviderConfig_Response.ProtoReflect.Descriptor instead. func (*ValidateProviderConfig_Response) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{9, 1} + return file_tfplugin6_proto_rawDescGZIP(), []int{10, 1} } func (x *ValidateProviderConfig_Response) GetDiagnostics() []*Diagnostic { @@ -2098,12 +2294,18 @@ type UpgradeResourceState_Request struct { // appropriate older schema. The raw_state will be the json encoded // state, or a legacy flat-mapped format. RawState *RawState `protobuf:"bytes,3,opt,name=raw_state,json=rawState,proto3" json:"raw_state,omitempty"` + // Terraform Core will set this if it will honor elements of + // "deferred" in the response. If this field is not set then + // a provider must return error diagnostics whenever it has + // insufficient information to upgrade the state date to the + // latest schema version. + DeferAllowed bool `protobuf:"varint,4,opt,name=defer_allowed,json=deferAllowed,proto3" json:"defer_allowed,omitempty"` } func (x *UpgradeResourceState_Request) Reset() { *x = UpgradeResourceState_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[37] + mi := &file_tfplugin6_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2116,7 +2318,7 @@ func (x *UpgradeResourceState_Request) String() string { func (*UpgradeResourceState_Request) ProtoMessage() {} func (x *UpgradeResourceState_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[37] + mi := &file_tfplugin6_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2129,7 +2331,7 @@ func (x *UpgradeResourceState_Request) ProtoReflect() protoreflect.Message { // Deprecated: Use UpgradeResourceState_Request.ProtoReflect.Descriptor instead. func (*UpgradeResourceState_Request) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{10, 0} + return file_tfplugin6_proto_rawDescGZIP(), []int{11, 0} } func (x *UpgradeResourceState_Request) GetTypeName() string { @@ -2153,6 +2355,13 @@ func (x *UpgradeResourceState_Request) GetRawState() *RawState { return nil } +func (x *UpgradeResourceState_Request) GetDeferAllowed() bool { + if x != nil { + return x.DeferAllowed + } + return false +} + type UpgradeResourceState_Response struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2166,12 +2375,22 @@ type UpgradeResourceState_Response struct { // be safely resolved, and warnings about any possibly-risky assumptions made // in the upgrade process. Diagnostics []*Diagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` + // If the request set defer_allowed to true then the provider may + // annotate a response with one or more deferral reasons, in which + // case Terraform Core will treat the object as completely unknown + // while planning downstream actions. Providers should avoid deferring + // an upgrade if at all possible, by implementing upgrade logic entirely + // within the provider and not accessing any external system. + // Don't set this if the request did not have defer_allowed set; + // instead return error diagnostics explaining why upgrading is + // impossible. + Deferred []*DeferredAction `protobuf:"bytes,3,rep,name=deferred,proto3" json:"deferred,omitempty"` } func (x *UpgradeResourceState_Response) Reset() { *x = UpgradeResourceState_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[38] + mi := &file_tfplugin6_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2184,7 +2403,7 @@ func (x *UpgradeResourceState_Response) String() string { func (*UpgradeResourceState_Response) ProtoMessage() {} func (x *UpgradeResourceState_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[38] + mi := &file_tfplugin6_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2197,7 +2416,7 @@ func (x *UpgradeResourceState_Response) ProtoReflect() protoreflect.Message { // Deprecated: Use UpgradeResourceState_Response.ProtoReflect.Descriptor instead. func (*UpgradeResourceState_Response) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{10, 1} + return file_tfplugin6_proto_rawDescGZIP(), []int{11, 1} } func (x *UpgradeResourceState_Response) GetUpgradedState() *DynamicValue { @@ -2214,6 +2433,13 @@ func (x *UpgradeResourceState_Response) GetDiagnostics() []*Diagnostic { return nil } +func (x *UpgradeResourceState_Response) GetDeferred() []*DeferredAction { + if x != nil { + return x.Deferred + } + return nil +} + type ValidateResourceConfig_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2226,7 +2452,7 @@ type ValidateResourceConfig_Request struct { func (x *ValidateResourceConfig_Request) Reset() { *x = ValidateResourceConfig_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[39] + mi := &file_tfplugin6_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2239,7 +2465,7 @@ func (x *ValidateResourceConfig_Request) String() string { func (*ValidateResourceConfig_Request) ProtoMessage() {} func (x *ValidateResourceConfig_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[39] + mi := &file_tfplugin6_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2252,7 +2478,7 @@ func (x *ValidateResourceConfig_Request) ProtoReflect() protoreflect.Message { // Deprecated: Use ValidateResourceConfig_Request.ProtoReflect.Descriptor instead. func (*ValidateResourceConfig_Request) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{11, 0} + return file_tfplugin6_proto_rawDescGZIP(), []int{12, 0} } func (x *ValidateResourceConfig_Request) GetTypeName() string { @@ -2280,7 +2506,7 @@ type ValidateResourceConfig_Response struct { func (x *ValidateResourceConfig_Response) Reset() { *x = ValidateResourceConfig_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[40] + mi := &file_tfplugin6_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2293,7 +2519,7 @@ func (x *ValidateResourceConfig_Response) String() string { func (*ValidateResourceConfig_Response) ProtoMessage() {} func (x *ValidateResourceConfig_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[40] + mi := &file_tfplugin6_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2306,7 +2532,7 @@ func (x *ValidateResourceConfig_Response) ProtoReflect() protoreflect.Message { // Deprecated: Use ValidateResourceConfig_Response.ProtoReflect.Descriptor instead. func (*ValidateResourceConfig_Response) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{11, 1} + return file_tfplugin6_proto_rawDescGZIP(), []int{12, 1} } func (x *ValidateResourceConfig_Response) GetDiagnostics() []*Diagnostic { @@ -2328,7 +2554,7 @@ type ValidateDataResourceConfig_Request struct { func (x *ValidateDataResourceConfig_Request) Reset() { *x = ValidateDataResourceConfig_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[41] + mi := &file_tfplugin6_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2341,7 +2567,7 @@ func (x *ValidateDataResourceConfig_Request) String() string { func (*ValidateDataResourceConfig_Request) ProtoMessage() {} func (x *ValidateDataResourceConfig_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[41] + mi := &file_tfplugin6_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2354,7 +2580,7 @@ func (x *ValidateDataResourceConfig_Request) ProtoReflect() protoreflect.Message // Deprecated: Use ValidateDataResourceConfig_Request.ProtoReflect.Descriptor instead. func (*ValidateDataResourceConfig_Request) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{12, 0} + return file_tfplugin6_proto_rawDescGZIP(), []int{13, 0} } func (x *ValidateDataResourceConfig_Request) GetTypeName() string { @@ -2382,7 +2608,7 @@ type ValidateDataResourceConfig_Response struct { func (x *ValidateDataResourceConfig_Response) Reset() { *x = ValidateDataResourceConfig_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[42] + mi := &file_tfplugin6_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2395,7 +2621,7 @@ func (x *ValidateDataResourceConfig_Response) String() string { func (*ValidateDataResourceConfig_Response) ProtoMessage() {} func (x *ValidateDataResourceConfig_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[42] + mi := &file_tfplugin6_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2408,7 +2634,7 @@ func (x *ValidateDataResourceConfig_Response) ProtoReflect() protoreflect.Messag // Deprecated: Use ValidateDataResourceConfig_Response.ProtoReflect.Descriptor instead. func (*ValidateDataResourceConfig_Response) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{12, 1} + return file_tfplugin6_proto_rawDescGZIP(), []int{13, 1} } func (x *ValidateDataResourceConfig_Response) GetDiagnostics() []*Diagnostic { @@ -2430,7 +2656,7 @@ type ConfigureProvider_Request struct { func (x *ConfigureProvider_Request) Reset() { *x = ConfigureProvider_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[43] + mi := &file_tfplugin6_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2443,7 +2669,7 @@ func (x *ConfigureProvider_Request) String() string { func (*ConfigureProvider_Request) ProtoMessage() {} func (x *ConfigureProvider_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[43] + mi := &file_tfplugin6_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2456,7 +2682,7 @@ func (x *ConfigureProvider_Request) ProtoReflect() protoreflect.Message { // Deprecated: Use ConfigureProvider_Request.ProtoReflect.Descriptor instead. func (*ConfigureProvider_Request) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{13, 0} + return file_tfplugin6_proto_rawDescGZIP(), []int{14, 0} } func (x *ConfigureProvider_Request) GetTerraformVersion() string { @@ -2484,7 +2710,7 @@ type ConfigureProvider_Response struct { func (x *ConfigureProvider_Response) Reset() { *x = ConfigureProvider_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[44] + mi := &file_tfplugin6_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2497,7 +2723,7 @@ func (x *ConfigureProvider_Response) String() string { func (*ConfigureProvider_Response) ProtoMessage() {} func (x *ConfigureProvider_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[44] + mi := &file_tfplugin6_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2510,7 +2736,7 @@ func (x *ConfigureProvider_Response) ProtoReflect() protoreflect.Message { // Deprecated: Use ConfigureProvider_Response.ProtoReflect.Descriptor instead. func (*ConfigureProvider_Response) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{13, 1} + return file_tfplugin6_proto_rawDescGZIP(), []int{14, 1} } func (x *ConfigureProvider_Response) GetDiagnostics() []*Diagnostic { @@ -2537,12 +2763,18 @@ type ReadResource_Request struct { CurrentState *DynamicValue `protobuf:"bytes,2,opt,name=current_state,json=currentState,proto3" json:"current_state,omitempty"` Private []byte `protobuf:"bytes,3,opt,name=private,proto3" json:"private,omitempty"` ProviderMeta *DynamicValue `protobuf:"bytes,4,opt,name=provider_meta,json=providerMeta,proto3" json:"provider_meta,omitempty"` + // Terraform Core will set this if it will honor elements of + // "deferred" in the response. If this field is not set then + // a provider must return error diagnostics whenever it has + // insufficient information to retrieve the current state + // of the object. + DeferAllowed bool `protobuf:"varint,5,opt,name=defer_allowed,json=deferAllowed,proto3" json:"defer_allowed,omitempty"` } func (x *ReadResource_Request) Reset() { *x = ReadResource_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[45] + mi := &file_tfplugin6_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2555,7 +2787,7 @@ func (x *ReadResource_Request) String() string { func (*ReadResource_Request) ProtoMessage() {} func (x *ReadResource_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[45] + mi := &file_tfplugin6_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2568,7 +2800,7 @@ func (x *ReadResource_Request) ProtoReflect() protoreflect.Message { // Deprecated: Use ReadResource_Request.ProtoReflect.Descriptor instead. func (*ReadResource_Request) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{14, 0} + return file_tfplugin6_proto_rawDescGZIP(), []int{15, 0} } func (x *ReadResource_Request) GetTypeName() string { @@ -2599,6 +2831,13 @@ func (x *ReadResource_Request) GetProviderMeta() *DynamicValue { return nil } +func (x *ReadResource_Request) GetDeferAllowed() bool { + if x != nil { + return x.DeferAllowed + } + return false +} + type ReadResource_Response struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2607,12 +2846,22 @@ type ReadResource_Response struct { NewState *DynamicValue `protobuf:"bytes,1,opt,name=new_state,json=newState,proto3" json:"new_state,omitempty"` Diagnostics []*Diagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` Private []byte `protobuf:"bytes,3,opt,name=private,proto3" json:"private,omitempty"` + // If the request set defer_allowed to true then the provider may + // annotate a response with one or more deferral reasons, in which + // case Terraform Core will still use the partial "new_state" value + // to plan downstream actions but will then defer them until a + // future run where a similar ReadResource request can return + // a complete result. + // Don't set this if the request did not have defer_allowed set; + // instead return error diagnostics explaining why reading is + // impossible. + Deferred []*DeferredAction `protobuf:"bytes,4,rep,name=deferred,proto3" json:"deferred,omitempty"` } func (x *ReadResource_Response) Reset() { *x = ReadResource_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[46] + mi := &file_tfplugin6_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2625,7 +2874,7 @@ func (x *ReadResource_Response) String() string { func (*ReadResource_Response) ProtoMessage() {} func (x *ReadResource_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[46] + mi := &file_tfplugin6_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2638,7 +2887,7 @@ func (x *ReadResource_Response) ProtoReflect() protoreflect.Message { // Deprecated: Use ReadResource_Response.ProtoReflect.Descriptor instead. func (*ReadResource_Response) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{14, 1} + return file_tfplugin6_proto_rawDescGZIP(), []int{15, 1} } func (x *ReadResource_Response) GetNewState() *DynamicValue { @@ -2662,6 +2911,13 @@ func (x *ReadResource_Response) GetPrivate() []byte { return nil } +func (x *ReadResource_Response) GetDeferred() []*DeferredAction { + if x != nil { + return x.Deferred + } + return nil +} + type PlanResourceChange_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2673,12 +2929,17 @@ type PlanResourceChange_Request struct { Config *DynamicValue `protobuf:"bytes,4,opt,name=config,proto3" json:"config,omitempty"` PriorPrivate []byte `protobuf:"bytes,5,opt,name=prior_private,json=priorPrivate,proto3" json:"prior_private,omitempty"` ProviderMeta *DynamicValue `protobuf:"bytes,6,opt,name=provider_meta,json=providerMeta,proto3" json:"provider_meta,omitempty"` + // Terraform Core will set this if it will honor elements of + // "deferred" in the response. If this field is not set then + // a provider must return error diagnostics whenever it has + // insufficient information to fully plan the change. + DeferAllowed bool `protobuf:"varint,7,opt,name=defer_allowed,json=deferAllowed,proto3" json:"defer_allowed,omitempty"` } func (x *PlanResourceChange_Request) Reset() { *x = PlanResourceChange_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[47] + mi := &file_tfplugin6_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2691,7 +2952,7 @@ func (x *PlanResourceChange_Request) String() string { func (*PlanResourceChange_Request) ProtoMessage() {} func (x *PlanResourceChange_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[47] + mi := &file_tfplugin6_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2704,7 +2965,7 @@ func (x *PlanResourceChange_Request) ProtoReflect() protoreflect.Message { // Deprecated: Use PlanResourceChange_Request.ProtoReflect.Descriptor instead. func (*PlanResourceChange_Request) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{15, 0} + return file_tfplugin6_proto_rawDescGZIP(), []int{16, 0} } func (x *PlanResourceChange_Request) GetTypeName() string { @@ -2749,6 +3010,13 @@ func (x *PlanResourceChange_Request) GetProviderMeta() *DynamicValue { return nil } +func (x *PlanResourceChange_Request) GetDeferAllowed() bool { + if x != nil { + return x.DeferAllowed + } + return false +} + type PlanResourceChange_Response struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2758,6 +3026,15 @@ type PlanResourceChange_Response struct { RequiresReplace []*AttributePath `protobuf:"bytes,2,rep,name=requires_replace,json=requiresReplace,proto3" json:"requires_replace,omitempty"` PlannedPrivate []byte `protobuf:"bytes,3,opt,name=planned_private,json=plannedPrivate,proto3" json:"planned_private,omitempty"` Diagnostics []*Diagnostic `protobuf:"bytes,4,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` + // If the request set defer_allowed to true then the provider may + // annotate a response with one or more deferral reasons, in which + // case Terraform Core will still present the planned action to the + // user for review as a deferred action but will not actually apply + // the change without first asking for a new plan with more information. + // Don't set this if the request did not have defer_allowed set; + // instead return error diagnostics explaining why planning is + // impossible. + Deferred []*DeferredAction `protobuf:"bytes,6,rep,name=deferred,proto3" json:"deferred,omitempty"` // This may be set only by the helper/schema "SDK" in the main Terraform // repository, to request that Terraform Core >=0.12 permit additional // inconsistencies that can result from the legacy SDK type system @@ -2775,7 +3052,7 @@ type PlanResourceChange_Response struct { func (x *PlanResourceChange_Response) Reset() { *x = PlanResourceChange_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[48] + mi := &file_tfplugin6_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2788,7 +3065,7 @@ func (x *PlanResourceChange_Response) String() string { func (*PlanResourceChange_Response) ProtoMessage() {} func (x *PlanResourceChange_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[48] + mi := &file_tfplugin6_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2801,7 +3078,7 @@ func (x *PlanResourceChange_Response) ProtoReflect() protoreflect.Message { // Deprecated: Use PlanResourceChange_Response.ProtoReflect.Descriptor instead. func (*PlanResourceChange_Response) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{15, 1} + return file_tfplugin6_proto_rawDescGZIP(), []int{16, 1} } func (x *PlanResourceChange_Response) GetPlannedState() *DynamicValue { @@ -2832,6 +3109,13 @@ func (x *PlanResourceChange_Response) GetDiagnostics() []*Diagnostic { return nil } +func (x *PlanResourceChange_Response) GetDeferred() []*DeferredAction { + if x != nil { + return x.Deferred + } + return nil +} + func (x *PlanResourceChange_Response) GetLegacyTypeSystem() bool { if x != nil { return x.LegacyTypeSystem @@ -2855,7 +3139,7 @@ type ApplyResourceChange_Request struct { func (x *ApplyResourceChange_Request) Reset() { *x = ApplyResourceChange_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[49] + mi := &file_tfplugin6_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2868,7 +3152,7 @@ func (x *ApplyResourceChange_Request) String() string { func (*ApplyResourceChange_Request) ProtoMessage() {} func (x *ApplyResourceChange_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[49] + mi := &file_tfplugin6_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2881,7 +3165,7 @@ func (x *ApplyResourceChange_Request) ProtoReflect() protoreflect.Message { // Deprecated: Use ApplyResourceChange_Request.ProtoReflect.Descriptor instead. func (*ApplyResourceChange_Request) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{16, 0} + return file_tfplugin6_proto_rawDescGZIP(), []int{17, 0} } func (x *ApplyResourceChange_Request) GetTypeName() string { @@ -2951,7 +3235,7 @@ type ApplyResourceChange_Response struct { func (x *ApplyResourceChange_Response) Reset() { *x = ApplyResourceChange_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[50] + mi := &file_tfplugin6_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2964,7 +3248,7 @@ func (x *ApplyResourceChange_Response) String() string { func (*ApplyResourceChange_Response) ProtoMessage() {} func (x *ApplyResourceChange_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[50] + mi := &file_tfplugin6_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2977,7 +3261,7 @@ func (x *ApplyResourceChange_Response) ProtoReflect() protoreflect.Message { // Deprecated: Use ApplyResourceChange_Response.ProtoReflect.Descriptor instead. func (*ApplyResourceChange_Response) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{16, 1} + return file_tfplugin6_proto_rawDescGZIP(), []int{17, 1} } func (x *ApplyResourceChange_Response) GetNewState() *DynamicValue { @@ -3015,12 +3299,17 @@ type ImportResourceState_Request struct { TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + // Terraform Core will set this if it will honor elements of + // "deferred" in the response. If this field is not set then + // a provider must return error diagnostics whenever it has + // insufficient information to import the requested object. + DeferAllowed bool `protobuf:"varint,4,opt,name=defer_allowed,json=deferAllowed,proto3" json:"defer_allowed,omitempty"` } func (x *ImportResourceState_Request) Reset() { *x = ImportResourceState_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[51] + mi := &file_tfplugin6_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3033,7 +3322,7 @@ func (x *ImportResourceState_Request) String() string { func (*ImportResourceState_Request) ProtoMessage() {} func (x *ImportResourceState_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[51] + mi := &file_tfplugin6_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3046,7 +3335,7 @@ func (x *ImportResourceState_Request) ProtoReflect() protoreflect.Message { // Deprecated: Use ImportResourceState_Request.ProtoReflect.Descriptor instead. func (*ImportResourceState_Request) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{17, 0} + return file_tfplugin6_proto_rawDescGZIP(), []int{18, 0} } func (x *ImportResourceState_Request) GetTypeName() string { @@ -3063,6 +3352,13 @@ func (x *ImportResourceState_Request) GetId() string { return "" } +func (x *ImportResourceState_Request) GetDeferAllowed() bool { + if x != nil { + return x.DeferAllowed + } + return false +} + type ImportResourceState_ImportedResource struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3076,7 +3372,7 @@ type ImportResourceState_ImportedResource struct { func (x *ImportResourceState_ImportedResource) Reset() { *x = ImportResourceState_ImportedResource{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[52] + mi := &file_tfplugin6_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3089,7 +3385,7 @@ func (x *ImportResourceState_ImportedResource) String() string { func (*ImportResourceState_ImportedResource) ProtoMessage() {} func (x *ImportResourceState_ImportedResource) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[52] + mi := &file_tfplugin6_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3102,7 +3398,7 @@ func (x *ImportResourceState_ImportedResource) ProtoReflect() protoreflect.Messa // Deprecated: Use ImportResourceState_ImportedResource.ProtoReflect.Descriptor instead. func (*ImportResourceState_ImportedResource) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{17, 1} + return file_tfplugin6_proto_rawDescGZIP(), []int{18, 1} } func (x *ImportResourceState_ImportedResource) GetTypeName() string { @@ -3133,12 +3429,22 @@ type ImportResourceState_Response struct { ImportedResources []*ImportResourceState_ImportedResource `protobuf:"bytes,1,rep,name=imported_resources,json=importedResources,proto3" json:"imported_resources,omitempty"` Diagnostics []*Diagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` + // If the request set defer_allowed to true then the provider may + // annotate a response with one or more deferral reasons, in which + // case Terraform Core will treat this object as entirely unknown + // during downstream planning and defer the import until a later + // run when there's hopefully more information available. + // + // Don't set this if the request did not have defer_allowed set; + // instead return error diagnostics explaining why importing is + // impossible. + Deferred []*DeferredAction `protobuf:"bytes,3,rep,name=deferred,proto3" json:"deferred,omitempty"` } func (x *ImportResourceState_Response) Reset() { *x = ImportResourceState_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[53] + mi := &file_tfplugin6_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3151,7 +3457,7 @@ func (x *ImportResourceState_Response) String() string { func (*ImportResourceState_Response) ProtoMessage() {} func (x *ImportResourceState_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[53] + mi := &file_tfplugin6_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3164,7 +3470,7 @@ func (x *ImportResourceState_Response) ProtoReflect() protoreflect.Message { // Deprecated: Use ImportResourceState_Response.ProtoReflect.Descriptor instead. func (*ImportResourceState_Response) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{17, 2} + return file_tfplugin6_proto_rawDescGZIP(), []int{18, 2} } func (x *ImportResourceState_Response) GetImportedResources() []*ImportResourceState_ImportedResource { @@ -3181,6 +3487,13 @@ func (x *ImportResourceState_Response) GetDiagnostics() []*Diagnostic { return nil } +func (x *ImportResourceState_Response) GetDeferred() []*DeferredAction { + if x != nil { + return x.Deferred + } + return nil +} + type ReadDataSource_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3189,12 +3502,17 @@ type ReadDataSource_Request struct { TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` Config *DynamicValue `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` ProviderMeta *DynamicValue `protobuf:"bytes,3,opt,name=provider_meta,json=providerMeta,proto3" json:"provider_meta,omitempty"` + // Terraform Core will set this if it will honor elements of + // "deferred" in the response. If this field is not set then + // a provider must return error diagnostics whenever it has + // insufficient information to fetch the requested remote object. + DeferAllowed bool `protobuf:"varint,4,opt,name=defer_allowed,json=deferAllowed,proto3" json:"defer_allowed,omitempty"` } func (x *ReadDataSource_Request) Reset() { *x = ReadDataSource_Request{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[54] + mi := &file_tfplugin6_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3207,7 +3525,7 @@ func (x *ReadDataSource_Request) String() string { func (*ReadDataSource_Request) ProtoMessage() {} func (x *ReadDataSource_Request) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[54] + mi := &file_tfplugin6_proto_msgTypes[57] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3220,7 +3538,7 @@ func (x *ReadDataSource_Request) ProtoReflect() protoreflect.Message { // Deprecated: Use ReadDataSource_Request.ProtoReflect.Descriptor instead. func (*ReadDataSource_Request) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{18, 0} + return file_tfplugin6_proto_rawDescGZIP(), []int{19, 0} } func (x *ReadDataSource_Request) GetTypeName() string { @@ -3244,6 +3562,13 @@ func (x *ReadDataSource_Request) GetProviderMeta() *DynamicValue { return nil } +func (x *ReadDataSource_Request) GetDeferAllowed() bool { + if x != nil { + return x.DeferAllowed + } + return false +} + type ReadDataSource_Response struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3251,12 +3576,22 @@ type ReadDataSource_Response struct { State *DynamicValue `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` Diagnostics []*Diagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` + // If the request set defer_allowed to true then the provider may + // annotate a response with one or more deferral reasons, in which + // case Terraform Core will still use the partial "state" value + // to plan downstream actions but will then defer them until a + // future run where a similar ReadDataSource request can return + // a complete result. + // Don't set this if the request did not have defer_allowed set; + // instead return error diagnostics explaining why reading is + // impossible. + Deferred []*DeferredAction `protobuf:"bytes,3,rep,name=deferred,proto3" json:"deferred,omitempty"` } func (x *ReadDataSource_Response) Reset() { *x = ReadDataSource_Response{} if protoimpl.UnsafeEnabled { - mi := &file_tfplugin6_proto_msgTypes[55] + mi := &file_tfplugin6_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3269,7 +3604,7 @@ func (x *ReadDataSource_Response) String() string { func (*ReadDataSource_Response) ProtoMessage() {} func (x *ReadDataSource_Response) ProtoReflect() protoreflect.Message { - mi := &file_tfplugin6_proto_msgTypes[55] + mi := &file_tfplugin6_proto_msgTypes[58] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3282,7 +3617,7 @@ func (x *ReadDataSource_Response) ProtoReflect() protoreflect.Message { // Deprecated: Use ReadDataSource_Response.ProtoReflect.Descriptor instead. func (*ReadDataSource_Response) Descriptor() ([]byte, []int) { - return file_tfplugin6_proto_rawDescGZIP(), []int{18, 1} + return file_tfplugin6_proto_rawDescGZIP(), []int{19, 1} } func (x *ReadDataSource_Response) GetState() *DynamicValue { @@ -3299,6 +3634,13 @@ func (x *ReadDataSource_Response) GetDiagnostics() []*Diagnostic { return nil } +func (x *ReadDataSource_Response) GetDeferred() []*DeferredAction { + if x != nil { + return x.Deferred + } + return nil +} + var File_tfplugin6_proto protoreflect.FileDescriptor var file_tfplugin6_proto_rawDesc = []byte{ @@ -3438,162 +3780,198 @@ var file_tfplugin6_proto_rawDesc = []byte{ 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x67, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x22, 0xa7, 0x03, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0xa8, 0x02, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x4e, 0x0a, 0x13, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, - 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, - 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, - 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x12, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, - 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, - 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, - 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x4c, 0x0a, 0x0c, 0x64, 0x61, 0x74, 0x61, - 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, - 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x53, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x45, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x74, 0x66, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x1a, 0x31, 0x0a, - 0x12, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x1a, 0x2f, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x22, 0xa0, 0x05, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x1a, 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0xff, 0x04, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x2d, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x65, - 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x73, 0x12, 0x6c, 0x0a, 0x13, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x47, - 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, - 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x11, 0x64, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x63, 0x68, 0x65, - 0x6d, 0x61, 0x73, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, - 0x63, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, - 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x36, 0x0a, 0x0d, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, - 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x4d, 0x65, 0x74, 0x61, 0x12, 0x4e, 0x0a, 0x13, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x63, - 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, - 0x52, 0x12, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, - 0x74, 0x69, 0x65, 0x73, 0x1a, 0x55, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x22, 0x84, 0x03, 0x0a, 0x0e, 0x44, 0x65, 0x66, 0x65, + 0x72, 0x72, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4a, 0x0a, 0x0c, 0x6f, 0x74, + 0x68, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x25, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x65, 0x66, + 0x65, 0x72, 0x72, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4f, 0x74, 0x68, 0x65, + 0x72, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0b, 0x6f, 0x74, 0x68, 0x65, 0x72, + 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x61, 0x0a, 0x17, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, + 0x48, 0x00, 0x52, 0x15, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x12, 0x61, 0x0a, 0x17, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x75, 0x6e, 0x6b, + 0x6e, 0x6f, 0x77, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x55, 0x6e, 0x6b, 0x6e, + 0x6f, 0x77, 0x6e, 0x48, 0x00, 0x52, 0x15, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x1a, 0x0d, 0x0a, 0x0b, + 0x4f, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x1a, 0x47, 0x0a, 0x0d, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x12, 0x36, 0x0a, 0x09, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x18, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x41, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x50, 0x61, 0x74, 0x68, 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x42, 0x08, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0xa7, + 0x03, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x09, + 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0xa8, 0x02, 0x0a, 0x08, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x13, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, + 0x65, 0x73, 0x52, 0x12, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, + 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, + 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, + 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, + 0x4c, 0x0a, 0x0c, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x36, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x44, 0x61, + 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x52, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x45, 0x0a, + 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x27, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x47, 0x65, 0x74, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x1a, 0x31, 0x0a, 0x12, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, + 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, + 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x2f, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x09, 0x74, + 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xa0, 0x05, 0x0a, 0x11, 0x47, 0x65, 0x74, + 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x1a, 0x09, + 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0xff, 0x04, 0x0a, 0x08, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x08, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x65, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x3a, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x47, 0x65, 0x74, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x12, 0x6c, 0x0a, 0x13, + 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x64, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, + 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, + 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, + 0x69, 0x63, 0x73, 0x12, 0x36, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, + 0x6d, 0x65, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x0c, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x4e, 0x0a, 0x13, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, + 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, + 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x12, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, + 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x1a, 0x55, 0x0a, 0x14, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 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, 0x27, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, + 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x1a, 0x57, 0x0a, 0x16, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 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, 0x27, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x57, 0x0a, 0x16, 0x44, - 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 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, 0x27, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x36, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0x99, 0x01, 0x0a, 0x16, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, - 0x3a, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x99, 0x01, 0x0a, 0x16, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x3a, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, + 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x1a, 0x43, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, + 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, + 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, + 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0xed, 0x02, 0x0a, 0x14, 0x55, 0x70, 0x67, 0x72, + 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x1a, 0x97, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, + 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x09, 0x72, 0x61, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x36, 0x2e, 0x52, 0x61, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x08, 0x72, 0x61, 0x77, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x65, 0x66, 0x65, 0x72, 0x5f, 0x61, + 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x65, + 0x66, 0x65, 0x72, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x1a, 0xba, 0x01, 0x0a, 0x08, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0e, 0x75, 0x70, 0x67, 0x72, 0x61, + 0x64, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, + 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0d, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, + 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, + 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, + 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, + 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, + 0x12, 0x35, 0x0a, 0x08, 0x64, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, + 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x64, + 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x22, 0xb6, 0x01, 0x0a, 0x16, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x1a, 0x57, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, + 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x43, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, - 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, + 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, - 0x22, 0x90, 0x02, 0x0a, 0x14, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x72, 0x0a, 0x07, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x09, 0x72, - 0x61, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x52, 0x61, 0x77, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x52, 0x08, 0x72, 0x61, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x83, 0x01, - 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0e, 0x75, 0x70, - 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, - 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0d, 0x75, 0x70, 0x67, - 0x72, 0x61, 0x64, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, - 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, - 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, - 0x69, 0x63, 0x73, 0x22, 0xb6, 0x01, 0x0a, 0x16, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x57, - 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, - 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, - 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x43, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, - 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, - 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0xba, 0x01, 0x0a, - 0x1a, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x57, 0x0a, 0x07, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, - 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x43, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, - 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0xc1, 0x01, 0x0a, 0x11, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x1a, - 0x67, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x65, - 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x22, 0xba, 0x01, 0x0a, 0x1a, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, + 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, + 0x57, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, + 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, + 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x43, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, - 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0xe3, 0x02, - 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0xbc, - 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, - 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, - 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, - 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, - 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x61, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x1a, 0x93, 0x01, - 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x09, 0x6e, 0x65, - 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, - 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, - 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, + 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0xc1, 0x01, + 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x1a, 0x67, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, + 0x0a, 0x11, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x74, 0x65, 0x72, 0x72, 0x61, + 0x66, 0x6f, 0x72, 0x6d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2f, 0x0a, 0x06, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x43, 0x0a, 0x08, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, + 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, + 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, + 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, + 0x73, 0x22, 0xbf, 0x03, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x1a, 0xe1, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, + 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, + 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, - 0x61, 0x74, 0x65, 0x22, 0xf2, 0x04, 0x0a, 0x12, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x1a, 0xbb, 0x02, 0x0a, 0x07, 0x52, + 0x61, 0x74, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, + 0x6d, 0x65, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, + 0x61, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x65, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, + 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x65, 0x66, 0x65, 0x72, 0x41, + 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x1a, 0xca, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x08, 0x6e, 0x65, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, + 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, + 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, + 0x63, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x35, 0x0a, 0x08, + 0x64, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, + 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x65, 0x66, 0x65, 0x72, + 0x72, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x64, 0x65, 0x66, 0x65, 0x72, + 0x72, 0x65, 0x64, 0x22, 0xce, 0x05, 0x0a, 0x12, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x1a, 0xe0, 0x02, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, @@ -3613,187 +3991,205 @@ var file_tfplugin6_proto_rawDesc = []byte{ 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x1a, 0x9d, 0x02, 0x0a, 0x08, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, - 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, - 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x12, 0x43, 0x0a, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x5f, - 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, - 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, - 0x73, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x6c, 0x61, 0x6e, - 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x0e, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, - 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, - 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, - 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x6c, 0x65, - 0x67, 0x61, 0x63, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x54, 0x79, - 0x70, 0x65, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x22, 0x92, 0x04, 0x0a, 0x13, 0x41, 0x70, 0x70, - 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x1a, 0xb6, 0x02, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, - 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x70, 0x72, 0x69, - 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, - 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x65, 0x66, 0x65, + 0x72, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0c, 0x64, 0x65, 0x66, 0x65, 0x72, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x1a, 0xd4, 0x02, + 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x6c, + 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, - 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x72, - 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x70, 0x6c, 0x61, - 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, - 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x1a, 0xc1, 0x01, 0x0a, 0x08, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, - 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, + 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x6c, 0x61, 0x6e, + 0x6e, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x43, 0x0a, 0x10, 0x72, 0x65, 0x71, 0x75, + 0x69, 0x72, 0x65, 0x73, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x41, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0f, 0x72, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x12, 0x27, 0x0a, + 0x0f, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, - 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, + 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, - 0x2c, 0x0a, 0x12, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, - 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6c, 0x65, 0x67, - 0x61, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x22, 0xed, 0x02, - 0x0a, 0x13, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x36, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x1a, 0x78, 0x0a, - 0x10, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2d, - 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x35, 0x0a, 0x08, 0x64, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x18, 0x06, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x65, + 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x64, 0x65, + 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x12, 0x2c, 0x0a, 0x12, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x10, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x53, 0x79, + 0x73, 0x74, 0x65, 0x6d, 0x22, 0x92, 0x04, 0x0a, 0x13, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x1a, 0xb6, 0x02, 0x0a, + 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x5f, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, + 0x3c, 0x0a, 0x0d, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x0c, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x0a, + 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, - 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, - 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, - 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x1a, 0xa3, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x12, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, - 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x2f, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x49, 0x6d, 0x70, - 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x52, 0x11, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, - 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, - 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0x9c, 0x02, - 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x1a, 0x95, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, + 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x27, + 0x0a, 0x0f, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, + 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, + 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x4d, 0x65, 0x74, 0x61, 0x1a, 0xc1, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x34, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, + 0x6e, 0x65, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, + 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, + 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, + 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x6c, + 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, + 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x54, + 0x79, 0x70, 0x65, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x22, 0xc9, 0x03, 0x0a, 0x13, 0x49, 0x6d, + 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x1a, 0x5b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x65, 0x66, + 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0c, 0x64, 0x65, 0x66, 0x65, 0x72, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x1a, 0x78, + 0x0a, 0x10, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, + 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x1a, 0xda, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x12, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x49, 0x6d, + 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x52, 0x11, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, + 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, + 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x35, + 0x0a, 0x08, 0x64, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x65, 0x66, + 0x65, 0x72, 0x72, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x64, 0x65, 0x66, + 0x65, 0x72, 0x72, 0x65, 0x64, 0x22, 0xf9, 0x02, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, + 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0xba, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, + 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, + 0x65, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x75, 0x65, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, + 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x65, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, + 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x65, 0x66, 0x65, 0x72, 0x41, 0x6c, + 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x1a, 0xa9, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, - 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x1a, 0x72, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, - 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, - 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, - 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x2a, 0x25, 0x0a, 0x0a, - 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x4c, - 0x41, 0x49, 0x4e, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x4d, 0x41, 0x52, 0x4b, 0x44, 0x4f, 0x57, - 0x4e, 0x10, 0x01, 0x32, 0x9c, 0x0a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x12, 0x4e, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, - 0x1e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x47, 0x65, 0x74, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1f, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x47, 0x65, 0x74, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x60, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x24, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x36, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, - 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x74, 0x66, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x6f, 0x0a, 0x16, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x2e, 0x74, + 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, + 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x35, 0x0a, 0x08, 0x64, 0x65, + 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, + 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, + 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x64, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, + 0x64, 0x2a, 0x25, 0x0a, 0x0a, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4b, 0x69, 0x6e, 0x64, 0x12, + 0x09, 0x0a, 0x05, 0x50, 0x4c, 0x41, 0x49, 0x4e, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x4d, 0x41, + 0x52, 0x4b, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x01, 0x32, 0x9c, 0x0a, 0x0a, 0x08, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x4e, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x12, 0x1e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, + 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, + 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x24, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x25, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x47, 0x65, 0x74, + 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6f, 0x0a, 0x16, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x29, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x36, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x6f, 0x0a, 0x16, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x2e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6f, 0x0a, 0x16, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x29, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7b, 0x0a, 0x1a, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, - 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x2d, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x69, 0x0a, 0x14, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x27, 0x2e, 0x74, 0x66, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, + 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7b, 0x0a, 0x1a, 0x56, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x36, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x36, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, 0x14, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, + 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x27, + 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x55, 0x70, 0x67, 0x72, 0x61, + 0x64, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x36, 0x2e, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x60, 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x24, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x36, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x74, + 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, + 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x12, 0x1f, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, + 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, + 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x63, 0x0a, 0x12, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x25, 0x2e, 0x74, + 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, + 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x13, 0x41, + 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x12, 0x26, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x41, + 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x13, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x26, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x55, - 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x11, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x12, 0x24, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x36, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, - 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1f, - 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x20, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x52, 0x65, 0x61, 0x64, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x63, 0x0a, 0x12, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x25, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x36, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, - 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x13, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x26, 0x2e, - 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x36, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, - 0x0a, 0x13, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x26, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x36, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, - 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, - 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x21, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x74, 0x66, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, - 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x51, 0x0a, 0x0c, 0x53, 0x74, 0x6f, 0x70, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, - 0x1f, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x74, 0x6f, 0x70, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x20, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x74, 0x6f, - 0x70, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x42, 0x33, 0x5a, 0x31, 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, 0x74, 0x66, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x49, + 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0e, 0x52, + 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x21, 0x2e, + 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, + 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x22, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x52, 0x65, 0x61, + 0x64, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x0c, 0x53, 0x74, 0x6f, 0x70, 0x50, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, + 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x36, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x33, 0x5a, 0x31, 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, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -3809,7 +4205,7 @@ func file_tfplugin6_proto_rawDescGZIP() []byte { } var file_tfplugin6_proto_enumTypes = make([]protoimpl.EnumInfo, 4) -var file_tfplugin6_proto_msgTypes = make([]protoimpl.MessageInfo, 56) +var file_tfplugin6_proto_msgTypes = make([]protoimpl.MessageInfo, 59) var file_tfplugin6_proto_goTypes = []interface{}{ (StringKind)(0), // 0: tfplugin6.StringKind (Diagnostic_Severity)(0), // 1: tfplugin6.Diagnostic.Severity @@ -3822,149 +4218,161 @@ var file_tfplugin6_proto_goTypes = []interface{}{ (*RawState)(nil), // 8: tfplugin6.RawState (*Schema)(nil), // 9: tfplugin6.Schema (*ServerCapabilities)(nil), // 10: tfplugin6.ServerCapabilities - (*GetMetadata)(nil), // 11: tfplugin6.GetMetadata - (*GetProviderSchema)(nil), // 12: tfplugin6.GetProviderSchema - (*ValidateProviderConfig)(nil), // 13: tfplugin6.ValidateProviderConfig - (*UpgradeResourceState)(nil), // 14: tfplugin6.UpgradeResourceState - (*ValidateResourceConfig)(nil), // 15: tfplugin6.ValidateResourceConfig - (*ValidateDataResourceConfig)(nil), // 16: tfplugin6.ValidateDataResourceConfig - (*ConfigureProvider)(nil), // 17: tfplugin6.ConfigureProvider - (*ReadResource)(nil), // 18: tfplugin6.ReadResource - (*PlanResourceChange)(nil), // 19: tfplugin6.PlanResourceChange - (*ApplyResourceChange)(nil), // 20: tfplugin6.ApplyResourceChange - (*ImportResourceState)(nil), // 21: tfplugin6.ImportResourceState - (*ReadDataSource)(nil), // 22: tfplugin6.ReadDataSource - (*AttributePath_Step)(nil), // 23: tfplugin6.AttributePath.Step - (*StopProvider_Request)(nil), // 24: tfplugin6.StopProvider.Request - (*StopProvider_Response)(nil), // 25: tfplugin6.StopProvider.Response - nil, // 26: tfplugin6.RawState.FlatmapEntry - (*Schema_Block)(nil), // 27: tfplugin6.Schema.Block - (*Schema_Attribute)(nil), // 28: tfplugin6.Schema.Attribute - (*Schema_NestedBlock)(nil), // 29: tfplugin6.Schema.NestedBlock - (*Schema_Object)(nil), // 30: tfplugin6.Schema.Object - (*GetMetadata_Request)(nil), // 31: tfplugin6.GetMetadata.Request - (*GetMetadata_Response)(nil), // 32: tfplugin6.GetMetadata.Response - (*GetMetadata_DataSourceMetadata)(nil), // 33: tfplugin6.GetMetadata.DataSourceMetadata - (*GetMetadata_ResourceMetadata)(nil), // 34: tfplugin6.GetMetadata.ResourceMetadata - (*GetProviderSchema_Request)(nil), // 35: tfplugin6.GetProviderSchema.Request - (*GetProviderSchema_Response)(nil), // 36: tfplugin6.GetProviderSchema.Response - nil, // 37: tfplugin6.GetProviderSchema.Response.ResourceSchemasEntry - nil, // 38: tfplugin6.GetProviderSchema.Response.DataSourceSchemasEntry - (*ValidateProviderConfig_Request)(nil), // 39: tfplugin6.ValidateProviderConfig.Request - (*ValidateProviderConfig_Response)(nil), // 40: tfplugin6.ValidateProviderConfig.Response - (*UpgradeResourceState_Request)(nil), // 41: tfplugin6.UpgradeResourceState.Request - (*UpgradeResourceState_Response)(nil), // 42: tfplugin6.UpgradeResourceState.Response - (*ValidateResourceConfig_Request)(nil), // 43: tfplugin6.ValidateResourceConfig.Request - (*ValidateResourceConfig_Response)(nil), // 44: tfplugin6.ValidateResourceConfig.Response - (*ValidateDataResourceConfig_Request)(nil), // 45: tfplugin6.ValidateDataResourceConfig.Request - (*ValidateDataResourceConfig_Response)(nil), // 46: tfplugin6.ValidateDataResourceConfig.Response - (*ConfigureProvider_Request)(nil), // 47: tfplugin6.ConfigureProvider.Request - (*ConfigureProvider_Response)(nil), // 48: tfplugin6.ConfigureProvider.Response - (*ReadResource_Request)(nil), // 49: tfplugin6.ReadResource.Request - (*ReadResource_Response)(nil), // 50: tfplugin6.ReadResource.Response - (*PlanResourceChange_Request)(nil), // 51: tfplugin6.PlanResourceChange.Request - (*PlanResourceChange_Response)(nil), // 52: tfplugin6.PlanResourceChange.Response - (*ApplyResourceChange_Request)(nil), // 53: tfplugin6.ApplyResourceChange.Request - (*ApplyResourceChange_Response)(nil), // 54: tfplugin6.ApplyResourceChange.Response - (*ImportResourceState_Request)(nil), // 55: tfplugin6.ImportResourceState.Request - (*ImportResourceState_ImportedResource)(nil), // 56: tfplugin6.ImportResourceState.ImportedResource - (*ImportResourceState_Response)(nil), // 57: tfplugin6.ImportResourceState.Response - (*ReadDataSource_Request)(nil), // 58: tfplugin6.ReadDataSource.Request - (*ReadDataSource_Response)(nil), // 59: tfplugin6.ReadDataSource.Response + (*DeferredAction)(nil), // 11: tfplugin6.DeferredAction + (*GetMetadata)(nil), // 12: tfplugin6.GetMetadata + (*GetProviderSchema)(nil), // 13: tfplugin6.GetProviderSchema + (*ValidateProviderConfig)(nil), // 14: tfplugin6.ValidateProviderConfig + (*UpgradeResourceState)(nil), // 15: tfplugin6.UpgradeResourceState + (*ValidateResourceConfig)(nil), // 16: tfplugin6.ValidateResourceConfig + (*ValidateDataResourceConfig)(nil), // 17: tfplugin6.ValidateDataResourceConfig + (*ConfigureProvider)(nil), // 18: tfplugin6.ConfigureProvider + (*ReadResource)(nil), // 19: tfplugin6.ReadResource + (*PlanResourceChange)(nil), // 20: tfplugin6.PlanResourceChange + (*ApplyResourceChange)(nil), // 21: tfplugin6.ApplyResourceChange + (*ImportResourceState)(nil), // 22: tfplugin6.ImportResourceState + (*ReadDataSource)(nil), // 23: tfplugin6.ReadDataSource + (*AttributePath_Step)(nil), // 24: tfplugin6.AttributePath.Step + (*StopProvider_Request)(nil), // 25: tfplugin6.StopProvider.Request + (*StopProvider_Response)(nil), // 26: tfplugin6.StopProvider.Response + nil, // 27: tfplugin6.RawState.FlatmapEntry + (*Schema_Block)(nil), // 28: tfplugin6.Schema.Block + (*Schema_Attribute)(nil), // 29: tfplugin6.Schema.Attribute + (*Schema_NestedBlock)(nil), // 30: tfplugin6.Schema.NestedBlock + (*Schema_Object)(nil), // 31: tfplugin6.Schema.Object + (*DeferredAction_OtherReason)(nil), // 32: tfplugin6.DeferredAction.OtherReason + (*DeferredAction_ConfigUnknown)(nil), // 33: tfplugin6.DeferredAction.ConfigUnknown + (*GetMetadata_Request)(nil), // 34: tfplugin6.GetMetadata.Request + (*GetMetadata_Response)(nil), // 35: tfplugin6.GetMetadata.Response + (*GetMetadata_DataSourceMetadata)(nil), // 36: tfplugin6.GetMetadata.DataSourceMetadata + (*GetMetadata_ResourceMetadata)(nil), // 37: tfplugin6.GetMetadata.ResourceMetadata + (*GetProviderSchema_Request)(nil), // 38: tfplugin6.GetProviderSchema.Request + (*GetProviderSchema_Response)(nil), // 39: tfplugin6.GetProviderSchema.Response + nil, // 40: tfplugin6.GetProviderSchema.Response.ResourceSchemasEntry + nil, // 41: tfplugin6.GetProviderSchema.Response.DataSourceSchemasEntry + (*ValidateProviderConfig_Request)(nil), // 42: tfplugin6.ValidateProviderConfig.Request + (*ValidateProviderConfig_Response)(nil), // 43: tfplugin6.ValidateProviderConfig.Response + (*UpgradeResourceState_Request)(nil), // 44: tfplugin6.UpgradeResourceState.Request + (*UpgradeResourceState_Response)(nil), // 45: tfplugin6.UpgradeResourceState.Response + (*ValidateResourceConfig_Request)(nil), // 46: tfplugin6.ValidateResourceConfig.Request + (*ValidateResourceConfig_Response)(nil), // 47: tfplugin6.ValidateResourceConfig.Response + (*ValidateDataResourceConfig_Request)(nil), // 48: tfplugin6.ValidateDataResourceConfig.Request + (*ValidateDataResourceConfig_Response)(nil), // 49: tfplugin6.ValidateDataResourceConfig.Response + (*ConfigureProvider_Request)(nil), // 50: tfplugin6.ConfigureProvider.Request + (*ConfigureProvider_Response)(nil), // 51: tfplugin6.ConfigureProvider.Response + (*ReadResource_Request)(nil), // 52: tfplugin6.ReadResource.Request + (*ReadResource_Response)(nil), // 53: tfplugin6.ReadResource.Response + (*PlanResourceChange_Request)(nil), // 54: tfplugin6.PlanResourceChange.Request + (*PlanResourceChange_Response)(nil), // 55: tfplugin6.PlanResourceChange.Response + (*ApplyResourceChange_Request)(nil), // 56: tfplugin6.ApplyResourceChange.Request + (*ApplyResourceChange_Response)(nil), // 57: tfplugin6.ApplyResourceChange.Response + (*ImportResourceState_Request)(nil), // 58: tfplugin6.ImportResourceState.Request + (*ImportResourceState_ImportedResource)(nil), // 59: tfplugin6.ImportResourceState.ImportedResource + (*ImportResourceState_Response)(nil), // 60: tfplugin6.ImportResourceState.Response + (*ReadDataSource_Request)(nil), // 61: tfplugin6.ReadDataSource.Request + (*ReadDataSource_Response)(nil), // 62: tfplugin6.ReadDataSource.Response } var file_tfplugin6_proto_depIdxs = []int32{ 1, // 0: tfplugin6.Diagnostic.severity:type_name -> tfplugin6.Diagnostic.Severity 6, // 1: tfplugin6.Diagnostic.attribute:type_name -> tfplugin6.AttributePath - 23, // 2: tfplugin6.AttributePath.steps:type_name -> tfplugin6.AttributePath.Step - 26, // 3: tfplugin6.RawState.flatmap:type_name -> tfplugin6.RawState.FlatmapEntry - 27, // 4: tfplugin6.Schema.block:type_name -> tfplugin6.Schema.Block - 28, // 5: tfplugin6.Schema.Block.attributes:type_name -> tfplugin6.Schema.Attribute - 29, // 6: tfplugin6.Schema.Block.block_types:type_name -> tfplugin6.Schema.NestedBlock - 0, // 7: tfplugin6.Schema.Block.description_kind:type_name -> tfplugin6.StringKind - 30, // 8: tfplugin6.Schema.Attribute.nested_type:type_name -> tfplugin6.Schema.Object - 0, // 9: tfplugin6.Schema.Attribute.description_kind:type_name -> tfplugin6.StringKind - 27, // 10: tfplugin6.Schema.NestedBlock.block:type_name -> tfplugin6.Schema.Block - 2, // 11: tfplugin6.Schema.NestedBlock.nesting:type_name -> tfplugin6.Schema.NestedBlock.NestingMode - 28, // 12: tfplugin6.Schema.Object.attributes:type_name -> tfplugin6.Schema.Attribute - 3, // 13: tfplugin6.Schema.Object.nesting:type_name -> tfplugin6.Schema.Object.NestingMode - 10, // 14: tfplugin6.GetMetadata.Response.server_capabilities:type_name -> tfplugin6.ServerCapabilities - 5, // 15: tfplugin6.GetMetadata.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 33, // 16: tfplugin6.GetMetadata.Response.data_sources:type_name -> tfplugin6.GetMetadata.DataSourceMetadata - 34, // 17: tfplugin6.GetMetadata.Response.resources:type_name -> tfplugin6.GetMetadata.ResourceMetadata - 9, // 18: tfplugin6.GetProviderSchema.Response.provider:type_name -> tfplugin6.Schema - 37, // 19: tfplugin6.GetProviderSchema.Response.resource_schemas:type_name -> tfplugin6.GetProviderSchema.Response.ResourceSchemasEntry - 38, // 20: tfplugin6.GetProviderSchema.Response.data_source_schemas:type_name -> tfplugin6.GetProviderSchema.Response.DataSourceSchemasEntry - 5, // 21: tfplugin6.GetProviderSchema.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 9, // 22: tfplugin6.GetProviderSchema.Response.provider_meta:type_name -> tfplugin6.Schema - 10, // 23: tfplugin6.GetProviderSchema.Response.server_capabilities:type_name -> tfplugin6.ServerCapabilities - 9, // 24: tfplugin6.GetProviderSchema.Response.ResourceSchemasEntry.value:type_name -> tfplugin6.Schema - 9, // 25: tfplugin6.GetProviderSchema.Response.DataSourceSchemasEntry.value:type_name -> tfplugin6.Schema - 4, // 26: tfplugin6.ValidateProviderConfig.Request.config:type_name -> tfplugin6.DynamicValue - 5, // 27: tfplugin6.ValidateProviderConfig.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 8, // 28: tfplugin6.UpgradeResourceState.Request.raw_state:type_name -> tfplugin6.RawState - 4, // 29: tfplugin6.UpgradeResourceState.Response.upgraded_state:type_name -> tfplugin6.DynamicValue - 5, // 30: tfplugin6.UpgradeResourceState.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 4, // 31: tfplugin6.ValidateResourceConfig.Request.config:type_name -> tfplugin6.DynamicValue - 5, // 32: tfplugin6.ValidateResourceConfig.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 4, // 33: tfplugin6.ValidateDataResourceConfig.Request.config:type_name -> tfplugin6.DynamicValue - 5, // 34: tfplugin6.ValidateDataResourceConfig.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 4, // 35: tfplugin6.ConfigureProvider.Request.config:type_name -> tfplugin6.DynamicValue - 5, // 36: tfplugin6.ConfigureProvider.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 4, // 37: tfplugin6.ReadResource.Request.current_state:type_name -> tfplugin6.DynamicValue - 4, // 38: tfplugin6.ReadResource.Request.provider_meta:type_name -> tfplugin6.DynamicValue - 4, // 39: tfplugin6.ReadResource.Response.new_state:type_name -> tfplugin6.DynamicValue - 5, // 40: tfplugin6.ReadResource.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 4, // 41: tfplugin6.PlanResourceChange.Request.prior_state:type_name -> tfplugin6.DynamicValue - 4, // 42: tfplugin6.PlanResourceChange.Request.proposed_new_state:type_name -> tfplugin6.DynamicValue - 4, // 43: tfplugin6.PlanResourceChange.Request.config:type_name -> tfplugin6.DynamicValue - 4, // 44: tfplugin6.PlanResourceChange.Request.provider_meta:type_name -> tfplugin6.DynamicValue - 4, // 45: tfplugin6.PlanResourceChange.Response.planned_state:type_name -> tfplugin6.DynamicValue - 6, // 46: tfplugin6.PlanResourceChange.Response.requires_replace:type_name -> tfplugin6.AttributePath - 5, // 47: tfplugin6.PlanResourceChange.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 4, // 48: tfplugin6.ApplyResourceChange.Request.prior_state:type_name -> tfplugin6.DynamicValue - 4, // 49: tfplugin6.ApplyResourceChange.Request.planned_state:type_name -> tfplugin6.DynamicValue - 4, // 50: tfplugin6.ApplyResourceChange.Request.config:type_name -> tfplugin6.DynamicValue - 4, // 51: tfplugin6.ApplyResourceChange.Request.provider_meta:type_name -> tfplugin6.DynamicValue - 4, // 52: tfplugin6.ApplyResourceChange.Response.new_state:type_name -> tfplugin6.DynamicValue - 5, // 53: tfplugin6.ApplyResourceChange.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 4, // 54: tfplugin6.ImportResourceState.ImportedResource.state:type_name -> tfplugin6.DynamicValue - 56, // 55: tfplugin6.ImportResourceState.Response.imported_resources:type_name -> tfplugin6.ImportResourceState.ImportedResource - 5, // 56: tfplugin6.ImportResourceState.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 4, // 57: tfplugin6.ReadDataSource.Request.config:type_name -> tfplugin6.DynamicValue - 4, // 58: tfplugin6.ReadDataSource.Request.provider_meta:type_name -> tfplugin6.DynamicValue - 4, // 59: tfplugin6.ReadDataSource.Response.state:type_name -> tfplugin6.DynamicValue - 5, // 60: tfplugin6.ReadDataSource.Response.diagnostics:type_name -> tfplugin6.Diagnostic - 31, // 61: tfplugin6.Provider.GetMetadata:input_type -> tfplugin6.GetMetadata.Request - 35, // 62: tfplugin6.Provider.GetProviderSchema:input_type -> tfplugin6.GetProviderSchema.Request - 39, // 63: tfplugin6.Provider.ValidateProviderConfig:input_type -> tfplugin6.ValidateProviderConfig.Request - 43, // 64: tfplugin6.Provider.ValidateResourceConfig:input_type -> tfplugin6.ValidateResourceConfig.Request - 45, // 65: tfplugin6.Provider.ValidateDataResourceConfig:input_type -> tfplugin6.ValidateDataResourceConfig.Request - 41, // 66: tfplugin6.Provider.UpgradeResourceState:input_type -> tfplugin6.UpgradeResourceState.Request - 47, // 67: tfplugin6.Provider.ConfigureProvider:input_type -> tfplugin6.ConfigureProvider.Request - 49, // 68: tfplugin6.Provider.ReadResource:input_type -> tfplugin6.ReadResource.Request - 51, // 69: tfplugin6.Provider.PlanResourceChange:input_type -> tfplugin6.PlanResourceChange.Request - 53, // 70: tfplugin6.Provider.ApplyResourceChange:input_type -> tfplugin6.ApplyResourceChange.Request - 55, // 71: tfplugin6.Provider.ImportResourceState:input_type -> tfplugin6.ImportResourceState.Request - 58, // 72: tfplugin6.Provider.ReadDataSource:input_type -> tfplugin6.ReadDataSource.Request - 24, // 73: tfplugin6.Provider.StopProvider:input_type -> tfplugin6.StopProvider.Request - 32, // 74: tfplugin6.Provider.GetMetadata:output_type -> tfplugin6.GetMetadata.Response - 36, // 75: tfplugin6.Provider.GetProviderSchema:output_type -> tfplugin6.GetProviderSchema.Response - 40, // 76: tfplugin6.Provider.ValidateProviderConfig:output_type -> tfplugin6.ValidateProviderConfig.Response - 44, // 77: tfplugin6.Provider.ValidateResourceConfig:output_type -> tfplugin6.ValidateResourceConfig.Response - 46, // 78: tfplugin6.Provider.ValidateDataResourceConfig:output_type -> tfplugin6.ValidateDataResourceConfig.Response - 42, // 79: tfplugin6.Provider.UpgradeResourceState:output_type -> tfplugin6.UpgradeResourceState.Response - 48, // 80: tfplugin6.Provider.ConfigureProvider:output_type -> tfplugin6.ConfigureProvider.Response - 50, // 81: tfplugin6.Provider.ReadResource:output_type -> tfplugin6.ReadResource.Response - 52, // 82: tfplugin6.Provider.PlanResourceChange:output_type -> tfplugin6.PlanResourceChange.Response - 54, // 83: tfplugin6.Provider.ApplyResourceChange:output_type -> tfplugin6.ApplyResourceChange.Response - 57, // 84: tfplugin6.Provider.ImportResourceState:output_type -> tfplugin6.ImportResourceState.Response - 59, // 85: tfplugin6.Provider.ReadDataSource:output_type -> tfplugin6.ReadDataSource.Response - 25, // 86: tfplugin6.Provider.StopProvider:output_type -> tfplugin6.StopProvider.Response - 74, // [74:87] is the sub-list for method output_type - 61, // [61:74] is the sub-list for method input_type - 61, // [61:61] is the sub-list for extension type_name - 61, // [61:61] is the sub-list for extension extendee - 0, // [0:61] is the sub-list for field type_name + 24, // 2: tfplugin6.AttributePath.steps:type_name -> tfplugin6.AttributePath.Step + 27, // 3: tfplugin6.RawState.flatmap:type_name -> tfplugin6.RawState.FlatmapEntry + 28, // 4: tfplugin6.Schema.block:type_name -> tfplugin6.Schema.Block + 32, // 5: tfplugin6.DeferredAction.other_reason:type_name -> tfplugin6.DeferredAction.OtherReason + 33, // 6: tfplugin6.DeferredAction.provider_config_unknown:type_name -> tfplugin6.DeferredAction.ConfigUnknown + 33, // 7: tfplugin6.DeferredAction.resource_config_unknown:type_name -> tfplugin6.DeferredAction.ConfigUnknown + 29, // 8: tfplugin6.Schema.Block.attributes:type_name -> tfplugin6.Schema.Attribute + 30, // 9: tfplugin6.Schema.Block.block_types:type_name -> tfplugin6.Schema.NestedBlock + 0, // 10: tfplugin6.Schema.Block.description_kind:type_name -> tfplugin6.StringKind + 31, // 11: tfplugin6.Schema.Attribute.nested_type:type_name -> tfplugin6.Schema.Object + 0, // 12: tfplugin6.Schema.Attribute.description_kind:type_name -> tfplugin6.StringKind + 28, // 13: tfplugin6.Schema.NestedBlock.block:type_name -> tfplugin6.Schema.Block + 2, // 14: tfplugin6.Schema.NestedBlock.nesting:type_name -> tfplugin6.Schema.NestedBlock.NestingMode + 29, // 15: tfplugin6.Schema.Object.attributes:type_name -> tfplugin6.Schema.Attribute + 3, // 16: tfplugin6.Schema.Object.nesting:type_name -> tfplugin6.Schema.Object.NestingMode + 6, // 17: tfplugin6.DeferredAction.ConfigUnknown.attribute:type_name -> tfplugin6.AttributePath + 10, // 18: tfplugin6.GetMetadata.Response.server_capabilities:type_name -> tfplugin6.ServerCapabilities + 5, // 19: tfplugin6.GetMetadata.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 36, // 20: tfplugin6.GetMetadata.Response.data_sources:type_name -> tfplugin6.GetMetadata.DataSourceMetadata + 37, // 21: tfplugin6.GetMetadata.Response.resources:type_name -> tfplugin6.GetMetadata.ResourceMetadata + 9, // 22: tfplugin6.GetProviderSchema.Response.provider:type_name -> tfplugin6.Schema + 40, // 23: tfplugin6.GetProviderSchema.Response.resource_schemas:type_name -> tfplugin6.GetProviderSchema.Response.ResourceSchemasEntry + 41, // 24: tfplugin6.GetProviderSchema.Response.data_source_schemas:type_name -> tfplugin6.GetProviderSchema.Response.DataSourceSchemasEntry + 5, // 25: tfplugin6.GetProviderSchema.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 9, // 26: tfplugin6.GetProviderSchema.Response.provider_meta:type_name -> tfplugin6.Schema + 10, // 27: tfplugin6.GetProviderSchema.Response.server_capabilities:type_name -> tfplugin6.ServerCapabilities + 9, // 28: tfplugin6.GetProviderSchema.Response.ResourceSchemasEntry.value:type_name -> tfplugin6.Schema + 9, // 29: tfplugin6.GetProviderSchema.Response.DataSourceSchemasEntry.value:type_name -> tfplugin6.Schema + 4, // 30: tfplugin6.ValidateProviderConfig.Request.config:type_name -> tfplugin6.DynamicValue + 5, // 31: tfplugin6.ValidateProviderConfig.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 8, // 32: tfplugin6.UpgradeResourceState.Request.raw_state:type_name -> tfplugin6.RawState + 4, // 33: tfplugin6.UpgradeResourceState.Response.upgraded_state:type_name -> tfplugin6.DynamicValue + 5, // 34: tfplugin6.UpgradeResourceState.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 11, // 35: tfplugin6.UpgradeResourceState.Response.deferred:type_name -> tfplugin6.DeferredAction + 4, // 36: tfplugin6.ValidateResourceConfig.Request.config:type_name -> tfplugin6.DynamicValue + 5, // 37: tfplugin6.ValidateResourceConfig.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 4, // 38: tfplugin6.ValidateDataResourceConfig.Request.config:type_name -> tfplugin6.DynamicValue + 5, // 39: tfplugin6.ValidateDataResourceConfig.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 4, // 40: tfplugin6.ConfigureProvider.Request.config:type_name -> tfplugin6.DynamicValue + 5, // 41: tfplugin6.ConfigureProvider.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 4, // 42: tfplugin6.ReadResource.Request.current_state:type_name -> tfplugin6.DynamicValue + 4, // 43: tfplugin6.ReadResource.Request.provider_meta:type_name -> tfplugin6.DynamicValue + 4, // 44: tfplugin6.ReadResource.Response.new_state:type_name -> tfplugin6.DynamicValue + 5, // 45: tfplugin6.ReadResource.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 11, // 46: tfplugin6.ReadResource.Response.deferred:type_name -> tfplugin6.DeferredAction + 4, // 47: tfplugin6.PlanResourceChange.Request.prior_state:type_name -> tfplugin6.DynamicValue + 4, // 48: tfplugin6.PlanResourceChange.Request.proposed_new_state:type_name -> tfplugin6.DynamicValue + 4, // 49: tfplugin6.PlanResourceChange.Request.config:type_name -> tfplugin6.DynamicValue + 4, // 50: tfplugin6.PlanResourceChange.Request.provider_meta:type_name -> tfplugin6.DynamicValue + 4, // 51: tfplugin6.PlanResourceChange.Response.planned_state:type_name -> tfplugin6.DynamicValue + 6, // 52: tfplugin6.PlanResourceChange.Response.requires_replace:type_name -> tfplugin6.AttributePath + 5, // 53: tfplugin6.PlanResourceChange.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 11, // 54: tfplugin6.PlanResourceChange.Response.deferred:type_name -> tfplugin6.DeferredAction + 4, // 55: tfplugin6.ApplyResourceChange.Request.prior_state:type_name -> tfplugin6.DynamicValue + 4, // 56: tfplugin6.ApplyResourceChange.Request.planned_state:type_name -> tfplugin6.DynamicValue + 4, // 57: tfplugin6.ApplyResourceChange.Request.config:type_name -> tfplugin6.DynamicValue + 4, // 58: tfplugin6.ApplyResourceChange.Request.provider_meta:type_name -> tfplugin6.DynamicValue + 4, // 59: tfplugin6.ApplyResourceChange.Response.new_state:type_name -> tfplugin6.DynamicValue + 5, // 60: tfplugin6.ApplyResourceChange.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 4, // 61: tfplugin6.ImportResourceState.ImportedResource.state:type_name -> tfplugin6.DynamicValue + 59, // 62: tfplugin6.ImportResourceState.Response.imported_resources:type_name -> tfplugin6.ImportResourceState.ImportedResource + 5, // 63: tfplugin6.ImportResourceState.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 11, // 64: tfplugin6.ImportResourceState.Response.deferred:type_name -> tfplugin6.DeferredAction + 4, // 65: tfplugin6.ReadDataSource.Request.config:type_name -> tfplugin6.DynamicValue + 4, // 66: tfplugin6.ReadDataSource.Request.provider_meta:type_name -> tfplugin6.DynamicValue + 4, // 67: tfplugin6.ReadDataSource.Response.state:type_name -> tfplugin6.DynamicValue + 5, // 68: tfplugin6.ReadDataSource.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 11, // 69: tfplugin6.ReadDataSource.Response.deferred:type_name -> tfplugin6.DeferredAction + 34, // 70: tfplugin6.Provider.GetMetadata:input_type -> tfplugin6.GetMetadata.Request + 38, // 71: tfplugin6.Provider.GetProviderSchema:input_type -> tfplugin6.GetProviderSchema.Request + 42, // 72: tfplugin6.Provider.ValidateProviderConfig:input_type -> tfplugin6.ValidateProviderConfig.Request + 46, // 73: tfplugin6.Provider.ValidateResourceConfig:input_type -> tfplugin6.ValidateResourceConfig.Request + 48, // 74: tfplugin6.Provider.ValidateDataResourceConfig:input_type -> tfplugin6.ValidateDataResourceConfig.Request + 44, // 75: tfplugin6.Provider.UpgradeResourceState:input_type -> tfplugin6.UpgradeResourceState.Request + 50, // 76: tfplugin6.Provider.ConfigureProvider:input_type -> tfplugin6.ConfigureProvider.Request + 52, // 77: tfplugin6.Provider.ReadResource:input_type -> tfplugin6.ReadResource.Request + 54, // 78: tfplugin6.Provider.PlanResourceChange:input_type -> tfplugin6.PlanResourceChange.Request + 56, // 79: tfplugin6.Provider.ApplyResourceChange:input_type -> tfplugin6.ApplyResourceChange.Request + 58, // 80: tfplugin6.Provider.ImportResourceState:input_type -> tfplugin6.ImportResourceState.Request + 61, // 81: tfplugin6.Provider.ReadDataSource:input_type -> tfplugin6.ReadDataSource.Request + 25, // 82: tfplugin6.Provider.StopProvider:input_type -> tfplugin6.StopProvider.Request + 35, // 83: tfplugin6.Provider.GetMetadata:output_type -> tfplugin6.GetMetadata.Response + 39, // 84: tfplugin6.Provider.GetProviderSchema:output_type -> tfplugin6.GetProviderSchema.Response + 43, // 85: tfplugin6.Provider.ValidateProviderConfig:output_type -> tfplugin6.ValidateProviderConfig.Response + 47, // 86: tfplugin6.Provider.ValidateResourceConfig:output_type -> tfplugin6.ValidateResourceConfig.Response + 49, // 87: tfplugin6.Provider.ValidateDataResourceConfig:output_type -> tfplugin6.ValidateDataResourceConfig.Response + 45, // 88: tfplugin6.Provider.UpgradeResourceState:output_type -> tfplugin6.UpgradeResourceState.Response + 51, // 89: tfplugin6.Provider.ConfigureProvider:output_type -> tfplugin6.ConfigureProvider.Response + 53, // 90: tfplugin6.Provider.ReadResource:output_type -> tfplugin6.ReadResource.Response + 55, // 91: tfplugin6.Provider.PlanResourceChange:output_type -> tfplugin6.PlanResourceChange.Response + 57, // 92: tfplugin6.Provider.ApplyResourceChange:output_type -> tfplugin6.ApplyResourceChange.Response + 60, // 93: tfplugin6.Provider.ImportResourceState:output_type -> tfplugin6.ImportResourceState.Response + 62, // 94: tfplugin6.Provider.ReadDataSource:output_type -> tfplugin6.ReadDataSource.Response + 26, // 95: tfplugin6.Provider.StopProvider:output_type -> tfplugin6.StopProvider.Response + 83, // [83:96] is the sub-list for method output_type + 70, // [70:83] is the sub-list for method input_type + 70, // [70:70] is the sub-list for extension type_name + 70, // [70:70] is the sub-list for extension extendee + 0, // [0:70] is the sub-list for field type_name } func init() { file_tfplugin6_proto_init() } @@ -4058,7 +4466,7 @@ func file_tfplugin6_proto_init() { } } file_tfplugin6_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetMetadata); i { + switch v := v.(*DeferredAction); i { case 0: return &v.state case 1: @@ -4070,7 +4478,7 @@ func file_tfplugin6_proto_init() { } } file_tfplugin6_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetProviderSchema); i { + switch v := v.(*GetMetadata); i { case 0: return &v.state case 1: @@ -4082,7 +4490,7 @@ func file_tfplugin6_proto_init() { } } file_tfplugin6_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ValidateProviderConfig); i { + switch v := v.(*GetProviderSchema); i { case 0: return &v.state case 1: @@ -4094,7 +4502,7 @@ func file_tfplugin6_proto_init() { } } file_tfplugin6_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpgradeResourceState); i { + switch v := v.(*ValidateProviderConfig); i { case 0: return &v.state case 1: @@ -4106,7 +4514,7 @@ func file_tfplugin6_proto_init() { } } file_tfplugin6_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ValidateResourceConfig); i { + switch v := v.(*UpgradeResourceState); i { case 0: return &v.state case 1: @@ -4118,7 +4526,7 @@ func file_tfplugin6_proto_init() { } } file_tfplugin6_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ValidateDataResourceConfig); i { + switch v := v.(*ValidateResourceConfig); i { case 0: return &v.state case 1: @@ -4130,7 +4538,7 @@ func file_tfplugin6_proto_init() { } } file_tfplugin6_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ConfigureProvider); i { + switch v := v.(*ValidateDataResourceConfig); i { case 0: return &v.state case 1: @@ -4142,7 +4550,7 @@ func file_tfplugin6_proto_init() { } } file_tfplugin6_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReadResource); i { + switch v := v.(*ConfigureProvider); i { case 0: return &v.state case 1: @@ -4154,7 +4562,7 @@ func file_tfplugin6_proto_init() { } } file_tfplugin6_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PlanResourceChange); i { + switch v := v.(*ReadResource); i { case 0: return &v.state case 1: @@ -4166,7 +4574,7 @@ func file_tfplugin6_proto_init() { } } file_tfplugin6_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ApplyResourceChange); i { + switch v := v.(*PlanResourceChange); i { case 0: return &v.state case 1: @@ -4178,7 +4586,7 @@ func file_tfplugin6_proto_init() { } } file_tfplugin6_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ImportResourceState); i { + switch v := v.(*ApplyResourceChange); i { case 0: return &v.state case 1: @@ -4190,7 +4598,7 @@ func file_tfplugin6_proto_init() { } } file_tfplugin6_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReadDataSource); i { + switch v := v.(*ImportResourceState); i { case 0: return &v.state case 1: @@ -4202,7 +4610,7 @@ func file_tfplugin6_proto_init() { } } file_tfplugin6_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AttributePath_Step); i { + switch v := v.(*ReadDataSource); i { case 0: return &v.state case 1: @@ -4214,7 +4622,7 @@ func file_tfplugin6_proto_init() { } } file_tfplugin6_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StopProvider_Request); i { + switch v := v.(*AttributePath_Step); i { case 0: return &v.state case 1: @@ -4226,6 +4634,18 @@ func file_tfplugin6_proto_init() { } } file_tfplugin6_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StopProvider_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StopProvider_Response); i { case 0: return &v.state @@ -4237,7 +4657,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Schema_Block); i { case 0: return &v.state @@ -4249,7 +4669,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Schema_Attribute); i { case 0: return &v.state @@ -4261,7 +4681,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Schema_NestedBlock); i { case 0: return &v.state @@ -4273,7 +4693,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Schema_Object); i { case 0: return &v.state @@ -4285,7 +4705,31 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeferredAction_OtherReason); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeferredAction_ConfigUnknown); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetMetadata_Request); i { case 0: return &v.state @@ -4297,7 +4741,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetMetadata_Response); i { case 0: return &v.state @@ -4309,7 +4753,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetMetadata_DataSourceMetadata); i { case 0: return &v.state @@ -4321,7 +4765,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetMetadata_ResourceMetadata); i { case 0: return &v.state @@ -4333,7 +4777,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetProviderSchema_Request); i { case 0: return &v.state @@ -4345,7 +4789,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetProviderSchema_Response); i { case 0: return &v.state @@ -4357,7 +4801,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateProviderConfig_Request); i { case 0: return &v.state @@ -4369,7 +4813,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateProviderConfig_Response); i { case 0: return &v.state @@ -4381,7 +4825,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UpgradeResourceState_Request); i { case 0: return &v.state @@ -4393,7 +4837,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UpgradeResourceState_Response); i { case 0: return &v.state @@ -4405,7 +4849,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateResourceConfig_Request); i { case 0: return &v.state @@ -4417,7 +4861,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateResourceConfig_Response); i { case 0: return &v.state @@ -4429,7 +4873,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateDataResourceConfig_Request); i { case 0: return &v.state @@ -4441,7 +4885,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ValidateDataResourceConfig_Response); i { case 0: return &v.state @@ -4453,7 +4897,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ConfigureProvider_Request); i { case 0: return &v.state @@ -4465,7 +4909,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ConfigureProvider_Response); i { case 0: return &v.state @@ -4477,7 +4921,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReadResource_Request); i { case 0: return &v.state @@ -4489,7 +4933,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReadResource_Response); i { case 0: return &v.state @@ -4501,7 +4945,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PlanResourceChange_Request); i { case 0: return &v.state @@ -4513,7 +4957,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PlanResourceChange_Response); i { case 0: return &v.state @@ -4525,7 +4969,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ApplyResourceChange_Request); i { case 0: return &v.state @@ -4537,7 +4981,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ApplyResourceChange_Response); i { case 0: return &v.state @@ -4549,7 +4993,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ImportResourceState_Request); i { case 0: return &v.state @@ -4561,7 +5005,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ImportResourceState_ImportedResource); i { case 0: return &v.state @@ -4573,7 +5017,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ImportResourceState_Response); i { case 0: return &v.state @@ -4585,7 +5029,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReadDataSource_Request); i { case 0: return &v.state @@ -4597,7 +5041,7 @@ func file_tfplugin6_proto_init() { return nil } } - file_tfplugin6_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + file_tfplugin6_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ReadDataSource_Response); i { case 0: return &v.state @@ -4610,7 +5054,12 @@ func file_tfplugin6_proto_init() { } } } - file_tfplugin6_proto_msgTypes[19].OneofWrappers = []interface{}{ + file_tfplugin6_proto_msgTypes[7].OneofWrappers = []interface{}{ + (*DeferredAction_OtherReason_)(nil), + (*DeferredAction_ProviderConfigUnknown)(nil), + (*DeferredAction_ResourceConfigUnknown)(nil), + } + file_tfplugin6_proto_msgTypes[20].OneofWrappers = []interface{}{ (*AttributePath_Step_AttributeName)(nil), (*AttributePath_Step_ElementKeyString)(nil), (*AttributePath_Step_ElementKeyInt)(nil), @@ -4621,7 +5070,7 @@ func file_tfplugin6_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_tfplugin6_proto_rawDesc, NumEnums: 4, - NumMessages: 56, + NumMessages: 59, NumExtensions: 0, NumServices: 1, }, diff --git a/internal/tfplugin6/tfplugin6.proto b/internal/tfplugin6/tfplugin6.proto index a5f8d0903f8a..cb60ee615ab4 120000 --- a/internal/tfplugin6/tfplugin6.proto +++ b/internal/tfplugin6/tfplugin6.proto @@ -1 +1 @@ -../../docs/plugin-protocol/tfplugin6.4.proto \ No newline at end of file +../../docs/plugin-protocol/tfplugin6.5.proto \ No newline at end of file From b5296a44e786c410ab34747e7bc4561d07129950 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Thu, 9 Feb 2023 15:53:52 -0800 Subject: [PATCH 02/17] addrs: Deferrable address types This represents the two address types that could potentially have deferred actions associated with them during a Terraform plan operation, because deferring can happen either before or after instance expansion. --- internal/addrs/deferrable.go | 64 ++++++++++++++++++++++ internal/addrs/deferrable_test.go | 88 +++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 internal/addrs/deferrable.go create mode 100644 internal/addrs/deferrable_test.go diff --git a/internal/addrs/deferrable.go b/internal/addrs/deferrable.go new file mode 100644 index 000000000000..680fd2fc69a3 --- /dev/null +++ b/internal/addrs/deferrable.go @@ -0,0 +1,64 @@ +package addrs + +import ( + "strings" +) + +// Deferable represents addresses of types of objects that can have actions +// that might be deferred for execution in a later run. +// +// When an action is defered we still need to describe an approximation of +// the effects of that action so that users can get early feedback about +// whether applying the non-deferred changes will move them closer to their +// desired state. Deferable addresses are how we communicate which object +// each deferred action relates to. +type Deferrable interface { + deferrableSigil() + UniqueKeyer + + // DeferrableString is like String but returns an address form specifically + // tailored for a UI that is describing deferred changes, which clearly + // distinguishes between the different possible deferable address types. + DeferrableString() string +} + +// ConfigResource is deferable because sometimes we must defer even the +// expansion of a resource due to either its own repetition argument or that +// of one of its containing modules being unknown. +func (ConfigResource) deferrableSigil() {} + +func (r ConfigResource) DeferrableString() string { + // Because deferred unexpanded resources will be shown in the same context + // as expanded resource instances, we'll use a special format here to + // make it explicit that we're talking about all instances of a particular + // resource or module, rather than the _unkeyed instance_ of each. + // This follows a similar convention to how we display "move endpoints" + // in the UI, like [MoveEndpointInModule.String]: + // module.foo[*].aws_instance.bar[*], to differentiate from + // module.foo.aws_instance.bar the single instance. + var buf strings.Builder + for _, name := range r.Module { + buf.WriteString("module.") + buf.WriteString(name) + buf.WriteString("[*].") + } + buf.WriteString(r.Resource.String()) + buf.WriteString("[*]") + return buf.String() +} + +var _ Deferrable = ConfigResource{} + +// AbsResourceInstance is deferable for situations where we have already +// succeeded in expanding a resource but one of its instances must be +// defered either for provider-specific reasons or because it is downstream +// of some other deferred action. +func (AbsResourceInstance) deferrableSigil() {} + +var _ Deferrable = AbsResourceInstance{} + +func (r AbsResourceInstance) DeferrableString() string { + // Our "deferrable" string format for AbsResourceInstance is just its + // normal string format, because this is the main case. + return r.String() +} diff --git a/internal/addrs/deferrable_test.go b/internal/addrs/deferrable_test.go new file mode 100644 index 000000000000..679ddc85dcca --- /dev/null +++ b/internal/addrs/deferrable_test.go @@ -0,0 +1,88 @@ +package addrs + +import ( + "fmt" + "testing" +) + +func TestDeferrableString(t *testing.T) { + tests := []struct { + Addr Deferrable + Want string + }{ + { + Resource{ + Mode: ManagedResourceMode, + Type: "foo", + Name: "bar", + }.Instance(NoKey).Absolute(RootModuleInstance), + `foo.bar`, + }, + { + Resource{ + Mode: ManagedResourceMode, + Type: "foo", + Name: "bar", + }.Instance(IntKey(2)).Absolute(RootModuleInstance), + `foo.bar[2]`, + }, + { + Resource{ + Mode: ManagedResourceMode, + Type: "foo", + Name: "bar", + }.Instance(StringKey("blub")).Absolute(RootModuleInstance), + `foo.bar["blub"]`, + }, + { + Resource{ + Mode: ManagedResourceMode, + Type: "foo", + Name: "bar", + }.Instance(NoKey).Absolute(RootModuleInstance.Child("boop", NoKey)), + `module.boop.foo.bar`, + }, + { + Resource{ + Mode: ManagedResourceMode, + Type: "foo", + Name: "bar", + }.Instance(NoKey).Absolute(RootModuleInstance.Child("boop", IntKey(6))), + `module.boop[6].foo.bar`, + }, + { + Resource{ + Mode: ManagedResourceMode, + Type: "foo", + Name: "bar", + }.Instance(NoKey).Absolute(RootModuleInstance.Child("boop", StringKey("a"))), + `module.boop["a"].foo.bar`, + }, + { + Resource{ + Mode: ManagedResourceMode, + Type: "foo", + Name: "bar", + }.InModule(RootModule), + `foo.bar[*]`, + }, + { + Resource{ + Mode: ManagedResourceMode, + Type: "foo", + Name: "bar", + }.InModule(RootModule.Child("boop")), + `module.boop[*].foo.bar[*]`, + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("%#v", test.Addr), func(t *testing.T) { + got := test.Addr.DeferrableString() + + if got != test.Want { + t.Errorf("wrong result\ngot: %s\nwant: %s", got, test.Want) + } + }) + } +} From 0c595bf6b34bc22eac2cb90cdb73a188e27f7062 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Tue, 28 Feb 2023 16:25:32 -0800 Subject: [PATCH 03/17] plans/deferring: Some helpers to track deferred actions --- internal/plans/deferring/deferred.go | 109 +++++++++++++++++++++++++++ internal/plans/deferring/doc.go | 4 + internal/plans/deferring/reason.go | 54 +++++++++++++ 3 files changed, 167 insertions(+) create mode 100644 internal/plans/deferring/deferred.go create mode 100644 internal/plans/deferring/doc.go create mode 100644 internal/plans/deferring/reason.go diff --git a/internal/plans/deferring/deferred.go b/internal/plans/deferring/deferred.go new file mode 100644 index 000000000000..e71d7111a283 --- /dev/null +++ b/internal/plans/deferring/deferred.go @@ -0,0 +1,109 @@ +package deferring + +import ( + "sync" + + "github.com/hashicorp/terraform/internal/addrs" +) + +// Deferred is the main type tracking which objects have already been deferred +// and what other objects might have their planning deferred as a result. +type Deferred struct { + // See the description of the resourceDeps argument of [NewDeferred]. + resourceDeps addrs.DirectedGraph[addrs.ConfigResource] + + // deferred is the set of addresses of all resources that either had their + // expansion deferred or whose provider requested deferral during of a + // specific instance during planning. The detail about exactly which + // instances were deferred is tracked in the plan, instead of in this + // data structure. + deferredResources addrs.Set[addrs.ConfigResource] + + // forceAllDeferred is a special case for when Terraform Core is planning + // a configuration that is downstream of some other configuration that + // included deferrals, which means that nothing at all in this configuration + // can be definitively planned yet. + forceAllDeferred bool + + mu sync.Mutex +} + +// NewDeferred allocates and returns a new [Deferred] which can track the +// deferral statuses for objects during Terraform Core evaluation and answer +// questions about whether downstream objects ought to be deferred as a result +// of existing deferrals. +// +// resourceDeps describes the dependency graph of unexpanded resources, +// which we need to decide whether a particular resource ought to be +// deferred as a result of one of its upstreams being deferred. +// This graph must always describe the same effective dependences as the +// main execution graph in Terraform Core, so that questions about +// downstream deferrals will arrive in the correct order for their +// information to be available. +// +// The caller must not modify anything reachable through the given arguments +// after passing them to this function. +func NewDeferred(resourceDeps addrs.DirectedGraph[addrs.ConfigResource]) *Deferred { + return &Deferred{ + resourceDeps: resourceDeps, + deferredResources: addrs.MakeSet[addrs.ConfigResource](), + } +} + +// ForceAllDeferred makes all future queries about whether an object should be +// deferred indicate that it _should_ be deferred. +// +// This should typically be called before beginning the Terraform Core graph +// walk to represent the special situation where we're planning a configuration +// that depends on some other configuration which itself had deferred actions, +// and therefore all of the actions we plan must be deferred. +func (d *Deferred) ForceAllDeferred() { + d.mu.Lock() + d.forceAllDeferred = true + d.mu.Unlock() +} + +// ReportResourceDeferred records that a particular resource has at least one +// deferred instance, or that its entire expansion was deferred. +// +// Terraform Core must call this for any resource that it generates deferred +// actions for, even if that deferral was forced by one of the "Should" +// functions on this same object, because our analyses typically consider only +// direct dependencies on the assumption that Terraform Core is going to visit +// everything in order anyway. +func (d *Deferred) ReportResourceDeferred(addr addrs.ConfigResource) { + // NOTE: We're currently only tracking deferral for entire ConfigResource + // addresses, which means that we don't track which module instances each + // deferral belongs to and so this analysis will be very conservative when + // considering deferrals inside multi-instance modules. + // + // We might improve the accuracy of this later, but we'll wait to see if + // that's justified based on experience with real-world usage. Even this + // imprecise analysis is presumably better than failing outright whenever + // unknown values appear in an unfortunate place. + + d.mu.Lock() + d.deferredResources.Add(addr) + d.mu.Unlock() +} + +func (d *Deferred) ShouldDeferResourceInstanceAction(addr addrs.AbsResourceInstance) bool { + d.mu.Lock() + defer d.mu.Unlock() + + if d.forceAllDeferred { + return true + } + + // We use DirectDependenciesOf because we expect Terraform Core to visit + // all of the resources in dependency order and to have already called + // ReportResourceDeferred for any direct upstreams that were deferred for + // any reason. + deps := d.resourceDeps.DirectDependenciesOf(addr.ConfigResource()) + for _, depAddr := range deps { + if d.deferredResources.Has(depAddr) { + return true + } + } + return false +} diff --git a/internal/plans/deferring/doc.go b/internal/plans/deferring/doc.go new file mode 100644 index 000000000000..4416c687ee3a --- /dev/null +++ b/internal/plans/deferring/doc.go @@ -0,0 +1,4 @@ +// Package deferring has data structures that aim to encapsulate the details +// of how the full planning of certain actions might be deferred until a future +// plan where there's more information available. +package deferring diff --git a/internal/plans/deferring/reason.go b/internal/plans/deferring/reason.go new file mode 100644 index 000000000000..c0d52a07caff --- /dev/null +++ b/internal/plans/deferring/reason.go @@ -0,0 +1,54 @@ +package deferring + +import ( + "github.com/zclconf/go-cty/cty" +) + +// Reason is an enumeration of possible reasons for deferring. +type Reason rune + +//go:generate go run golang.org/x/tools/cmd/stringer -type=Reason + +const ( + // BecauseUpstream means that an action was deferred because it depends + // on the result of some other action that was deferred. + // + // In this case the proposed action should typically be complete in itself + // but must be deferred anyway to preserve the correct order of operations + // in relation to some other deferred action. + BecauseUpstream = '↰' + + // BecauseExpansionUnknown means that an action was deferred because + // Terraform can't yet calculate the full set of instances of the object + // whose action is being described. + BecauseExpansionUnknown = '#' + + // BecauseProviderConfigUnknown means that a resource instance action was + // deferred because the configuration for the provider instance that would + // be performing the action is not yet sufficiently known to produce a + // complete plan. + BecauseProviderConfigUnknown = 'P' + + // BecauseResourceInstanceConfigUnknown means that a resource instance + // action was deferred because the configuration for that resource instance + // is not yet sufficiently known to produce a complete plan. + BecauseResourceInstanceConfigUnknown = 'R' +) + +// Explanation combines a deferral reason with other reason-specific +// information. +type Explanation struct { + // Reason describes both the deferral reason and how to interpret the + // other fields of this type. + Reason Reason + + // ArgPath describes an attribute path to an argument in an object that + // specifically caused the deferral. + // + // - For [BecauseProviderConfigUnknown] this is a path into the provider + // configuration block that the relevant resource is associated with. + // - For [BecauseResourceInstanceConfigUnknown] this is a path into the + // configuration of the relevant resource instance. + // - For all other reasons this is always nil. + ArgPath cty.Path +} From c7d76f09d5a6ea6f076d158811857e819c5825ae Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 8 Mar 2023 16:18:48 -0800 Subject: [PATCH 04/17] core: Register unknown count and for_each with instance expander Previously we just immediately bailed out with an error if either count or for_each were not sufficiently known to determine their full set of instance keys. The Expander abstraction can now talk about module calls and resources having unknown expansion, so Terraform Core should tolerate that situation and just let the expander know that the expansion is unknown, and then we'll deal with that situation downstream. For now "downstream" actually means directly after these functions return, because the rest of Terraform Core isn't yet ready to deal with objects that don't know their full expansions. We'll just return errors similar to (but slightly lower quality than) the ones we used to return during evaluation, as a temporary placeholder to keep things working until we get downstream more ready to deal with this. While working on this I also noticed that we were redundantly re-evaluating the for_each expressions for each resource instance just to prepare the repetition data, which is unnecessary because the Expander abstraction already keeps track of that to ensure that all of the graph nodes have a consistent view of the expansions. We'll now just ask the expander directly what our RepetitionData should be, since that's part of the expander's responsibility. --- internal/terraform/eval_count.go | 29 ++------ internal/terraform/eval_count_test.go | 13 +++- internal/terraform/eval_for_each.go | 66 +++++-------------- internal/terraform/eval_for_each_test.go | 59 +++++++++-------- internal/terraform/node_module_expand.go | 46 +++++++++++-- internal/terraform/node_resource_abstract.go | 44 +++++++++++-- .../node_resource_abstract_instance.go | 20 +++--- .../terraform/node_resource_plan_instance.go | 9 +-- 8 files changed, 161 insertions(+), 125 deletions(-) diff --git a/internal/terraform/eval_count.go b/internal/terraform/eval_count.go index 9e2144d9e0f6..be16328ba100 100644 --- a/internal/terraform/eval_count.go +++ b/internal/terraform/eval_count.go @@ -20,35 +20,18 @@ import ( // evaluateCountExpression differs from evaluateCountExpressionValue by // returning an error if the count value is not known, and converting the // cty.Value to an integer. -func evaluateCountExpression(expr hcl.Expression, ctx EvalContext) (int, tfdiags.Diagnostics) { +func evaluateCountExpression(expr hcl.Expression, ctx EvalContext) (v int, known bool, diags tfdiags.Diagnostics) { countVal, diags := evaluateCountExpressionValue(expr, ctx) - if !countVal.IsKnown() { - // Currently this is a rather bad outcome from a UX standpoint, since we have - // no real mechanism to deal with this situation and all we can do is produce - // an error message. - // FIXME: In future, implement a built-in mechanism for deferring changes that - // can't yet be predicted, and use it to guide the user through several - // plan/apply steps until the desired configuration is eventually reached. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid count argument", - Detail: `The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the count depends on.`, - Subject: expr.Range().Ptr(), - // TODO: Also populate Expression and EvalContext in here, but - // we can't easily do that right now because the hcl.EvalContext - // (which is not the same as the ctx we have in scope here) is - // hidden away inside evaluateCountExpressionValue. - Extra: diagnosticCausedByUnknown(true), - }) + if !countVal.IsKnown() { + return -1, false, diags } - - if countVal.IsNull() || !countVal.IsKnown() { - return -1, diags + if countVal.IsNull() { + return -1, true, diags } count, _ := countVal.AsBigFloat().Int64() - return int(count), diags + return int(count), true, diags } // evaluateCountExpressionValue is like evaluateCountExpression diff --git a/internal/terraform/eval_count_test.go b/internal/terraform/eval_count_test.go index 74bcfbdf721d..8c97ebf33691 100644 --- a/internal/terraform/eval_count_test.go +++ b/internal/terraform/eval_count_test.go @@ -18,21 +18,29 @@ func TestEvaluateCountExpression(t *testing.T) { tests := map[string]struct { Expr hcl.Expression Count int + Known bool }{ "zero": { hcltest.MockExprLiteral(cty.NumberIntVal(0)), 0, + true, }, "expression with marked value": { hcltest.MockExprLiteral(cty.NumberIntVal(8).Mark(marks.Sensitive)), 8, + true, + }, + "unknown value": { + hcltest.MockExprLiteral(cty.UnknownVal(cty.Number)), + -1, + false, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { ctx := &MockEvalContext{} ctx.installSimpleEval() - countVal, diags := evaluateCountExpression(test.Expr, ctx) + countVal, known, diags := evaluateCountExpression(test.Expr, ctx) if len(diags) != 0 { t.Errorf("unexpected diagnostics %s", spew.Sdump(diags)) @@ -44,6 +52,9 @@ func TestEvaluateCountExpression(t *testing.T) { spew.Sdump(countVal), spew.Sdump(test.Count), ) } + if known != test.Known { + t.Errorf("wrong 'knownness'\ngot: %#v\nwant: %#v", known, test.Known) + } }) } } diff --git a/internal/terraform/eval_for_each.go b/internal/terraform/eval_for_each.go index 3782234e9b0e..0e31179ee95b 100644 --- a/internal/terraform/eval_for_each.go +++ b/internal/terraform/eval_for_each.go @@ -19,7 +19,7 @@ import ( // evaluateForEachExpression differs from evaluateForEachExpressionValue by // returning an error if the count value is not known, and converting the // cty.Value to a map[string]cty.Value for compatibility with other calls. -func evaluateForEachExpression(expr hcl.Expression, ctx EvalContext) (forEach map[string]cty.Value, diags tfdiags.Diagnostics) { +func evaluateForEachExpression(expr hcl.Expression, ctx EvalContext) (forEach map[string]cty.Value, known bool, diags tfdiags.Diagnostics) { return newForEachEvaluator(expr, ctx).ResourceValue() } @@ -54,38 +54,36 @@ type forEachEvaluator struct { // ResourceForEachValue returns a known for_each map[string]cty.Value // appropriate for use within resource expansion. -func (ev *forEachEvaluator) ResourceValue() (map[string]cty.Value, tfdiags.Diagnostics) { +func (ev *forEachEvaluator) ResourceValue() (map[string]cty.Value, bool, tfdiags.Diagnostics) { res := map[string]cty.Value{} // no expression always results in an empty map if ev.expr == nil { - return res, nil + return res, true, nil } forEachVal, diags := ev.Value() if diags.HasErrors() { - return res, diags + return res, true, diags } - // ensure our value is known for use in resource expansion - diags = diags.Append(ev.ensureKnownForResource(forEachVal)) - if diags.HasErrors() { - return res, diags + if !ev.isKnownForResource(forEachVal) { + return nil, false, diags } // validate the for_each value for use in resource expansion diags = diags.Append(ev.validateResource(forEachVal)) if diags.HasErrors() { - return res, diags + return res, true, diags } if forEachVal.IsNull() || !forEachVal.IsKnown() || markSafeLengthInt(forEachVal) == 0 { // we check length, because an empty set returns a nil map which will panic below - return res, diags + return res, true, diags } res = forEachVal.AsValueMap() - return res, diags + return res, true, diags } // ImportValue returns the for_each map for use within an import block, @@ -176,47 +174,19 @@ func (ev *forEachEvaluator) ensureKnownForImport(forEachVal cty.Value) tfdiags.D return diags } -// ensureKnownForResource checks that the value is known within the rules of +// isKnownForResource checks that the value is known within the rules of // resource and module expansion. -func (ev *forEachEvaluator) ensureKnownForResource(forEachVal cty.Value) tfdiags.Diagnostics { - var diags tfdiags.Diagnostics +func (ev *forEachEvaluator) isKnownForResource(forEachVal cty.Value) bool { ty := forEachVal.Type() - const errInvalidUnknownDetailMap = "The \"for_each\" map includes keys derived from resource attributes that cannot be determined until apply, and so Terraform cannot determine the full set of keys that will identify the instances of this resource.\n\nWhen working with unknown values in for_each, it's better to define the map keys statically in your configuration and place apply-time results only in the map values.\n\nAlternatively, you could use the -target planning option to first apply only the resources that the for_each value depends on, and then apply a second time to fully converge." - const errInvalidUnknownDetailSet = "The \"for_each\" set includes values derived from resource attributes that cannot be determined until apply, and so Terraform cannot determine the full set of keys that will identify the instances of this resource.\n\nWhen working with unknown values in for_each, it's better to use a map value where the keys are defined statically in your configuration and where only the values contain apply-time results.\n\nAlternatively, you could use the -target planning option to first apply only the resources that the for_each value depends on, and then apply a second time to fully converge." - - if !forEachVal.IsKnown() { - var detailMsg string - switch { - case ty.IsSetType(): - detailMsg = errInvalidUnknownDetailSet - default: - detailMsg = errInvalidUnknownDetailMap - } - - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid for_each argument", - Detail: detailMsg, - Subject: ev.expr.Range().Ptr(), - Expression: ev.expr, - EvalContext: ev.hclCtx, - Extra: diagnosticCausedByUnknown(true), - }) - return diags - } - if ty.IsSetType() && !forEachVal.IsWhollyKnown() { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid for_each argument", - Detail: errInvalidUnknownDetailSet, - Subject: ev.expr.Range().Ptr(), - Expression: ev.expr, - EvalContext: ev.hclCtx, - Extra: diagnosticCausedByUnknown(true), - }) + switch { + case !forEachVal.IsKnown(): + return false + case ty.IsSetType() && !forEachVal.IsWhollyKnown(): + return false + default: + return true } - return diags } // ValidateResourceValue is used from validation walks to verify the validity diff --git a/internal/terraform/eval_for_each_test.go b/internal/terraform/eval_for_each_test.go index 3f7717d09015..1c58c0b2e015 100644 --- a/internal/terraform/eval_for_each_test.go +++ b/internal/terraform/eval_for_each_test.go @@ -20,10 +20,12 @@ func TestEvaluateForEachExpression_valid(t *testing.T) { tests := map[string]struct { Expr hcl.Expression ForEachMap map[string]cty.Value + Known bool }{ "empty set": { hcltest.MockExprLiteral(cty.SetValEmpty(cty.String)), map[string]cty.Value{}, + true, }, "multi-value string set": { hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.StringVal("a"), cty.StringVal("b")})), @@ -31,10 +33,12 @@ func TestEvaluateForEachExpression_valid(t *testing.T) { "a": cty.StringVal("a"), "b": cty.StringVal("b"), }, + true, }, "empty map": { hcltest.MockExprLiteral(cty.MapValEmpty(cty.Bool)), map[string]cty.Value{}, + true, }, "map": { hcltest.MockExprLiteral(cty.MapVal(map[string]cty.Value{ @@ -45,6 +49,7 @@ func TestEvaluateForEachExpression_valid(t *testing.T) { "a": cty.BoolVal(true), "b": cty.BoolVal(false), }, + true, }, "map containing unknown values": { hcltest.MockExprLiteral(cty.MapVal(map[string]cty.Value{ @@ -55,6 +60,7 @@ func TestEvaluateForEachExpression_valid(t *testing.T) { "a": cty.UnknownVal(cty.Bool), "b": cty.UnknownVal(cty.Bool), }, + true, }, "map containing sensitive values, but strings are literal": { hcltest.MockExprLiteral(cty.MapVal(map[string]cty.Value{ @@ -65,6 +71,27 @@ func TestEvaluateForEachExpression_valid(t *testing.T) { "a": cty.BoolVal(true).Mark(marks.Sensitive), "b": cty.BoolVal(false), }, + true, + }, + "unknown string set": { + hcltest.MockExprLiteral(cty.UnknownVal(cty.Set(cty.String))), + nil, + false, + }, + "unknown map": { + hcltest.MockExprLiteral(cty.UnknownVal(cty.Map(cty.Bool))), + nil, + false, + }, + "set containing unknown value": { + hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.UnknownVal(cty.String)})), + nil, + false, + }, + "set containing dynamic unknown value": { + hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.UnknownVal(cty.DynamicPseudoType)})), + nil, + false, }, } @@ -72,7 +99,7 @@ func TestEvaluateForEachExpression_valid(t *testing.T) { t.Run(name, func(t *testing.T) { ctx := &MockEvalContext{} ctx.installSimpleEval() - forEachMap, diags := evaluateForEachExpression(test.Expr, ctx) + forEachMap, known, diags := evaluateForEachExpression(test.Expr, ctx) if len(diags) != 0 { t.Errorf("unexpected diagnostics %s", spew.Sdump(diags)) @@ -84,7 +111,9 @@ func TestEvaluateForEachExpression_valid(t *testing.T) { spew.Sdump(forEachMap), spew.Sdump(test.ForEachMap), ) } - + if known != test.Known { + t.Errorf("wrong 'knownness'\ngot: %#v\nwant: %#v", known, test.Known) + } }) } } @@ -119,18 +148,6 @@ func TestEvaluateForEachExpression_errors(t *testing.T) { "must be a map, or set of strings, and you have provided a value of type tuple", false, false, }, - "unknown string set": { - hcltest.MockExprLiteral(cty.UnknownVal(cty.Set(cty.String))), - "Invalid for_each argument", - "set includes values derived from resource attributes that cannot be determined until apply", - true, false, - }, - "unknown map": { - hcltest.MockExprLiteral(cty.UnknownVal(cty.Map(cty.Bool))), - "Invalid for_each argument", - "map includes keys derived from resource attributes that cannot be determined until apply", - true, false, - }, "marked map": { hcltest.MockExprLiteral(cty.MapVal(map[string]cty.Value{ "a": cty.BoolVal(true), @@ -152,18 +169,6 @@ func TestEvaluateForEachExpression_errors(t *testing.T) { "must not contain null values", false, false, }, - "set containing unknown value": { - hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.UnknownVal(cty.String)})), - "Invalid for_each argument", - "set includes values derived from resource attributes that cannot be determined until apply", - true, false, - }, - "set containing dynamic unknown value": { - hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.UnknownVal(cty.DynamicPseudoType)})), - "Invalid for_each argument", - "set includes values derived from resource attributes that cannot be determined until apply", - true, false, - }, "set containing marked values": { hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.StringVal("beep").Mark(marks.Sensitive), cty.StringVal("boop")})), "Invalid for_each argument", @@ -176,7 +181,7 @@ func TestEvaluateForEachExpression_errors(t *testing.T) { t.Run(name, func(t *testing.T) { ctx := &MockEvalContext{} ctx.installSimpleEval() - _, diags := evaluateForEachExpression(test.Expr, ctx) + _, _, diags := evaluateForEachExpression(test.Expr, ctx) if len(diags) != 1 { t.Fatalf("got %d diagnostics; want 1", len(diags)) diff --git a/internal/terraform/node_module_expand.go b/internal/terraform/node_module_expand.go index 9fc0495c9554..0e3a447dbc31 100644 --- a/internal/terraform/node_module_expand.go +++ b/internal/terraform/node_module_expand.go @@ -6,6 +6,8 @@ package terraform import ( "log" + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/dag" @@ -113,20 +115,54 @@ func (n *nodeExpandModule) Execute(ctx EvalContext, op walkOperation) (diags tfd ctx = ctx.WithPath(module) switch { case n.ModuleCall.Count != nil: - count, ctDiags := evaluateCountExpression(n.ModuleCall.Count, ctx) + count, known, ctDiags := evaluateCountExpression(n.ModuleCall.Count, ctx) diags = diags.Append(ctDiags) if diags.HasErrors() { return diags } - expander.SetModuleCount(module, call, count) + if known { + expander.SetModuleCount(module, call, count) + } else { + // TEMP: The rest of Terraform Core isn't ready to deal with + // us marking expansion as unknown yet, so for now we'll preserve + // this as a similar error to what evaluateCountExpression used to + // return for an unknown count, thereby preventing downstream + // code from relying on this until we've made sure everything else + // is ready to deal with it. + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid count argument", + Detail: `The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the count depends on.`, + Subject: n.ModuleCall.Count.Range().Ptr(), + Extra: diagnosticCausedByUnknown(true), + }) + expander.SetModuleCountUnknown(module, call) + } case n.ModuleCall.ForEach != nil: - forEach, feDiags := evaluateForEachExpression(n.ModuleCall.ForEach, ctx) + forEach, known, feDiags := evaluateForEachExpression(n.ModuleCall.ForEach, ctx) diags = diags.Append(feDiags) if diags.HasErrors() { return diags } - expander.SetModuleForEach(module, call, forEach) + if known { + expander.SetModuleForEach(module, call, forEach) + } else { + // TEMP: The rest of Terraform Core isn't ready to deal with + // us marking expansion as unknown yet, so for now we'll preserve + // this as a similar error to what evaluateCountExpression used to + // return for an unknown count, thereby preventing downstream + // code from relying on this until we've made sure everything else + // is ready to deal with it. + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid for_each argument", + Detail: `The "for_each" value's instance keys depend on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that for_each depends on.`, + Subject: n.ModuleCall.ForEach.Range().Ptr(), + Extra: diagnosticCausedByUnknown(true), + }) + expander.SetModuleForEachUnknown(module, call) + } default: expander.SetModuleSingle(module, call) @@ -248,8 +284,6 @@ func (n *nodeValidateModule) Execute(ctx EvalContext, op walkOperation) (diags t ctx = ctx.WithPath(module) // Validate our for_each and count expressions at a basic level - // We skip validation on known, because there will be unknown values before - // a full expansion, presuming these errors will be caught in later steps switch { case n.ModuleCall.Count != nil: _, countDiags := evaluateCountExpressionValue(n.ModuleCall.Count, ctx) diff --git a/internal/terraform/node_resource_abstract.go b/internal/terraform/node_resource_abstract.go index cbf4f1d16e40..2751bb46e296 100644 --- a/internal/terraform/node_resource_abstract.go +++ b/internal/terraform/node_resource_abstract.go @@ -7,6 +7,8 @@ import ( "fmt" "log" + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/configs/configschema" @@ -404,17 +406,34 @@ func (n *NodeAbstractResource) writeResourceState(ctx EvalContext, addr addrs.Ab switch { case n.Config != nil && n.Config.Count != nil: - count, countDiags := evaluateCountExpression(n.Config.Count, ctx) + count, known, countDiags := evaluateCountExpression(n.Config.Count, ctx) diags = diags.Append(countDiags) if countDiags.HasErrors() { return diags } state.SetResourceProvider(addr, n.ResolvedProvider) - expander.SetResourceCount(addr.Module, n.Addr.Resource, count) + if known { + expander.SetResourceCount(addr.Module, n.Addr.Resource, count) + } else { + // TEMP: The rest of Terraform Core isn't ready to deal with + // us marking expansion as unknown yet, so for now we'll preserve + // this as a similar error to what evaluateCountExpression used to + // return for an unknown count, thereby preventing downstream + // code from relying on this until we've made sure everything else + // is ready to deal with it. + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid count argument", + Detail: `The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the count depends on.`, + Subject: n.Config.Count.Range().Ptr(), + Extra: diagnosticCausedByUnknown(true), + }) + expander.SetResourceCountUnknown(addr.Module, n.Addr.Resource) + } case n.Config != nil && n.Config.ForEach != nil: - forEach, forEachDiags := evaluateForEachExpression(n.Config.ForEach, ctx) + forEach, known, forEachDiags := evaluateForEachExpression(n.Config.ForEach, ctx) diags = diags.Append(forEachDiags) if forEachDiags.HasErrors() { return diags @@ -423,7 +442,24 @@ func (n *NodeAbstractResource) writeResourceState(ctx EvalContext, addr addrs.Ab // This method takes care of all of the business logic of updating this // while ensuring that any existing instances are preserved, etc. state.SetResourceProvider(addr, n.ResolvedProvider) - expander.SetResourceForEach(addr.Module, n.Addr.Resource, forEach) + if known { + expander.SetResourceForEach(addr.Module, n.Addr.Resource, forEach) + } else { + // TEMP: The rest of Terraform Core isn't ready to deal with + // us marking expansion as unknown yet, so for now we'll preserve + // this as a similar error to what evaluateForEachExpression used to + // return for an unknown for_each, thereby preventing downstream + // code from relying on this until we've made sure everything else + // is ready to deal with it. + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid for_each argument", + Detail: `The "for_each" value's instance keys depend on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that for_each depends on.`, + Subject: n.Config.ForEach.Range().Ptr(), + Extra: diagnosticCausedByUnknown(true), + }) + expander.SetResourceForEachUnknown(addr.Module, n.Addr.Resource) + } default: state.SetResourceProvider(addr, n.ResolvedProvider) diff --git a/internal/terraform/node_resource_abstract_instance.go b/internal/terraform/node_resource_abstract_instance.go index 056aa69a196a..7b93e91b47cb 100644 --- a/internal/terraform/node_resource_abstract_instance.go +++ b/internal/terraform/node_resource_abstract_instance.go @@ -773,10 +773,8 @@ func (n *NodeAbstractResourceInstance) plan( createBeforeDestroy = plannedChange.Action == plans.CreateThenDelete } - // Evaluate the configuration - forEach, _ := evaluateForEachExpression(n.Config.ForEach, ctx) - - keyData = EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, forEach) + exp := ctx.InstanceExpander() + keyData = exp.GetResourceInstanceRepetitionData(n.Addr) checkDiags := evalCheckRules( addrs.ResourcePrecondition, @@ -1730,8 +1728,8 @@ func (n *NodeAbstractResourceInstance) planDataSource(ctx EvalContext, checkRule objTy := schema.ImpliedType() priorVal := cty.NullVal(objTy) - forEach, _ := evaluateForEachExpression(config.ForEach, ctx) - keyData = EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, forEach) + exp := ctx.InstanceExpander() + keyData = exp.GetResourceInstanceRepetitionData(n.Addr) checkDiags := evalCheckRules( addrs.ResourcePrecondition, @@ -2010,8 +2008,8 @@ func (n *NodeAbstractResourceInstance) applyDataSource(ctx EvalContext, planned return nil, keyData, diags } - forEach, _ := evaluateForEachExpression(config.ForEach, ctx) - keyData = EvalDataForInstanceKey(n.Addr.Resource.Key, forEach) + exp := ctx.InstanceExpander() + keyData = exp.GetResourceInstanceRepetitionData(n.Addr) checkDiags := evalCheckRules( addrs.ResourcePrecondition, @@ -2314,10 +2312,8 @@ func (n *NodeAbstractResourceInstance) applyProvisioners(ctx EvalContext, state func (n *NodeAbstractResourceInstance) evalProvisionerConfig(ctx EvalContext, body hcl.Body, self cty.Value, schema *configschema.Block) (cty.Value, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics - forEach, forEachDiags := evaluateForEachExpression(n.Config.ForEach, ctx) - diags = diags.Append(forEachDiags) - - keyData := EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, forEach) + exp := ctx.InstanceExpander() + keyData := exp.GetResourceInstanceRepetitionData(n.Addr) config, _, configDiags := ctx.EvaluateBlock(body, schema, n.ResourceInstanceAddr().Resource, keyData) diags = diags.Append(configDiags) diff --git a/internal/terraform/node_resource_plan_instance.go b/internal/terraform/node_resource_plan_instance.go index 6322097d5ae4..5737a0496392 100644 --- a/internal/terraform/node_resource_plan_instance.go +++ b/internal/terraform/node_resource_plan_instance.go @@ -355,8 +355,8 @@ func (n *NodePlannableResourceInstance) managedResourceExecute(ctx EvalContext) // values, which could result in a post-condition check relying on that // value being inaccurate. Unless we decide to store the value of the // for-each expression in state, this is unavoidable. - forEach, _ := evaluateForEachExpression(n.Config.ForEach, ctx) - repeatData := EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, forEach) + exp := ctx.InstanceExpander() + repeatData := exp.GetResourceInstanceRepetitionData(n.Addr) checkDiags := evalCheckRules( addrs.ResourcePrecondition, @@ -463,8 +463,9 @@ func (n *NodePlannableResourceInstance) importState(ctx EvalContext, addr addrs. return nil, diags } - forEach, _ := evaluateForEachExpression(n.Config.ForEach, ctx) - keyData := EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, forEach) + exp := ctx.InstanceExpander() + keyData := exp.GetResourceInstanceRepetitionData(n.Addr) + configVal, _, configDiags := ctx.EvaluateBlock(n.Config.Config, schema, nil, keyData) if configDiags.HasErrors() { // We have an overridden resource so we're definitely in a test and From f7f210eb9378d4349567ab1fc187fdafa09c9789 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Thu, 9 Mar 2023 10:16:41 -0800 Subject: [PATCH 05/17] addrs: Getting the parent of a PartialExpandedModule Traversing upward from a PartialExpandedModule is trickier than traversing down because we need to deal with what happens if the traversal crosses over the boundary from partial-expanded into fully-expanded. To deal with that we end up having two different methods to handle the two situations, and a third method to indicate which one to call. Thankfully the need to ask for the parent of a partial-expanded module is relatively rare -- mainly just for input variables whose definitions need to eval in the parent module's scope -- so this awkward API shouldn't be needed in two many places. --- internal/addrs/partial_expanded.go | 51 ++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/internal/addrs/partial_expanded.go b/internal/addrs/partial_expanded.go index 6bffedac550e..d324f018cb25 100644 --- a/internal/addrs/partial_expanded.go +++ b/internal/addrs/partial_expanded.go @@ -175,6 +175,57 @@ func (pem PartialExpandedModule) Resource(resource Resource) PartialExpandedReso } } +// ParentIsModuleInstance returns true if +// [PartialExpandedModule.ParentModuleInstance] would succeed, or false if +// [PartialExpandedModule.ParentPartialExpandedModule] would succeed. +// +// In other words, it determines whether accessing the parent module would +// cross the boundary between the unexpanded and expanded portions of the +// address, which means that any further traversal upwards should be done +// using [ModuleInstance] values rather than [PartialExpandedModule] +// values. +func (pem PartialExpandedModule) ParentIsModuleInstance() bool { + // NOTE: We don't handle the case where unexpandedSuffix is zero-length + // here because standalone PartialExpandedModule values should always have + // at least one unexpanded part. + // + // This isn't true for the special PartialExpandedModule values hidden in + // the internals of PartialExpandedResource, so don't use this method with + // those. + return len(pem.unexpandedSuffix) == 1 +} + +// ParentModuleInstance returns the fully-expanded module instance that is the +// parent of this module if and only if the last step is the only step in the +// path that is unexpanded. +// +// If the receiever does not meet that criteria, the second return value is +// false. Use ParentPartialExpandedModule instead in that case, to get the +// parent represented as a [PartialExpandedModule]. +func (pem PartialExpandedModule) ParentModuleInstance() (ModuleInstance, bool) { + if len(pem.unexpandedSuffix) != 1 { + return nil, false + } + return pem.expandedPrefix, true +} + +// ParentPartialExpandedModule is like ParentModuleInstance but deals with the +// situation where the parent is also partially-expanded and so needs to still +// be described as a PartialExpandedModule. +// +// If the reciever's parent is already exact then the second return value is +// false. Use ParentModuleInstance instead in that case, to get the parent +// represented as a [ModuleInstance]. +func (pem PartialExpandedModule) ParentPartialExpandedModule() (PartialExpandedModule, bool) { + if len(pem.unexpandedSuffix) < 2 { + return PartialExpandedModule{}, false + } + return PartialExpandedModule{ + expandedPrefix: pem.expandedPrefix, + unexpandedSuffix: pem.unexpandedSuffix[: len(pem.unexpandedSuffix)-1 : len(pem.unexpandedSuffix)-1], + }, true +} + // String returns a string representation of the pattern where the known // prefix uses the normal module instance address syntax and the unknown // suffix steps use a similar syntax but with "[*]" as a placeholder to From 40d525f2d7e3b1df3ab91b07dd5b58104071c7b2 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Thu, 9 Mar 2023 10:22:12 -0800 Subject: [PATCH 06/17] terraform: Initial implementation of partial-expanded input variables This is mainly just a proof-of-concept of what it might look like to generate graph nodes representing placeholders for objects in not-fully-expanded modules. These new codepaths are not really accessible yet because it's still invalid to have a module whose expansion is unknown; we'll continue down this path further in later commits once there's actually somewhere to save the partially-evaluated placeholder values. --- internal/terraform/eval_context.go | 10 ++ internal/terraform/eval_context_builtin.go | 25 ++++ internal/terraform/eval_context_mock.go | 11 ++ .../terraform/graph_interface_subgraph.go | 8 ++ internal/terraform/node_module_expand.go | 19 ++- internal/terraform/node_module_variable.go | 124 +++++++++++++++++- 6 files changed, 191 insertions(+), 6 deletions(-) diff --git a/internal/terraform/eval_context.go b/internal/terraform/eval_context.go index e16d034aef0e..396289beb360 100644 --- a/internal/terraform/eval_context.go +++ b/internal/terraform/eval_context.go @@ -211,4 +211,14 @@ type EvalContext interface { // WithPath returns a copy of the context with the internal path set to the // path argument. WithPath(path addrs.ModuleInstance) EvalContext + + // WithPartialExpandedPath is like WithPath but works with a + // partially-expanded path, allowing for speculative evaluation inside + // as-yet-unexpanded modules. + // + // TODO: This currently doesn't really do anything useful, because none + // of the other EvalContext methods pay attention to the partial-expanded + // path. We should make all of the evaluation-related methods support it + // and calculate speculative results in the partial-expanded context. + WithPartialExpandedPath(path addrs.PartialExpandedModule) EvalContext } diff --git a/internal/terraform/eval_context_builtin.go b/internal/terraform/eval_context_builtin.go index ccd30f9929c5..1c7cd1c8f2c9 100644 --- a/internal/terraform/eval_context_builtin.go +++ b/internal/terraform/eval_context_builtin.go @@ -44,6 +44,16 @@ type BuiltinEvalContext struct { // panic if this is not set. pathSet bool + // PartialPathValue is an alternative to PathValue for situations where + // Terraform is speculating about any possible instances of a module whose + // expansion isn't known. partialPathSet is analogous to pathSet. + // This pair is mutually-exclusive with PathValue/pathSet. The Path + // method will still panic if a partial path is set instead of an exact one, + // but evaluation methods will work and will use a speculative evaluation + // context. + PartialPathValue addrs.PartialExpandedModule + partialPathSet bool + // Evaluator is used for evaluating expressions within the scope of this // eval context. Evaluator *Evaluator @@ -89,12 +99,27 @@ type BuiltinEvalContext struct { var _ EvalContext = (*BuiltinEvalContext)(nil) func (ctx *BuiltinEvalContext) WithPath(path addrs.ModuleInstance) EvalContext { + if ctx.pathSet || ctx.partialPathSet { + panic("context already has a module path or partial module path") + } + newCtx := *ctx newCtx.pathSet = true newCtx.PathValue = path return &newCtx } +func (ctx *BuiltinEvalContext) WithPartialExpandedPath(path addrs.PartialExpandedModule) EvalContext { + if ctx.pathSet || ctx.partialPathSet { + panic("context already has a module path or partial module path") + } + + newCtx := *ctx + newCtx.partialPathSet = true + newCtx.PartialPathValue = path + return &newCtx +} + func (ctx *BuiltinEvalContext) Stopped() <-chan struct{} { // This can happen during tests. During tests, we just block forever. if ctx.StopContext == nil { diff --git a/internal/terraform/eval_context_mock.go b/internal/terraform/eval_context_mock.go index 18d174c4aa81..3d65d39c2036 100644 --- a/internal/terraform/eval_context_mock.go +++ b/internal/terraform/eval_context_mock.go @@ -118,6 +118,9 @@ type MockEvalContext struct { PathCalled bool PathPath addrs.ModuleInstance + WithPartialExpandedPathCalled bool + WithPartialExpandedPathPath addrs.PartialExpandedModule + SetRootModuleArgumentCalled bool SetRootModuleArgumentAddr addrs.InputVariable SetRootModuleArgumentValue cty.Value @@ -346,6 +349,14 @@ func (c *MockEvalContext) Path() addrs.ModuleInstance { return c.PathPath } +func (c *MockEvalContext) WithPartialExpandedPath(path addrs.PartialExpandedModule) EvalContext { + c.WithPartialExpandedPathCalled = true + c.WithPartialExpandedPathPath = path + newC := *c + newC.WithPartialExpandedPathPath = path + return &newC +} + func (c *MockEvalContext) SetRootModuleArgument(addr addrs.InputVariable, v cty.Value) { c.SetRootModuleArgumentCalled = true c.SetRootModuleArgumentAddr = addr diff --git a/internal/terraform/graph_interface_subgraph.go b/internal/terraform/graph_interface_subgraph.go index 2e013aeca0fa..025b6518cc21 100644 --- a/internal/terraform/graph_interface_subgraph.go +++ b/internal/terraform/graph_interface_subgraph.go @@ -18,3 +18,11 @@ type GraphNodeModuleInstance interface { type GraphNodeModulePath interface { ModulePath() addrs.Module } + +// GraphNodePartialExpandedModule is implemented by nodes that represent +// potentially multiple instances of a particular configuration object that +// we haven't fully resolved yet because the expansion of one or more of +// the calling modules was deferred. +type GraphNodePartialExpandedModule interface { + PartialExpandedModule() addrs.PartialExpandedModule +} diff --git a/internal/terraform/node_module_expand.go b/internal/terraform/node_module_expand.go index 0e3a447dbc31..c995de7de3b9 100644 --- a/internal/terraform/node_module_expand.go +++ b/internal/terraform/node_module_expand.go @@ -113,6 +113,7 @@ func (n *nodeExpandModule) Execute(ctx EvalContext, op walkOperation) (diags tfd // to our module, and register module instances with each of them. for _, module := range expander.ExpandModule(n.Addr.Parent()) { ctx = ctx.WithPath(module) + callAddr := module.ChildCall(call.Name) switch { case n.ModuleCall.Count != nil: count, known, ctDiags := evaluateCountExpression(n.ModuleCall.Count, ctx) @@ -121,6 +122,7 @@ func (n *nodeExpandModule) Execute(ctx EvalContext, op walkOperation) (diags tfd return diags } if known { + log.Printf("[TRACE] nodeExpandModule: %s has count = %d", callAddr, count) expander.SetModuleCount(module, call, count) } else { // TEMP: The rest of Terraform Core isn't ready to deal with @@ -136,6 +138,7 @@ func (n *nodeExpandModule) Execute(ctx EvalContext, op walkOperation) (diags tfd Subject: n.ModuleCall.Count.Range().Ptr(), Extra: diagnosticCausedByUnknown(true), }) + log.Printf("[TRACE] nodeExpandModule: %s has unknown count", callAddr) expander.SetModuleCountUnknown(module, call) } @@ -146,6 +149,7 @@ func (n *nodeExpandModule) Execute(ctx EvalContext, op walkOperation) (diags tfd return diags } if known { + log.Printf("[TRACE] nodeExpandModule: %s has for_each with instance count %d", callAddr, len(forEach)) expander.SetModuleForEach(module, call, forEach) } else { // TEMP: The rest of Terraform Core isn't ready to deal with @@ -161,10 +165,12 @@ func (n *nodeExpandModule) Execute(ctx EvalContext, op walkOperation) (diags tfd Subject: n.ModuleCall.ForEach.Range().Ptr(), Extra: diagnosticCausedByUnknown(true), }) + log.Printf("[TRACE] nodeExpandModule: %s has for_each with unknown instance keys", callAddr) expander.SetModuleForEachUnknown(module, call) } default: + log.Printf("[TRACE] nodeExpandModule: %s is a single-instance module", callAddr) expander.SetModuleSingle(module, call) } } @@ -276,10 +282,15 @@ func (n *nodeValidateModule) Execute(ctx EvalContext, op walkOperation) (diags t _, call := n.Addr.Call() expander := ctx.InstanceExpander() - // Modules all evaluate to single instances during validation, only to - // create a proper context within which to evaluate. All parent modules - // will be a single instance, but still get our address in the expected - // manner anyway to ensure they've been registered correctly. + // We don't actually ever expand during validation, since we're testing + // whether modules are valid for _any_ input, rather than for some specific + // input. + // + // Therefore we evaluate the count or for_each expression purely to check + // whether its valid, but then always mark the expansion unknown so that + // downstreams will work with placeholder values that cover only what + // all instances of the module have in common. Specific instance validation + // will happen as part of planning. for _, module := range expander.ExpandModule(n.Addr.Parent()) { ctx = ctx.WithPath(module) diff --git a/internal/terraform/node_module_variable.go b/internal/terraform/node_module_variable.go index 7237fb84b322..1b2ca5ca83f5 100644 --- a/internal/terraform/node_module_variable.go +++ b/internal/terraform/node_module_variable.go @@ -80,6 +80,14 @@ func (n *nodeExpandModuleVariable) DynamicExpand(ctx EvalContext) (*Graph, tfdia } g.Add(o) } + for _, unexpModule := range expander.UnknownModuleInstances(n.Module) { + o := &nodePartialExpandedModuleVariable{ + Addr: addrs.ObjectInPartialExpandedModule(unexpModule, n.Addr), + Config: n.Config, + Expr: n.Expr, + } + g.Add(o) + } addRootNodeToGraph(&g) if checkableAddrs != nil { @@ -136,8 +144,8 @@ func (n *nodeExpandModuleVariable) ReferenceableAddrs() []addrs.Referenceable { return []addrs.Referenceable{n.Addr} } -// nodeModuleVariable represents a module variable input during -// the apply step. +// nodeModuleVariable represents a module input variable in an already-expanded +// module instance. type nodeModuleVariable struct { Addr addrs.AbsInputVariableInstance Config *configs.Variable // Config is the var in the config @@ -248,6 +256,9 @@ func (n *nodeModuleVariable) evalModuleVariable(ctx EvalContext, validateOnly bo case validateOnly: // the instance expander does not track unknown expansion values, so we // have to assume all RepetitionData is unknown. + // TODO: This is essentially what nodePartialExpandedModuleVariable + // does too, so we should consider making the validate walk use + // that node type instead so we have fewer codepaths. moduleInstanceRepetitionData = instances.RepetitionData{ CountIndex: cty.UnknownVal(cty.Number), EachKey: cty.UnknownVal(cty.String), @@ -288,3 +299,112 @@ func (n *nodeModuleVariable) evalModuleVariable(ctx EvalContext, validateOnly bo return finalVal, diags.ErrWithWarnings() } + +// nodePartialExpandedModuleVariable represents a placeholder for zero or more +// module input variable instances that live under a module path which can't +// be fully expanded yet. +type nodePartialExpandedModuleVariable struct { + Addr addrs.InPartialExpandedModule[addrs.InputVariable] + Config *configs.Variable // Config is the var in the config + Expr hcl.Expression // Expr is the value expression given in the call +} + +// Ensure that we are implementing all of the interfaces we think we are +// implementing. +var ( + _ GraphNodePartialExpandedModule = (*nodePartialExpandedModuleVariable)(nil) + _ GraphNodeExecutable = (*nodePartialExpandedModuleVariable)(nil) + _ graphNodeTemporaryValue = (*nodePartialExpandedModuleVariable)(nil) +) + +func (n *nodePartialExpandedModuleVariable) Name() string { + return n.Addr.String() +} + +func (n *nodePartialExpandedModuleVariable) temporaryValue() bool { + return true +} + +// Execute implements GraphNodeExecutable +func (n *nodePartialExpandedModuleVariable) Execute(ctx EvalContext, op walkOperation) tfdiags.Diagnostics { + // For an input variable in a partial-expanded module our job is to + // produce a placeholder value that ought to be true for any possible + // instance of the containing module once its expansion _is_ known. + // Downstreams will use this to further derive approximate results, so + // it's important that we don't over-promise and err on the side of + // using unknown value placeholders for anything that might vary between + // instances of the module. + + var diags tfdiags.Diagnostics + var givenVal cty.Value + var errSourceRange tfdiags.SourceRange + + ctx = n.evalContextForParentModuleInstance(ctx) + if expr := n.Expr; expr != nil { + + // TODO: We should ideally be able to tailor this based on whether the + // module configuration is using count, for_each, or neither, but we don't + // currently have access to that information here so we'll settle for just + // allowing everything as unknown for now. + moduleInstanceRepetitionData := instances.RepetitionData{ + CountIndex: cty.UnknownVal(cty.Number), + EachKey: cty.UnknownVal(cty.String), + EachValue: cty.DynamicVal, + } + + scope := ctx.EvaluationScope(nil, moduleInstanceRepetitionData) + val, moreDiags := scope.EvalExpr(expr, cty.DynamicPseudoType) + diags = diags.Append(moreDiags) + if moreDiags.HasErrors() { + return diags + } + givenVal = val + errSourceRange = tfdiags.SourceRangeFromHCL(expr.Range()) + } else { + // We'll use cty.NilVal to represent the variable not being set at all. + givenVal = cty.NilVal + errSourceRange = tfdiags.SourceRangeFromHCL(n.Config.DeclRange) // we use the declaration range as a fallback for an undefined variable + } + + // We construct a synthetic InputValue here to pretend as if this were + // a root module variable set from outside, just as a convenience so we + // can reuse the InputValue type for this. + rawVal := &InputValue{ + Value: givenVal, + SourceType: ValueFromConfig, + SourceRange: errSourceRange, + } + + // prepareFinalInputVariableValue expects to be working with a fully-expanded + // input variable, so for now we'll construct a synthetic address for it to + // use. We should probably find a better way to do this at some point + // because this will produce mildly-inaccurate error messages. + fakeInstanceAddr := n.Addr.Module.Module().UnkeyedInstanceShim().InputVariable(n.Addr.Local.Name) + _, moreDiags := prepareFinalInputVariableValue(fakeInstanceAddr, rawVal, n.Config) + diags = diags.Append(moreDiags) + + // TODO: Actually record the result for use elsewhere, once there's + // actually somewhere to save it. + + return diags +} + +// evalContextForParentModuleInstance returns a "pathed" [EvalContext] that has +// either an exact module path or a partial-expanded one depending on whether +// the parent module instance of this variable is expanded or not. +func (n *nodePartialExpandedModuleVariable) evalContextForParentModuleInstance(unpathedCtx EvalContext) EvalContext { + if parent, ok := n.Addr.Module.ParentModuleInstance(); ok { + return unpathedCtx.WithPath(parent) + } else if parent, ok := n.Addr.Module.ParentPartialExpandedModule(); ok { + return unpathedCtx.WithPartialExpandedPath(parent) + } else { + // Should never happen: parent should always be either a ModuleInstance + // or another PartialExpandedModule. + panic(fmt.Sprintf("parent of %s is neither fully-expanded nor partially-expanded module", n.Addr.Module)) + } +} + +// PartialExpandedModule implements GraphNodePartialExpandedModule +func (n *nodePartialExpandedModuleVariable) PartialExpandedModule() addrs.PartialExpandedModule { + return n.Addr.Module +} From b000ef503d123873503df4ab074ee875921fdf9a Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Thu, 9 Mar 2023 10:43:22 -0800 Subject: [PATCH 07/17] core: Graph walk is aware of partial-expanded module paths Our evaluation strategy for module-namespaced objects unfortunately depends quite strongly on having the right EvalContext in scope for each graph node, referring to the appropriate namespace in which to evaluate expressions. Although I was pretty reluctant to integrate the idea of partial-expanded module paths at quite this low a level, it does seem like the most pragmatic answer since it works with rather than against the existing evaluation strategies. As of this commit this isn't really doing anything because it isn't possible to reach any graph node that has a partial-expanded path and the EvalContext itself doesn't actually properly support evaluation in a partial-expanded path anyway; we'll fix up the rest of this in later commits before making these codepaths reachable. --- internal/terraform/context_plan_test.go | 61 -------------------- internal/terraform/eval_context_builtin.go | 12 ++-- internal/terraform/graph.go | 4 ++ internal/terraform/graph_walk.go | 17 +++++- internal/terraform/graph_walk_context.go | 13 +++++ internal/terraform/node_module_expand.go | 28 --------- internal/terraform/node_module_variable.go | 2 +- internal/terraform/node_resource_abstract.go | 28 --------- 8 files changed, 36 insertions(+), 129 deletions(-) diff --git a/internal/terraform/context_plan_test.go b/internal/terraform/context_plan_test.go index 52456e3f4100..0d759398b446 100644 --- a/internal/terraform/context_plan_test.go +++ b/internal/terraform/context_plan_test.go @@ -2248,25 +2248,6 @@ func TestContext2Plan_countComputed(t *testing.T) { } } -func TestContext2Plan_countComputedModule(t *testing.T) { - m := testModule(t, "plan-count-computed-module") - p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn - ctx := testContext2(t, &ContextOpts{ - Providers: map[addrs.Provider]providers.Factory{ - addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), - }, - }) - - _, err := ctx.Plan(m, states.NewState(), DefaultPlanOpts) - - expectedErr := `The "count" value depends on resource attributes` - if !strings.Contains(fmt.Sprintf("%s", err), expectedErr) { - t.Fatalf("expected err would contain %q\nerr: %s\n", - expectedErr, err) - } -} - func TestContext2Plan_countModuleStatic(t *testing.T) { m := testModule(t, "plan-count-module-static") p := testProvider("aws") @@ -3089,48 +3070,6 @@ func TestContext2Plan_forEach(t *testing.T) { } } -func TestContext2Plan_forEachUnknownValue(t *testing.T) { - // This module has a variable defined, but it's value is unknown. We - // expect this to produce an error, but not to panic. - m := testModule(t, "plan-for-each-unknown-value") - p := testProvider("aws") - ctx := testContext2(t, &ContextOpts{ - Providers: map[addrs.Provider]providers.Factory{ - addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), - }, - }) - - _, diags := ctx.Plan(m, states.NewState(), &PlanOpts{ - Mode: plans.NormalMode, - SetVariables: InputValues{ - "foo": { - Value: cty.UnknownVal(cty.String), - SourceType: ValueFromCLIArg, - }, - }, - }) - if !diags.HasErrors() { - // Should get this error: - // Invalid for_each argument: The "for_each" value depends on resource attributes that cannot be determined until apply... - t.Fatal("succeeded; want errors") - } - - gotErrStr := diags.Err().Error() - wantErrStr := "Invalid for_each argument" - if !strings.Contains(gotErrStr, wantErrStr) { - t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErrStr, wantErrStr) - } - - // We should have a diagnostic that is marked as being caused by unknown - // values. - for _, diag := range diags { - if tfdiags.DiagnosticCausedByUnknown(diag) { - return // don't fall through to the error below - } - } - t.Fatalf("no diagnostic is marked as being caused by unknown\n%s", diags.Err().Error()) -} - func TestContext2Plan_destroy(t *testing.T) { m := testModule(t, "plan-destroy") p := testProvider("aws") diff --git a/internal/terraform/eval_context_builtin.go b/internal/terraform/eval_context_builtin.go index 1c7cd1c8f2c9..31d67a47f38f 100644 --- a/internal/terraform/eval_context_builtin.go +++ b/internal/terraform/eval_context_builtin.go @@ -99,22 +99,18 @@ type BuiltinEvalContext struct { var _ EvalContext = (*BuiltinEvalContext)(nil) func (ctx *BuiltinEvalContext) WithPath(path addrs.ModuleInstance) EvalContext { - if ctx.pathSet || ctx.partialPathSet { - panic("context already has a module path or partial module path") - } - newCtx := *ctx newCtx.pathSet = true newCtx.PathValue = path + newCtx.partialPathSet = false + newCtx.PartialPathValue = addrs.PartialExpandedModule{} return &newCtx } func (ctx *BuiltinEvalContext) WithPartialExpandedPath(path addrs.PartialExpandedModule) EvalContext { - if ctx.pathSet || ctx.partialPathSet { - panic("context already has a module path or partial module path") - } - newCtx := *ctx + newCtx.pathSet = false + newCtx.PathValue = nil newCtx.partialPathSet = true newCtx.PartialPathValue = path return &newCtx diff --git a/internal/terraform/graph.go b/internal/terraform/graph.go index 4991c9a3b580..e68da3bd7838 100644 --- a/internal/terraform/graph.go +++ b/internal/terraform/graph.go @@ -75,6 +75,10 @@ func (g *Graph) walk(walker GraphWalker) tfdiags.Diagnostics { vertexCtx = walker.EnterPath(pn.Path()) defer walker.ExitPath(pn.Path()) } + if pn, ok := v.(GraphNodePartialExpandedModule); ok { + vertexCtx = walker.EnterPartialExpandedPath(pn.PartialExpandedModule()) + defer walker.ExitPartialExpandedPath(pn.PartialExpandedModule()) + } if g.checkAndApplyOverrides(ctx.Overrides(), v) { // We can skip whole vertices if they are in a module that has been diff --git a/internal/terraform/graph_walk.go b/internal/terraform/graph_walk.go index 54f5c674d24e..768e052baad1 100644 --- a/internal/terraform/graph_walk.go +++ b/internal/terraform/graph_walk.go @@ -14,6 +14,8 @@ type GraphWalker interface { EvalContext() EvalContext EnterPath(addrs.ModuleInstance) EvalContext ExitPath(addrs.ModuleInstance) + EnterPartialExpandedPath(addrs.PartialExpandedModule) EvalContext + ExitPartialExpandedPath(addrs.PartialExpandedModule) Execute(EvalContext, GraphNodeExecutable) tfdiags.Diagnostics } @@ -22,7 +24,16 @@ type GraphWalker interface { // implementing all the required functions. type NullGraphWalker struct{} -func (NullGraphWalker) EvalContext() EvalContext { return new(MockEvalContext) } -func (NullGraphWalker) EnterPath(addrs.ModuleInstance) EvalContext { return new(MockEvalContext) } -func (NullGraphWalker) ExitPath(addrs.ModuleInstance) {} +var _ GraphWalker = NullGraphWalker{} + +func (NullGraphWalker) EvalContext() EvalContext { return new(MockEvalContext) } + +func (NullGraphWalker) EnterPath(addrs.ModuleInstance) EvalContext { return new(MockEvalContext) } +func (NullGraphWalker) ExitPath(addrs.ModuleInstance) {} + +func (NullGraphWalker) EnterPartialExpandedPath(addrs.PartialExpandedModule) EvalContext { + return new(MockEvalContext) +} +func (NullGraphWalker) ExitPartialExpandedPath(addrs.PartialExpandedModule) {} + func (NullGraphWalker) Execute(EvalContext, GraphNodeExecutable) tfdiags.Diagnostics { return nil } diff --git a/internal/terraform/graph_walk_context.go b/internal/terraform/graph_walk_context.go index 2171a26b8fc3..f4f3be9a44d9 100644 --- a/internal/terraform/graph_walk_context.go +++ b/internal/terraform/graph_walk_context.go @@ -64,6 +64,8 @@ type ContextGraphWalker struct { provisionerLock sync.Mutex } +var _ GraphWalker = (*ContextGraphWalker)(nil) + func (w *ContextGraphWalker) EnterPath(path addrs.ModuleInstance) EvalContext { w.contextLock.Lock() defer w.contextLock.Unlock() @@ -79,6 +81,17 @@ func (w *ContextGraphWalker) EnterPath(path addrs.ModuleInstance) EvalContext { return ctx } +func (w *ContextGraphWalker) EnterPartialExpandedPath(path addrs.PartialExpandedModule) EvalContext { + w.contextLock.Lock() + defer w.contextLock.Unlock() + + // TODO: Consider caching these in a similar way as we do in EnterPath, + // if the allocations here prove bothersome. + + ctx := w.EvalContext().WithPartialExpandedPath(path) + return ctx +} + func (w *ContextGraphWalker) EvalContext() EvalContext { w.once.Do(w.init) diff --git a/internal/terraform/node_module_expand.go b/internal/terraform/node_module_expand.go index c995de7de3b9..f8044b8c7644 100644 --- a/internal/terraform/node_module_expand.go +++ b/internal/terraform/node_module_expand.go @@ -6,8 +6,6 @@ package terraform import ( "log" - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/dag" @@ -125,19 +123,6 @@ func (n *nodeExpandModule) Execute(ctx EvalContext, op walkOperation) (diags tfd log.Printf("[TRACE] nodeExpandModule: %s has count = %d", callAddr, count) expander.SetModuleCount(module, call, count) } else { - // TEMP: The rest of Terraform Core isn't ready to deal with - // us marking expansion as unknown yet, so for now we'll preserve - // this as a similar error to what evaluateCountExpression used to - // return for an unknown count, thereby preventing downstream - // code from relying on this until we've made sure everything else - // is ready to deal with it. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid count argument", - Detail: `The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the count depends on.`, - Subject: n.ModuleCall.Count.Range().Ptr(), - Extra: diagnosticCausedByUnknown(true), - }) log.Printf("[TRACE] nodeExpandModule: %s has unknown count", callAddr) expander.SetModuleCountUnknown(module, call) } @@ -152,19 +137,6 @@ func (n *nodeExpandModule) Execute(ctx EvalContext, op walkOperation) (diags tfd log.Printf("[TRACE] nodeExpandModule: %s has for_each with instance count %d", callAddr, len(forEach)) expander.SetModuleForEach(module, call, forEach) } else { - // TEMP: The rest of Terraform Core isn't ready to deal with - // us marking expansion as unknown yet, so for now we'll preserve - // this as a similar error to what evaluateCountExpression used to - // return for an unknown count, thereby preventing downstream - // code from relying on this until we've made sure everything else - // is ready to deal with it. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid for_each argument", - Detail: `The "for_each" value's instance keys depend on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that for_each depends on.`, - Subject: n.ModuleCall.ForEach.Range().Ptr(), - Extra: diagnosticCausedByUnknown(true), - }) log.Printf("[TRACE] nodeExpandModule: %s has for_each with unknown instance keys", callAddr) expander.SetModuleForEachUnknown(module, call) } diff --git a/internal/terraform/node_module_variable.go b/internal/terraform/node_module_variable.go index 1b2ca5ca83f5..1a3cfe4d3860 100644 --- a/internal/terraform/node_module_variable.go +++ b/internal/terraform/node_module_variable.go @@ -352,7 +352,7 @@ func (n *nodePartialExpandedModuleVariable) Execute(ctx EvalContext, op walkOper EachValue: cty.DynamicVal, } - scope := ctx.EvaluationScope(nil, moduleInstanceRepetitionData) + scope := ctx.EvaluationScope(nil, nil, moduleInstanceRepetitionData) val, moreDiags := scope.EvalExpr(expr, cty.DynamicPseudoType) diags = diags.Append(moreDiags) if moreDiags.HasErrors() { diff --git a/internal/terraform/node_resource_abstract.go b/internal/terraform/node_resource_abstract.go index 2751bb46e296..437371cfbedd 100644 --- a/internal/terraform/node_resource_abstract.go +++ b/internal/terraform/node_resource_abstract.go @@ -7,8 +7,6 @@ import ( "fmt" "log" - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/configs/configschema" @@ -416,19 +414,6 @@ func (n *NodeAbstractResource) writeResourceState(ctx EvalContext, addr addrs.Ab if known { expander.SetResourceCount(addr.Module, n.Addr.Resource, count) } else { - // TEMP: The rest of Terraform Core isn't ready to deal with - // us marking expansion as unknown yet, so for now we'll preserve - // this as a similar error to what evaluateCountExpression used to - // return for an unknown count, thereby preventing downstream - // code from relying on this until we've made sure everything else - // is ready to deal with it. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid count argument", - Detail: `The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the count depends on.`, - Subject: n.Config.Count.Range().Ptr(), - Extra: diagnosticCausedByUnknown(true), - }) expander.SetResourceCountUnknown(addr.Module, n.Addr.Resource) } @@ -445,19 +430,6 @@ func (n *NodeAbstractResource) writeResourceState(ctx EvalContext, addr addrs.Ab if known { expander.SetResourceForEach(addr.Module, n.Addr.Resource, forEach) } else { - // TEMP: The rest of Terraform Core isn't ready to deal with - // us marking expansion as unknown yet, so for now we'll preserve - // this as a similar error to what evaluateForEachExpression used to - // return for an unknown for_each, thereby preventing downstream - // code from relying on this until we've made sure everything else - // is ready to deal with it. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid for_each argument", - Detail: `The "for_each" value's instance keys depend on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that for_each depends on.`, - Subject: n.Config.ForEach.Range().Ptr(), - Extra: diagnosticCausedByUnknown(true), - }) expander.SetResourceForEachUnknown(addr.Module, n.Addr.Resource) } From cd4a2215fdbf8f1dcf3a00350f47e89b04cf39e0 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Thu, 9 Mar 2023 13:21:24 -0800 Subject: [PATCH 08/17] core: use namedvals.State to track input variable evaluation This replaces the direct manipulation of a map shared between three different components, encapsulating that manipulation now inside a single wrapping API that itself ensures safe concurrent access. In future commits we'll do the same for local values and output values, but for now those part of namedvals.State remain unused. --- internal/terraform/context_walk.go | 2 + internal/terraform/eval_context_builtin.go | 47 +++++----------------- internal/terraform/evaluate.go | 47 +++------------------- internal/terraform/evaluate_test.go | 18 +++++---- internal/terraform/graph_walk_context.go | 22 +++++----- internal/terraform/test_context.go | 21 +++++----- 6 files changed, 47 insertions(+), 110 deletions(-) diff --git a/internal/terraform/context_walk.go b/internal/terraform/context_walk.go index 30ed5547a500..d78fb7bc1c6d 100644 --- a/internal/terraform/context_walk.go +++ b/internal/terraform/context_walk.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/instances" "github.com/hashicorp/terraform/internal/moduletest/mocking" + "github.com/hashicorp/terraform/internal/namedvals" "github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/providers" "github.com/hashicorp/terraform/internal/refactoring" @@ -163,6 +164,7 @@ func (c *Context) graphWalker(operation walkOperation, opts *graphWalkOpts) *Con Overrides: opts.Overrides, PrevRunState: prevRunState, Changes: changes.SyncWrapper(), + NamedValues: namedvals.NewState(), Checks: checkState, InstanceExpander: instances.NewExpander(), ExternalProviderConfigs: opts.ExternalProviderConfigs, diff --git a/internal/terraform/eval_context_builtin.go b/internal/terraform/eval_context_builtin.go index 31d67a47f38f..f70c50236d07 100644 --- a/internal/terraform/eval_context_builtin.go +++ b/internal/terraform/eval_context_builtin.go @@ -19,6 +19,7 @@ import ( "github.com/hashicorp/terraform/internal/instances" "github.com/hashicorp/terraform/internal/lang" "github.com/hashicorp/terraform/internal/moduletest/mocking" + "github.com/hashicorp/terraform/internal/namedvals" "github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/providers" "github.com/hashicorp/terraform/internal/provisioners" @@ -58,14 +59,9 @@ type BuiltinEvalContext struct { // eval context. Evaluator *Evaluator - // VariableValues contains the variable values across all modules. This - // structure is shared across the entire containing context, and so it - // may be accessed only when holding VariableValuesLock. - // The keys of the first level of VariableValues are the string - // representations of addrs.ModuleInstance values. The second-level keys - // are variable names within each module instance. - VariableValues map[string]map[string]cty.Value - VariableValuesLock *sync.Mutex + // NamedValues is where we keep the values of already-evaluated input + // variables, local values, and output values. + NamedValues *namedvals.State // Plugins is a library of plugin components (providers and provisioners) // available for use during a graph walk. @@ -478,49 +474,24 @@ func (ctx *BuiltinEvalContext) Path() addrs.ModuleInstance { } func (ctx *BuiltinEvalContext) SetRootModuleArgument(addr addrs.InputVariable, v cty.Value) { - ctx.VariableValuesLock.Lock() - defer ctx.VariableValuesLock.Unlock() - log.Printf("[TRACE] BuiltinEvalContext: Storing final value for variable %s", addr.Absolute(addrs.RootModuleInstance)) - key := addrs.RootModuleInstance.String() - args := ctx.VariableValues[key] - if args == nil { - args = make(map[string]cty.Value) - ctx.VariableValues[key] = args - } - args[addr.Name] = v + + absAddr := addrs.RootModuleInstance.InputVariable(addr.Name) + ctx.NamedValues.SetInputVariableValue(absAddr, v) } func (ctx *BuiltinEvalContext) SetModuleCallArgument(callAddr addrs.ModuleCallInstance, varAddr addrs.InputVariable, v cty.Value) { - ctx.VariableValuesLock.Lock() - defer ctx.VariableValuesLock.Unlock() - if !ctx.pathSet { panic("context path not set") } childPath := callAddr.ModuleInstance(ctx.PathValue) log.Printf("[TRACE] BuiltinEvalContext: Storing final value for variable %s", varAddr.Absolute(childPath)) - key := childPath.String() - args := ctx.VariableValues[key] - if args == nil { - args = make(map[string]cty.Value) - ctx.VariableValues[key] = args - } - args[varAddr.Name] = v + ctx.NamedValues.SetInputVariableValue(childPath.InputVariable(varAddr.Name), v) } func (ctx *BuiltinEvalContext) GetVariableValue(addr addrs.AbsInputVariableInstance) cty.Value { - ctx.VariableValuesLock.Lock() - defer ctx.VariableValuesLock.Unlock() - - modKey := addr.Module.String() - modVars := ctx.VariableValues[modKey] - val, ok := modVars[addr.Variable.Name] - if !ok { - return cty.DynamicVal - } - return val + return ctx.NamedValues.GetInputVariableValue(addr) } func (ctx *BuiltinEvalContext) Changes() *plans.ChangesSync { diff --git a/internal/terraform/evaluate.go b/internal/terraform/evaluate.go index 8cf9fe771e7a..d304be61301f 100644 --- a/internal/terraform/evaluate.go +++ b/internal/terraform/evaluate.go @@ -8,7 +8,6 @@ import ( "log" "os" "path/filepath" - "sync" "time" "github.com/hashicorp/hcl/v2" @@ -21,6 +20,7 @@ import ( "github.com/hashicorp/terraform/internal/instances" "github.com/hashicorp/terraform/internal/lang" "github.com/hashicorp/terraform/internal/lang/marks" + "github.com/hashicorp/terraform/internal/namedvals" "github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/states" "github.com/hashicorp/terraform/internal/tfdiags" @@ -39,15 +39,9 @@ type Evaluator struct { // Config is the root node in the configuration tree. Config *configs.Config - // VariableValues is a map from variable names to their associated values, - // within the module indicated by ModulePath. VariableValues is modified - // concurrently, and so it must be accessed only while holding - // VariableValuesLock. - // - // The first map level is string representations of addr.ModuleInstance - // values, while the second level is variable names. - VariableValues map[string]map[string]cty.Value - VariableValuesLock *sync.Mutex + // NamedValues is where we keep the values of already-evaluated input + // variables, local values, and output values. + NamedValues *namedvals.State // Plugins is the library of available plugin components (providers and // provisioners) that we have available to help us evaluate expressions @@ -252,8 +246,6 @@ func (d *evaluationStateData) GetInputVariable(addr addrs.InputVariable, rng tfd }) return cty.DynamicVal, diags } - d.Evaluator.VariableValuesLock.Lock() - defer d.Evaluator.VariableValuesLock.Unlock() // During the validate walk, input variables are always unknown so // that we are validating the configuration for all possible input values @@ -276,36 +268,7 @@ func (d *evaluationStateData) GetInputVariable(addr addrs.InputVariable, rng tfd return cty.UnknownVal(config.Type), diags } - moduleAddrStr := d.ModulePath.String() - vals := d.Evaluator.VariableValues[moduleAddrStr] - if vals == nil { - return cty.UnknownVal(config.Type), diags - } - - // d.Evaluator.VariableValues should always contain valid "final values" - // for variables, which is to say that they have already had type - // conversions, validations, and default value handling applied to them. - // Those are the responsibility of the graph notes representing the - // variable declarations. Therefore here we just trust that we already - // have a correct value. - - val, isSet := vals[addr.Name] - if !isSet { - // We should not be able to get here without having a valid value - // for every variable, so this always indicates a bug in either - // the graph builder (not including all the needed nodes) or in - // the graph nodes representing variables. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Reference to unresolved input variable`, - Detail: fmt.Sprintf( - `The final value for %s is missing in Terraform's evaluation context. This is a bug in Terraform; please report it!`, - addr.Absolute(d.ModulePath), - ), - Subject: rng.ToHCL().Ptr(), - }) - val = cty.UnknownVal(config.Type) - } + val := d.Evaluator.NamedValues.GetInputVariableValue(d.ModulePath.InputVariable(addr.Name)) // Mark if sensitive if config.Sensitive { diff --git a/internal/terraform/evaluate_test.go b/internal/terraform/evaluate_test.go index 9c9dd2c84ed3..c7f96473c076 100644 --- a/internal/terraform/evaluate_test.go +++ b/internal/terraform/evaluate_test.go @@ -4,7 +4,6 @@ package terraform import ( - "sync" "testing" "github.com/davecgh/go-spew/spew" @@ -14,6 +13,7 @@ import ( "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/lang/marks" + "github.com/hashicorp/terraform/internal/namedvals" "github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/providers" "github.com/hashicorp/terraform/internal/states" @@ -155,6 +155,14 @@ func TestEvaluatorGetOutputValue(t *testing.T) { // This particularly tests that a sensitive attribute in config // results in a value that has a "sensitive" cty Mark func TestEvaluatorGetInputVariable(t *testing.T) { + namedValues := namedvals.NewState() + namedValues.SetInputVariableValue( + addrs.RootModuleInstance.InputVariable("some_var"), cty.StringVal("bar"), + ) + namedValues.SetInputVariableValue( + addrs.RootModuleInstance.InputVariable("some_other_var"), cty.StringVal("boop").Mark(marks.Sensitive), + ) + evaluator := &Evaluator{ Meta: &ContextMeta{ Env: "foo", @@ -180,13 +188,7 @@ func TestEvaluatorGetInputVariable(t *testing.T) { }, }, }, - VariableValues: map[string]map[string]cty.Value{ - "": { - "some_var": cty.StringVal("bar"), - "some_other_var": cty.StringVal("boop").Mark(marks.Sensitive), - }, - }, - VariableValuesLock: &sync.Mutex{}, + NamedValues: namedValues, } data := &evaluationStateData{ diff --git a/internal/terraform/graph_walk_context.go b/internal/terraform/graph_walk_context.go index f4f3be9a44d9..cfe04b7cba0d 100644 --- a/internal/terraform/graph_walk_context.go +++ b/internal/terraform/graph_walk_context.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/instances" "github.com/hashicorp/terraform/internal/moduletest/mocking" + "github.com/hashicorp/terraform/internal/namedvals" "github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/providers" "github.com/hashicorp/terraform/internal/provisioners" @@ -36,6 +37,7 @@ type ContextGraphWalker struct { PrevRunState *states.SyncState // Used for safe concurrent access to state Changes *plans.ChangesSync // Used for safe concurrent writes to changes Checks *checks.State // Used for safe concurrent writes of checkable objects and their check results + NamedValues *namedvals.State // Tracks evaluation of input variables, local values, and output values InstanceExpander *instances.Expander // Tracks our gradual expansion of module and resource instances Imports []configs.Import MoveResults refactoring.MoveResults // Read-only record of earlier processing of move statements @@ -99,15 +101,14 @@ func (w *ContextGraphWalker) EvalContext() EvalContext { // so that we can safely run multiple evaluations at once across // different modules. evaluator := &Evaluator{ - Meta: w.Context.meta, - Config: w.Config, - Operation: w.Operation, - State: w.State, - Changes: w.Changes, - Plugins: w.Context.plugins, - VariableValues: w.variableValues, - VariableValuesLock: &w.variableValuesLock, - PlanTimestamp: w.PlanTimestamp, + Meta: w.Context.meta, + Config: w.Config, + Operation: w.Operation, + State: w.State, + Changes: w.Changes, + Plugins: w.Context.plugins, + NamedValues: w.NamedValues, + PlanTimestamp: w.PlanTimestamp, } ctx := &BuiltinEvalContext{ @@ -125,12 +126,11 @@ func (w *ContextGraphWalker) EvalContext() EvalContext { ProvisionerLock: &w.provisionerLock, ChangesValue: w.Changes, ChecksValue: w.Checks, + NamedValues: w.NamedValues, StateValue: w.State, RefreshStateValue: w.RefreshState, PrevRunStateValue: w.PrevRunState, Evaluator: evaluator, - VariableValues: w.variableValues, - VariableValuesLock: &w.variableValuesLock, OverrideValues: w.Overrides, } diff --git a/internal/terraform/test_context.go b/internal/terraform/test_context.go index b9249892de36..c2933a50f297 100644 --- a/internal/terraform/test_context.go +++ b/internal/terraform/test_context.go @@ -5,7 +5,6 @@ package terraform import ( "fmt" - "sync" "github.com/hashicorp/hcl/v2" "github.com/zclconf/go-cty/cty" @@ -15,6 +14,7 @@ import ( "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/lang" "github.com/hashicorp/terraform/internal/moduletest" + "github.com/hashicorp/terraform/internal/namedvals" "github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/states" "github.com/hashicorp/terraform/internal/tfdiags" @@ -75,16 +75,15 @@ func (ctx *TestContext) evaluationStateData(alternateStates map[string]*evaluati return &evaluationStateData{ Evaluator: &Evaluator{ - Operation: operation, - Meta: ctx.meta, - Config: ctx.Config, - Plugins: ctx.plugins, - State: ctx.State.SyncWrapper(), - Changes: ctx.Plan.Changes.SyncWrapper(), - AlternateStates: alternateStates, - VariableValues: variableValues, - VariableValuesLock: new(sync.Mutex), - PlanTimestamp: ctx.Plan.Timestamp, + Operation: operation, + Meta: ctx.meta, + Config: ctx.Config, + Plugins: ctx.plugins, + State: ctx.State.SyncWrapper(), + Changes: ctx.Plan.Changes.SyncWrapper(), + AlternateStates: alternateStates, + NamedValues: namedvals.NewState(), + PlanTimestamp: ctx.Plan.Timestamp, }, ModulePath: nil, // nil for the root module InstanceKeyData: EvalDataForNoInstanceKey, From 52bafe306ac4e6f45068e1e6149d121df506ed96 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Thu, 9 Mar 2023 13:49:20 -0800 Subject: [PATCH 09/17] core: Remove variable-specific methods from EvalContext Now that we have the separate namedvals.State type to encapsulate all of the named-value tracking we can simplify the EvalContext API to just return that object directly. This removes the slightly odd evolved API for setting and retrieving input variable values, instead now just calling directly into the relevant namedvals.State methods. It also slightly simplifies some of our test code because there's no longer any need to mock accesses to what is just a temporary in-memory data store anyway. Finally, this now gives nodePartialExpandedModuleVariable somewhere to save its placeholder values, though there's not yet anything to read them. --- internal/terraform/eval_context.go | 34 ++------------ internal/terraform/eval_context_builtin.go | 25 ++-------- internal/terraform/eval_context_mock.go | 47 +++---------------- internal/terraform/eval_variable.go | 2 +- internal/terraform/eval_variable_test.go | 23 ++++----- internal/terraform/graph_walk_context.go | 2 +- internal/terraform/node_module_variable.go | 13 +++-- internal/terraform/node_root_variable.go | 2 +- internal/terraform/node_root_variable_test.go | 44 +++++------------ 9 files changed, 47 insertions(+), 145 deletions(-) diff --git a/internal/terraform/eval_context.go b/internal/terraform/eval_context.go index 396289beb360..b34508d9dd09 100644 --- a/internal/terraform/eval_context.go +++ b/internal/terraform/eval_context.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/terraform/internal/instances" "github.com/hashicorp/terraform/internal/lang" "github.com/hashicorp/terraform/internal/moduletest/mocking" + "github.com/hashicorp/terraform/internal/namedvals" "github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/providers" "github.com/hashicorp/terraform/internal/provisioners" @@ -133,35 +134,10 @@ type EvalContext interface { // addresses in this context. EvaluationScope(self addrs.Referenceable, source addrs.Referenceable, keyData InstanceKeyEvalData) *lang.Scope - // SetRootModuleArgument defines the value for one variable of the root - // module. The caller must ensure that given value is a suitable - // "final value" for the variable, which means that it's already converted - // and validated to match any configured constraints and validation rules. - // - // Calling this function multiple times with the same variable address - // will silently overwrite the value provided by a previous call. - SetRootModuleArgument(addrs.InputVariable, cty.Value) - - // SetModuleCallArgument defines the value for one input variable of a - // particular child module call. The caller must ensure that the given - // value is a suitable "final value" for the variable, which means that - // it's already converted and validated to match any configured - // constraints and validation rules. - // - // Calling this function multiple times with the same variable address - // will silently overwrite the value provided by a previous call. - SetModuleCallArgument(addrs.ModuleCallInstance, addrs.InputVariable, cty.Value) - - // GetVariableValue returns the value provided for the input variable with - // the given address, or cty.DynamicVal if the variable hasn't been assigned - // a value yet. - // - // Most callers should deal with variable values only indirectly via - // EvaluationScope and the other expression evaluation functions, but - // this is provided because variables tend to be evaluated outside of - // the context of the module they belong to and so we sometimes need to - // override the normal expression evaluation behavior. - GetVariableValue(addr addrs.AbsInputVariableInstance) cty.Value + // NamedValues returns the object that tracks the gradual evaluation of + // all input variables, local values, and output values during a graph + // walk. + NamedValues() *namedvals.State // Changes returns the writer object that can be used to write new proposed // changes into the global changes set. diff --git a/internal/terraform/eval_context_builtin.go b/internal/terraform/eval_context_builtin.go index f70c50236d07..470144c5aa26 100644 --- a/internal/terraform/eval_context_builtin.go +++ b/internal/terraform/eval_context_builtin.go @@ -59,9 +59,9 @@ type BuiltinEvalContext struct { // eval context. Evaluator *Evaluator - // NamedValues is where we keep the values of already-evaluated input + // NamedValuesValue is where we keep the values of already-evaluated input // variables, local values, and output values. - NamedValues *namedvals.State + NamedValuesValue *namedvals.State // Plugins is a library of plugin components (providers and provisioners) // available for use during a graph walk. @@ -473,25 +473,8 @@ func (ctx *BuiltinEvalContext) Path() addrs.ModuleInstance { return ctx.PathValue } -func (ctx *BuiltinEvalContext) SetRootModuleArgument(addr addrs.InputVariable, v cty.Value) { - log.Printf("[TRACE] BuiltinEvalContext: Storing final value for variable %s", addr.Absolute(addrs.RootModuleInstance)) - - absAddr := addrs.RootModuleInstance.InputVariable(addr.Name) - ctx.NamedValues.SetInputVariableValue(absAddr, v) -} - -func (ctx *BuiltinEvalContext) SetModuleCallArgument(callAddr addrs.ModuleCallInstance, varAddr addrs.InputVariable, v cty.Value) { - if !ctx.pathSet { - panic("context path not set") - } - - childPath := callAddr.ModuleInstance(ctx.PathValue) - log.Printf("[TRACE] BuiltinEvalContext: Storing final value for variable %s", varAddr.Absolute(childPath)) - ctx.NamedValues.SetInputVariableValue(childPath.InputVariable(varAddr.Name), v) -} - -func (ctx *BuiltinEvalContext) GetVariableValue(addr addrs.AbsInputVariableInstance) cty.Value { - return ctx.NamedValues.GetInputVariableValue(addr) +func (ctx *BuiltinEvalContext) NamedValues() *namedvals.State { + return ctx.NamedValuesValue } func (ctx *BuiltinEvalContext) Changes() *plans.ChangesSync { diff --git a/internal/terraform/eval_context_mock.go b/internal/terraform/eval_context_mock.go index 3d65d39c2036..3813e172c923 100644 --- a/internal/terraform/eval_context_mock.go +++ b/internal/terraform/eval_context_mock.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/terraform/internal/instances" "github.com/hashicorp/terraform/internal/lang" "github.com/hashicorp/terraform/internal/moduletest/mocking" + "github.com/hashicorp/terraform/internal/namedvals" "github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/providers" "github.com/hashicorp/terraform/internal/provisioners" @@ -121,21 +122,8 @@ type MockEvalContext struct { WithPartialExpandedPathCalled bool WithPartialExpandedPathPath addrs.PartialExpandedModule - SetRootModuleArgumentCalled bool - SetRootModuleArgumentAddr addrs.InputVariable - SetRootModuleArgumentValue cty.Value - SetRootModuleArgumentFunc func(addr addrs.InputVariable, v cty.Value) - - SetModuleCallArgumentCalled bool - SetModuleCallArgumentModuleCall addrs.ModuleCallInstance - SetModuleCallArgumentVariable addrs.InputVariable - SetModuleCallArgumentValue cty.Value - SetModuleCallArgumentFunc func(callAddr addrs.ModuleCallInstance, varAddr addrs.InputVariable, v cty.Value) - - GetVariableValueCalled bool - GetVariableValueAddr addrs.AbsInputVariableInstance - GetVariableValueValue cty.Value - GetVariableValueFunc func(addr addrs.AbsInputVariableInstance) cty.Value // supersedes GetVariableValueValue + NamedValuesCalled bool + NamedValuesState *namedvals.State ChangesCalled bool ChangesChanges *plans.ChangesSync @@ -357,32 +345,9 @@ func (c *MockEvalContext) WithPartialExpandedPath(path addrs.PartialExpandedModu return &newC } -func (c *MockEvalContext) SetRootModuleArgument(addr addrs.InputVariable, v cty.Value) { - c.SetRootModuleArgumentCalled = true - c.SetRootModuleArgumentAddr = addr - c.SetRootModuleArgumentValue = v - if c.SetRootModuleArgumentFunc != nil { - c.SetRootModuleArgumentFunc(addr, v) - } -} - -func (c *MockEvalContext) SetModuleCallArgument(callAddr addrs.ModuleCallInstance, varAddr addrs.InputVariable, v cty.Value) { - c.SetModuleCallArgumentCalled = true - c.SetModuleCallArgumentModuleCall = callAddr - c.SetModuleCallArgumentVariable = varAddr - c.SetModuleCallArgumentValue = v - if c.SetModuleCallArgumentFunc != nil { - c.SetModuleCallArgumentFunc(callAddr, varAddr, v) - } -} - -func (c *MockEvalContext) GetVariableValue(addr addrs.AbsInputVariableInstance) cty.Value { - c.GetVariableValueCalled = true - c.GetVariableValueAddr = addr - if c.GetVariableValueFunc != nil { - return c.GetVariableValueFunc(addr) - } - return c.GetVariableValueValue +func (c *MockEvalContext) NamedValues() *namedvals.State { + c.NamedValuesCalled = true + return c.NamedValuesState } func (c *MockEvalContext) Changes() *plans.ChangesSync { diff --git a/internal/terraform/eval_variable.go b/internal/terraform/eval_variable.go index 625792583911..4e58cfdcd78d 100644 --- a/internal/terraform/eval_variable.go +++ b/internal/terraform/eval_variable.go @@ -223,7 +223,7 @@ func evalVariableValidations(addr addrs.AbsInputVariableInstance, config *config // bypass our usual evaluation machinery here and just produce a minimal // evaluation context containing just the required value, and thus avoid // the problem that ctx's evaluation functions refer to the wrong module. - val := ctx.GetVariableValue(addr) + val := ctx.NamedValues().GetInputVariableValue(addr) if val == cty.NilVal { diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, diff --git a/internal/terraform/eval_variable_test.go b/internal/terraform/eval_variable_test.go index a081119de609..e9edae147c93 100644 --- a/internal/terraform/eval_variable_test.go +++ b/internal/terraform/eval_variable_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform/internal/checks" "github.com/hashicorp/terraform/internal/lang" "github.com/hashicorp/terraform/internal/lang/marks" + "github.com/hashicorp/terraform/internal/namedvals" "github.com/hashicorp/terraform/internal/tfdiags" ) @@ -1173,12 +1174,8 @@ func TestEvalVariableValidations_jsonErrorMessageEdgeCase(t *testing.T) { // We need a minimal scope to allow basic functions to be passed to // the HCL scope ctx.EvaluationScopeScope = &lang.Scope{} - ctx.GetVariableValueFunc = func(addr addrs.AbsInputVariableInstance) cty.Value { - if got, want := addr.String(), varAddr.String(); got != want { - t.Errorf("incorrect argument to GetVariableValue: got %s, want %s", got, want) - } - return test.given - } + ctx.NamedValuesState = namedvals.NewState() + ctx.NamedValuesState.SetInputVariableValue(varAddr, test.given) ctx.ChecksState = checks.NewState(cfg) ctx.ChecksState.ReportCheckableObjects(varAddr.ConfigCheckable(), addrs.MakeSet[addrs.Checkable](varAddr)) @@ -1326,15 +1323,11 @@ variable "bar" { // We need a minimal scope to allow basic functions to be passed to // the HCL scope ctx.EvaluationScopeScope = &lang.Scope{} - ctx.GetVariableValueFunc = func(addr addrs.AbsInputVariableInstance) cty.Value { - if got, want := addr.String(), varAddr.String(); got != want { - t.Errorf("incorrect argument to GetVariableValue: got %s, want %s", got, want) - } - if varCfg.Sensitive { - return test.given.Mark(marks.Sensitive) - } else { - return test.given - } + ctx.NamedValuesState = namedvals.NewState() + if varCfg.Sensitive { + ctx.NamedValuesState.SetInputVariableValue(varAddr, test.given.Mark(marks.Sensitive)) + } else { + ctx.NamedValuesState.SetInputVariableValue(varAddr, test.given) } ctx.ChecksState = checks.NewState(cfg) ctx.ChecksState.ReportCheckableObjects(varAddr.ConfigCheckable(), addrs.MakeSet[addrs.Checkable](varAddr)) diff --git a/internal/terraform/graph_walk_context.go b/internal/terraform/graph_walk_context.go index cfe04b7cba0d..edc3f9d35d62 100644 --- a/internal/terraform/graph_walk_context.go +++ b/internal/terraform/graph_walk_context.go @@ -126,7 +126,7 @@ func (w *ContextGraphWalker) EvalContext() EvalContext { ProvisionerLock: &w.provisionerLock, ChangesValue: w.Changes, ChecksValue: w.Checks, - NamedValues: w.NamedValues, + NamedValuesValue: w.NamedValues, StateValue: w.State, RefreshStateValue: w.RefreshState, PrevRunStateValue: w.PrevRunState, diff --git a/internal/terraform/node_module_variable.go b/internal/terraform/node_module_variable.go index 1a3cfe4d3860..e15c106af51e 100644 --- a/internal/terraform/node_module_variable.go +++ b/internal/terraform/node_module_variable.go @@ -209,8 +209,7 @@ func (n *nodeModuleVariable) Execute(ctx EvalContext, op walkOperation) (diags t // Set values for arguments of a child module call, for later retrieval // during expression evaluation. - _, call := n.Addr.Module.CallInstance() - ctx.SetModuleCallArgument(call, n.Addr.Variable, val) + ctx.NamedValues().SetInputVariableValue(n.Addr, val) // Skip evalVariableValidations during destroy operations. We still want // to evaluate the variable in case it is used to initialise providers @@ -380,11 +379,15 @@ func (n *nodePartialExpandedModuleVariable) Execute(ctx EvalContext, op walkOper // use. We should probably find a better way to do this at some point // because this will produce mildly-inaccurate error messages. fakeInstanceAddr := n.Addr.Module.Module().UnkeyedInstanceShim().InputVariable(n.Addr.Local.Name) - _, moreDiags := prepareFinalInputVariableValue(fakeInstanceAddr, rawVal, n.Config) + finalVal, moreDiags := prepareFinalInputVariableValue(fakeInstanceAddr, rawVal, n.Config) diags = diags.Append(moreDiags) - // TODO: Actually record the result for use elsewhere, once there's - // actually somewhere to save it. + // This placeholder represents a the value for all possible instances of + // this variable that might exist after we know the full expansion of the + // containing module, so will be used as the value of this variable for + // any evaluation we do to predict the results of these hypothetical module + // instances. + ctx.NamedValues().SetInputVariablePlaceholder(n.Addr, finalVal) return diags } diff --git a/internal/terraform/node_root_variable.go b/internal/terraform/node_root_variable.go index 3152d8427db0..29cc4b1aa2b2 100644 --- a/internal/terraform/node_root_variable.go +++ b/internal/terraform/node_root_variable.go @@ -111,7 +111,7 @@ func (n *NodeRootVariable) Execute(ctx EvalContext, op walkOperation) tfdiags.Di return diags } - ctx.SetRootModuleArgument(addr.Variable, finalVal) + ctx.NamedValues().SetInputVariableValue(addr, finalVal) if !n.DestroyApply { diags = diags.Append(evalVariableValidations( diff --git a/internal/terraform/node_root_variable_test.go b/internal/terraform/node_root_variable_test.go index d92bc9dbec61..696dee6be7e3 100644 --- a/internal/terraform/node_root_variable_test.go +++ b/internal/terraform/node_root_variable_test.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/terraform/internal/checks" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/lang" + "github.com/hashicorp/terraform/internal/namedvals" ) func TestNodeRootVariableExecute(t *testing.T) { @@ -33,18 +34,18 @@ func TestNodeRootVariableExecute(t *testing.T) { }, } + ctx.NamedValuesState = namedvals.NewState() + diags := n.Execute(ctx, walkApply) if diags.HasErrors() { t.Fatalf("unexpected error: %s", diags.Err()) } - if !ctx.SetRootModuleArgumentCalled { - t.Fatalf("ctx.SetRootModuleArgument wasn't called") - } - if got, want := ctx.SetRootModuleArgumentAddr.String(), "var.foo"; got != want { - t.Errorf("wrong address for ctx.SetRootModuleArgument\ngot: %s\nwant: %s", got, want) + absAddr := addrs.RootModuleInstance.InputVariable(n.Addr.Name) + if !ctx.NamedValues().HasInputVariableValue(absAddr) { + t.Fatalf("no result was registered") } - if got, want := ctx.SetRootModuleArgumentValue, cty.StringVal("true"); !want.RawEquals(got) { + if got, want := ctx.NamedValues().GetInputVariableValue(absAddr), cty.StringVal("true"); !want.RawEquals(got) { // NOTE: The given value was cty.Bool but the type constraint was // cty.String, so it was NodeRootVariable's responsibility to convert // as part of preparing the "final value". @@ -54,30 +55,13 @@ func TestNodeRootVariableExecute(t *testing.T) { t.Run("validation", func(t *testing.T) { ctx := new(MockEvalContext) + ctx.NamedValuesState = namedvals.NewState() + // The variable validation function gets called with Terraform's // built-in functions available, so we need a minimal scope just for // it to get the functions from. ctx.EvaluationScopeScope = &lang.Scope{} - // We need to reimplement a _little_ bit of EvalContextBuiltin logic - // here to get a similar effect with EvalContextMock just to get the - // value to flow through here in a realistic way that'll make this test - // useful. - var finalVal cty.Value - ctx.SetRootModuleArgumentFunc = func(addr addrs.InputVariable, v cty.Value) { - if addr.Name == "foo" { - t.Logf("set %s to %#v", addr.String(), v) - finalVal = v - } - } - ctx.GetVariableValueFunc = func(addr addrs.AbsInputVariableInstance) cty.Value { - if addr.String() != "var.foo" { - return cty.NilVal - } - t.Logf("reading final val for %s (%#v)", addr.String(), finalVal) - return finalVal - } - n := &NodeRootVariable{ Addr: addrs.InputVariable{Name: "foo"}, Config: &configs.Variable{ @@ -132,13 +116,11 @@ func TestNodeRootVariableExecute(t *testing.T) { t.Fatalf("unexpected error: %s", diags.Err()) } - if !ctx.SetRootModuleArgumentCalled { - t.Fatalf("ctx.SetRootModuleArgument wasn't called") - } - if got, want := ctx.SetRootModuleArgumentAddr.String(), "var.foo"; got != want { - t.Errorf("wrong address for ctx.SetRootModuleArgument\ngot: %s\nwant: %s", got, want) + absAddr := addrs.RootModuleInstance.InputVariable(n.Addr.Name) + if !ctx.NamedValues().HasInputVariableValue(absAddr) { + t.Fatalf("no result value for input variable") } - if got, want := ctx.SetRootModuleArgumentValue, cty.NumberIntVal(5); !want.RawEquals(got) { + if got, want := ctx.NamedValues().GetInputVariableValue(absAddr), cty.NumberIntVal(5); !want.RawEquals(got) { // NOTE: The given value was cty.Bool but the type constraint was // cty.String, so it was NodeRootVariable's responsibility to convert // as part of preparing the "final value". From 60fdd21bf5c71a03ada3a9b9171f295119547777 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Thu, 9 Mar 2023 14:19:52 -0800 Subject: [PATCH 10/17] core: Start of integrating "partial eval" mode into the evaluator This is a new mode for the evaluator where instead of returning information about exact objects it'll return placeholder values that represent potentially many different hypothetical objects all declared from the same static configuration object, in situations where we don't yet have enough information to expand all of the modules and their contents. So far only the GetInputVariable function actually knows how to deal with this, so this is far from sufficient but is a reasonable starting point just to establish that it's possible to get Terraform into this evaluation mode when working with graph nodes that represent such placeholder objects. --- internal/terraform/eval_context.go | 5 ----- internal/terraform/eval_context_builtin.go | 18 +++++++++++++----- internal/terraform/evaluate.go | 18 ++++++++++++++++-- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/internal/terraform/eval_context.go b/internal/terraform/eval_context.go index b34508d9dd09..b1fe06a83dd2 100644 --- a/internal/terraform/eval_context.go +++ b/internal/terraform/eval_context.go @@ -191,10 +191,5 @@ type EvalContext interface { // WithPartialExpandedPath is like WithPath but works with a // partially-expanded path, allowing for speculative evaluation inside // as-yet-unexpanded modules. - // - // TODO: This currently doesn't really do anything useful, because none - // of the other EvalContext methods pay attention to the partial-expanded - // path. We should make all of the evaluation-related methods support it - // and calculate speculative results in the partial-expanded context. WithPartialExpandedPath(path addrs.PartialExpandedModule) EvalContext } diff --git a/internal/terraform/eval_context_builtin.go b/internal/terraform/eval_context_builtin.go index 470144c5aa26..f66ad87f5d64 100644 --- a/internal/terraform/eval_context_builtin.go +++ b/internal/terraform/eval_context_builtin.go @@ -441,16 +441,21 @@ func (ctx *BuiltinEvalContext) EvaluateReplaceTriggeredBy(expr hcl.Expression, r return ref, replace, diags } -func (ctx *BuiltinEvalContext) EvaluationScope(self addrs.Referenceable, source addrs.Referenceable, keyData InstanceKeyEvalData) *lang.Scope { - if !ctx.pathSet { - panic("context path not set") - } +func (ctx *BuiltinEvalContext) EvaluationScope(self addrs.Referenceable, source addrs.Referenceable, keyData instances.RepetitionData) *lang.Scope { data := &evaluationStateData{ Evaluator: ctx.Evaluator, - ModulePath: ctx.PathValue, InstanceKeyData: keyData, Operation: ctx.Evaluator.Operation, } + switch { + case ctx.pathSet: + data.ModulePath = ctx.PathValue + case ctx.partialPathSet: + data.PartialExpandedModulePath = ctx.PartialPathValue + data.PartialEval = true + default: + panic("context path not set") + } scope := ctx.Evaluator.Scope(data, self, source) // ctx.PathValue is the path of the module that contains whatever @@ -467,6 +472,9 @@ func (ctx *BuiltinEvalContext) EvaluationScope(self addrs.Referenceable, source } func (ctx *BuiltinEvalContext) Path() addrs.ModuleInstance { + if ctx.partialPathSet { + panic(fmt.Sprintf("no module instance path during partial eval of %s", ctx.PartialPathValue)) + } if !ctx.pathSet { panic("context path not set") } diff --git a/internal/terraform/evaluate.go b/internal/terraform/evaluate.go index d304be61301f..d1e6054883b4 100644 --- a/internal/terraform/evaluate.go +++ b/internal/terraform/evaluate.go @@ -94,7 +94,16 @@ type evaluationStateData struct { // ModulePath is the path through the dynamic module tree to the module // that references will be resolved relative to. - ModulePath addrs.ModuleInstance + // + // PartialExpandedModulePath is the same but for situations when we're + // doing partial evaluation inside hypothetical instances of a + // not-yet-fully-expanded module. + // + // These two are mutually exclusive, and PartialEval is true when we're + // using PartialExpandedModulePath instead of ModulePath. + ModulePath addrs.ModuleInstance + PartialExpandedModulePath addrs.PartialExpandedModule + PartialEval bool // InstanceKeyData describes the values, if any, that are accessible due // to repetition of a containing object using "count" or "for_each" @@ -268,7 +277,12 @@ func (d *evaluationStateData) GetInputVariable(addr addrs.InputVariable, rng tfd return cty.UnknownVal(config.Type), diags } - val := d.Evaluator.NamedValues.GetInputVariableValue(d.ModulePath.InputVariable(addr.Name)) + var val cty.Value + if d.PartialEval { + val = d.Evaluator.NamedValues.GetInputVariablePlaceholder(addrs.ObjectInPartialExpandedModule(d.PartialExpandedModulePath, addr)) + } else { + val = d.Evaluator.NamedValues.GetInputVariableValue(d.ModulePath.InputVariable(addr.Name)) + } // Mark if sensitive if config.Sensitive { From 36307c2dd0ac58265ec79c81387831082f2bc2a8 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Thu, 9 Mar 2023 14:36:22 -0800 Subject: [PATCH 11/17] states: Local values no longer live in state Back when we added local values (a long time ago now!) we put their results in state mainly just because it was the only suitable shared data structure to keep them in. They are a bit ideosyncratic there because we intentionally discard them when serializing state to a snapshot, and that's just fine because they never need to be retained between runs anyway. We now have namedvals.State for all of our named value result storage needs, so we can remove the local-value-related fields of states.Module and use the relevant map inside the local value state instead. --- internal/command/jsonformat/state_test.go | 1 - internal/states/module.go | 21 +------ internal/states/state.go | 12 ---- internal/states/state_deepcopy.go | 6 -- internal/states/state_test.go | 14 +---- internal/states/sync.go | 40 ------------- internal/terraform/context_apply2_test.go | 60 ++++++++++++++----- internal/terraform/context_plan2_test.go | 14 +++-- internal/terraform/evaluate.go | 7 +-- internal/terraform/node_external_reference.go | 18 +++++- internal/terraform/node_local.go | 8 +-- internal/terraform/node_local_test.go | 35 +++++------ internal/terraform/update_state_hook_test.go | 5 +- 13 files changed, 98 insertions(+), 143 deletions(-) diff --git a/internal/command/jsonformat/state_test.go b/internal/command/jsonformat/state_test.go index 8ef37fd4c304..462b422cde29 100644 --- a/internal/command/jsonformat/state_test.go +++ b/internal/command/jsonformat/state_test.go @@ -252,7 +252,6 @@ func basicState(t *testing.T) *states.State { t.Errorf("root module is nil; want valid object") } - rootModule.SetLocalValue("foo", cty.StringVal("foo value")) rootModule.SetOutputValue("bar", cty.StringVal("bar value"), false) rootModule.SetResourceInstanceCurrent( addrs.Resource{ diff --git a/internal/states/module.go b/internal/states/module.go index edf30c7833e7..3ed2485601e9 100644 --- a/internal/states/module.go +++ b/internal/states/module.go @@ -20,10 +20,6 @@ type Module struct { // OutputValues contains the state for each output value. The keys in this // map are output value names. OutputValues map[string]*OutputValue - - // LocalValues contains the value for each named output value. The keys - // in this map are local value names. - LocalValues map[string]cty.Value } // NewModule constructs an empty module state for the given module address. @@ -32,7 +28,6 @@ func NewModule(addr addrs.ModuleInstance) *Module { Addr: addr, Resources: map[string]*Resource{}, OutputValues: map[string]*OutputValue{}, - LocalValues: map[string]cty.Value{}, } } @@ -277,19 +272,6 @@ func (ms *Module) RemoveOutputValue(name string) { delete(ms.OutputValues, name) } -// SetLocalValue writes a local value into the state, overwriting any -// existing value of the same name. -func (ms *Module) SetLocalValue(name string, value cty.Value) { - ms.LocalValues[name] = value -} - -// RemoveLocalValue removes the local value of the given name from the state, -// if it exists. This method is a no-op if there is no value of the given -// name. -func (ms *Module) RemoveLocalValue(name string) { - delete(ms.LocalValues, name) -} - // PruneResourceHusks is a specialized method that will remove any Resource // objects that do not contain any instances, even if they have an EachMode. // @@ -319,6 +301,5 @@ func (ms *Module) empty() bool { // This must be updated to cover any new collections added to Module // in future. return (len(ms.Resources) == 0 && - len(ms.OutputValues) == 0 && - len(ms.LocalValues) == 0) + len(ms.OutputValues) == 0) } diff --git a/internal/states/state.go b/internal/states/state.go index 20e25341c964..497c299f0841 100644 --- a/internal/states/state.go +++ b/internal/states/state.go @@ -7,8 +7,6 @@ import ( "fmt" "sort" - "github.com/zclconf/go-cty/cty" - "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/getproviders" ) @@ -316,16 +314,6 @@ func (s *State) OutputValue(addr addrs.AbsOutputValue) *OutputValue { return ms.OutputValues[addr.OutputValue.Name] } -// LocalValue returns the value of the named local value with the given address, -// or cty.NilVal if no such value is tracked in the state. -func (s *State) LocalValue(addr addrs.AbsLocalValue) cty.Value { - ms := s.Module(addr.Module) - if ms == nil { - return cty.NilVal - } - return ms.LocalValues[addr.LocalValue.Name] -} - // ProviderAddrs returns a list of all of the provider configuration addresses // referenced throughout the receiving state. // diff --git a/internal/states/state_deepcopy.go b/internal/states/state_deepcopy.go index 834a9e13da12..156a644a05f4 100644 --- a/internal/states/state_deepcopy.go +++ b/internal/states/state_deepcopy.go @@ -58,17 +58,11 @@ func (ms *Module) DeepCopy() *Module { for k, v := range ms.OutputValues { outputValues[k] = v.DeepCopy() } - localValues := make(map[string]cty.Value, len(ms.LocalValues)) - for k, v := range ms.LocalValues { - // cty.Value is immutable, so we don't need to copy these. - localValues[k] = v - } return &Module{ Addr: ms.Addr, // technically mutable, but immutable by convention Resources: resources, OutputValues: outputValues, - LocalValues: localValues, } } diff --git a/internal/states/state_test.go b/internal/states/state_test.go index 1f79ff61e080..c1120823003a 100644 --- a/internal/states/state_test.go +++ b/internal/states/state_test.go @@ -27,7 +27,6 @@ func TestState(t *testing.T) { t.Errorf("root module is nil; want valid object") } - rootModule.SetLocalValue("foo", cty.StringVal("foo value")) rootModule.SetOutputValue("bar", cty.StringVal("bar value"), false) rootModule.SetOutputValue("secret", cty.StringVal("secret value"), true) rootModule.SetResourceInstanceCurrent( @@ -58,9 +57,6 @@ func TestState(t *testing.T) { Modules: map[string]*Module{ "": { Addr: addrs.RootModuleInstance, - LocalValues: map[string]cty.Value{ - "foo": cty.StringVal("foo value"), - }, OutputValues: map[string]*OutputValue{ "bar": { Addr: addrs.AbsOutputValue{ @@ -107,8 +103,7 @@ func TestState(t *testing.T) { }, }, "module.child": { - Addr: addrs.RootModuleInstance.Child("child", addrs.NoKey), - LocalValues: map[string]cty.Value{}, + Addr: addrs.RootModuleInstance.Child("child", addrs.NoKey), OutputValues: map[string]*OutputValue{ "pizza": { Addr: addrs.AbsOutputValue{ @@ -124,8 +119,7 @@ func TestState(t *testing.T) { Resources: map[string]*Resource{}, }, `module.multi["a"]`: { - Addr: addrs.RootModuleInstance.Child("multi", addrs.StringKey("a")), - LocalValues: map[string]cty.Value{}, + Addr: addrs.RootModuleInstance.Child("multi", addrs.StringKey("a")), OutputValues: map[string]*OutputValue{ "pizza": { Addr: addrs.AbsOutputValue{ @@ -141,8 +135,7 @@ func TestState(t *testing.T) { Resources: map[string]*Resource{}, }, `module.multi["b"]`: { - Addr: addrs.RootModuleInstance.Child("multi", addrs.StringKey("b")), - LocalValues: map[string]cty.Value{}, + Addr: addrs.RootModuleInstance.Child("multi", addrs.StringKey("b")), OutputValues: map[string]*OutputValue{ "pizza": { Addr: addrs.AbsOutputValue{ @@ -231,7 +224,6 @@ func TestStateDeepCopy(t *testing.T) { t.Errorf("root module is nil; want valid object") } - rootModule.SetLocalValue("foo", cty.StringVal("foo value")) rootModule.SetOutputValue("bar", cty.StringVal("bar value"), false) rootModule.SetOutputValue("secret", cty.StringVal("secret value"), true) rootModule.SetResourceInstanceCurrent( diff --git a/internal/states/sync.go b/internal/states/sync.go index b82af1c10f25..8b61ce462870 100644 --- a/internal/states/sync.go +++ b/internal/states/sync.go @@ -117,46 +117,6 @@ func (s *SyncState) RemoveOutputValue(addr addrs.AbsOutputValue) { s.maybePruneModule(addr.Module) } -// LocalValue returns the current value associated with the given local value -// address. -func (s *SyncState) LocalValue(addr addrs.AbsLocalValue) cty.Value { - s.lock.RLock() - // cty.Value is immutable, so we don't need any extra copying here. - ret := s.state.LocalValue(addr) - s.lock.RUnlock() - return ret -} - -// SetLocalValue writes a given output value into the state, overwriting -// any existing value of the same name. -// -// If the module containing the local value is not yet tracked in state then it -// will be added as a side-effect. -func (s *SyncState) SetLocalValue(addr addrs.AbsLocalValue, value cty.Value) { - s.lock.Lock() - defer s.lock.Unlock() - - ms := s.state.EnsureModule(addr.Module) - ms.SetLocalValue(addr.LocalValue.Name, value) -} - -// RemoveLocalValue removes the stored value for the local value with the -// given address. -// -// If this results in its containing module being empty, the module will be -// pruned from the state as a side-effect. -func (s *SyncState) RemoveLocalValue(addr addrs.AbsLocalValue) { - s.lock.Lock() - defer s.lock.Unlock() - - ms := s.state.Module(addr.Module) - if ms == nil { - return - } - ms.RemoveLocalValue(addr.LocalValue.Name) - s.maybePruneModule(addr.Module) -} - // Resource returns a snapshot of the state of the resource with the given // address, or nil if no such resource is tracked. // diff --git a/internal/terraform/context_apply2_test.go b/internal/terraform/context_apply2_test.go index a7f1929e9055..2d102340f7f8 100644 --- a/internal/terraform/context_apply2_test.go +++ b/internal/terraform/context_apply2_test.go @@ -2269,16 +2269,27 @@ locals { t.Errorf("expected no errors, but got %s", diags) } - state, diags := ctx.Apply(plan, m, nil) - if diags.HasErrors() { - t.Errorf("expected no errors, but got %s", diags) + g, _, diags := ctx.applyGraph(plan, m, &ApplyOpts{}, true) + // The local value should've been pruned from the graph because nothing + // refers to it. + gotGraph := g.String() + wantGraph := `provider["registry.terraform.io/hashicorp/test"] +provider["registry.terraform.io/hashicorp/test"] (close) + test_object.a +root + provider["registry.terraform.io/hashicorp/test"] (close) +test_object.a + test_object.a (expand) +test_object.a (expand) + provider["registry.terraform.io/hashicorp/test"] +` + if diff := cmp.Diff(wantGraph, gotGraph); diff != "" { + t.Errorf("wrong apply graph\n%s", diff) } - // We didn't specify any external references, so the unreferenced local - // value should have been tidied up and never made it into the state. - module := state.RootModule() - if len(module.LocalValues) > 0 { - t.Errorf("expected no local values in the state but found %d", len(module.LocalValues)) + _, diags = ctx.Apply(plan, m, nil) + if diags.HasErrors() { + t.Errorf("expected no errors, but got %s", diags) } } @@ -2312,17 +2323,36 @@ locals { t.Errorf("expected no errors, but got %s", diags) } - state, diags := ctx.Apply(plan, m, nil) + g, _, diags := ctx.applyGraph(plan, m, &ApplyOpts{}, true) if diags.HasErrors() { t.Errorf("expected no errors, but got %s", diags) } - // We did specify the local value in the external references, so it should - // have been preserved even though it is not referenced by anything directly - // in the config. - module := state.RootModule() - if module.LocalValues["local_value"].AsString() != "foo" { - t.Errorf("expected local value to be \"foo\" but was \"%s\"", module.LocalValues["local_value"].AsString()) + // The local value should remain in the graph because the external + // reference uses it. + gotGraph := g.String() + wantGraph := ` + local.local_value (expand) +local.local_value (expand) + test_object.a +provider["registry.terraform.io/hashicorp/test"] +provider["registry.terraform.io/hashicorp/test"] (close) + test_object.a +root + + provider["registry.terraform.io/hashicorp/test"] (close) +test_object.a + test_object.a (expand) +test_object.a (expand) + provider["registry.terraform.io/hashicorp/test"] +` + if diff := cmp.Diff(wantGraph, gotGraph); diff != "" { + t.Errorf("wrong graph\n%s", diff) + } + + _, diags = ctx.Apply(plan, m, nil) + if diags.HasErrors() { + t.Errorf("expected no errors, but got %s", diags) } } diff --git a/internal/terraform/context_plan2_test.go b/internal/terraform/context_plan2_test.go index 6177883c29c1..98bb4336a372 100644 --- a/internal/terraform/context_plan2_test.go +++ b/internal/terraform/context_plan2_test.go @@ -4234,6 +4234,10 @@ resource "test_object" "a" { locals { local_value = test_object.a.test_string } + +output "from_local_value" { + value = local.local_value +} `, }) @@ -4253,20 +4257,20 @@ locals { module := state.RootModule() // So, the original state shouldn't have been updated at all. - if len(module.LocalValues) > 0 { - t.Errorf("expected no local values in the state but found %d", len(module.LocalValues)) + if len(state.RootOutputValues) > 0 { + t.Errorf("expected no root output values in the state but found %d", len(state.RootOutputValues)) } if len(module.Resources) > 0 { - t.Errorf("expected no resources in the state but found %d", len(module.LocalValues)) + t.Errorf("expected no resources in the state but found %d", len(module.Resources)) } // But, this makes it hard for the testing framework to valid things about // the returned plan. So, the plan contains the planned state: module = plan.PlannedState.RootModule() - if module.LocalValues["local_value"].AsString() != "foo" { - t.Errorf("expected local value to be \"foo\" but was \"%s\"", module.LocalValues["local_value"].AsString()) + if got, want := plan.PlannedState.RootOutputValues["from_local_value"].Value.AsString(), "foo"; got != want { + t.Errorf("expected local value to be %q but was %q", want, got) } if module.ResourceInstance(addr.Resource).Current.Status != states.ObjectPlanned { diff --git a/internal/terraform/evaluate.go b/internal/terraform/evaluate.go index d1e6054883b4..51d7531605c8 100644 --- a/internal/terraform/evaluate.go +++ b/internal/terraform/evaluate.go @@ -324,12 +324,7 @@ func (d *evaluationStateData) GetLocalValue(addr addrs.LocalValue, rng tfdiags.S return cty.DynamicVal, diags } - val := d.Evaluator.State.LocalValue(addr.Absolute(d.ModulePath)) - if val == cty.NilVal { - // Not evaluated yet? - val = cty.DynamicVal - } - + val := d.Evaluator.NamedValues.GetLocalValue(addr.Absolute(d.ModulePath)) return val, diags } diff --git a/internal/terraform/node_external_reference.go b/internal/terraform/node_external_reference.go index 59ac112a11a8..8050d15e3fa3 100644 --- a/internal/terraform/node_external_reference.go +++ b/internal/terraform/node_external_reference.go @@ -3,7 +3,13 @@ package terraform -import "github.com/hashicorp/terraform/internal/addrs" +import ( + "fmt" + "sort" + "strings" + + "github.com/hashicorp/terraform/internal/addrs" +) // nodeExternalReference allows external callers (such as the testing framework) // to provide the list of references they are making into the graph. This @@ -32,3 +38,13 @@ func (n *nodeExternalReference) ModulePath() addrs.Module { func (n *nodeExternalReference) References() []*addrs.Reference { return n.ExternalReferences } + +// Name implements dag.NamedVertex +func (n *nodeExternalReference) Name() string { + names := make([]string, len(n.ExternalReferences)) + for i, ref := range n.ExternalReferences { + names[i] = ref.DisplayString() + } + sort.Strings(names) + return fmt.Sprintf("", strings.Join(names, ", ")) +} diff --git a/internal/terraform/node_local.go b/internal/terraform/node_local.go index 7d955e514f53..ad5943cab95f 100644 --- a/internal/terraform/node_local.go +++ b/internal/terraform/node_local.go @@ -161,13 +161,7 @@ func (n *NodeLocal) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Di return diags } - state := ctx.State() - if state == nil { - diags = diags.Append(fmt.Errorf("cannot write local value to nil state")) - return diags - } - - state.SetLocalValue(addr.Absolute(ctx.Path()), val) + ctx.NamedValues().SetLocalValue(addr.Absolute(ctx.Path()), val) return diags } diff --git a/internal/terraform/node_local_test.go b/internal/terraform/node_local_test.go index 6e11d74f7d6d..ba28c118e99d 100644 --- a/internal/terraform/node_local_test.go +++ b/internal/terraform/node_local_test.go @@ -4,17 +4,15 @@ package terraform import ( - "reflect" "testing" - "github.com/davecgh/go-spew/spew" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" - "github.com/zclconf/go-cty/cty" "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/configs/hcl2shim" + "github.com/hashicorp/terraform/internal/namedvals" "github.com/hashicorp/terraform/internal/states" ) @@ -48,14 +46,16 @@ func TestNodeLocalExecute(t *testing.T) { t.Fatal(diags.Error()) } + localAddr := addrs.LocalValue{Name: "foo"}.Absolute(addrs.RootModuleInstance) n := &NodeLocal{ - Addr: addrs.LocalValue{Name: "foo"}.Absolute(addrs.RootModuleInstance), + Addr: localAddr, Config: &configs.Local{ Expr: expr, }, } ctx := &MockEvalContext{ - StateState: states.NewState().SyncWrapper(), + StateState: states.NewState().SyncWrapper(), + NamedValuesState: namedvals.NewState(), EvaluateExprResult: hcl2shim.HCL2ValueFromConfigValue(test.Want), } @@ -69,18 +69,19 @@ func TestNodeLocalExecute(t *testing.T) { } } - ms := ctx.StateState.Module(addrs.RootModuleInstance) - gotLocals := ms.LocalValues - wantLocals := map[string]cty.Value{} - if test.Want != nil { - wantLocals["foo"] = hcl2shim.HCL2ValueFromConfigValue(test.Want) - } - - if !reflect.DeepEqual(gotLocals, wantLocals) { - t.Errorf( - "wrong locals after Eval\ngot: %swant: %s", - spew.Sdump(gotLocals), spew.Sdump(wantLocals), - ) + if test.Err { + if ctx.NamedValues().HasLocalValue(localAddr) { + t.Errorf("have value for %s, but wanted none", localAddr) + } + } else { + if !ctx.NamedValues().HasLocalValue(localAddr) { + t.Fatalf("no value for %s", localAddr) + } + got := ctx.NamedValues().GetLocalValue(localAddr) + want := hcl2shim.HCL2ValueFromConfigValue(test.Want) + if !want.RawEquals(got) { + t.Errorf("wrong value for %s\ngot: %#v\nwant: %#v", localAddr, got, want) + } } }) } diff --git a/internal/terraform/update_state_hook_test.go b/internal/terraform/update_state_hook_test.go index efca41e82e98..01b6ee662bd2 100644 --- a/internal/terraform/update_state_hook_test.go +++ b/internal/terraform/update_state_hook_test.go @@ -17,7 +17,8 @@ func TestUpdateStateHook(t *testing.T) { mockHook := new(MockHook) state := states.NewState() - state.Module(addrs.RootModuleInstance).SetLocalValue("foo", cty.StringVal("hello")) + state.Module(addrs.RootModuleInstance).SetOutputValue("foo", cty.StringVal("hello"), false) + state.Module(addrs.RootModuleInstance) ctx := new(MockEvalContext) ctx.HookHook = mockHook @@ -30,7 +31,7 @@ func TestUpdateStateHook(t *testing.T) { if !mockHook.PostStateUpdateCalled { t.Fatal("should call PostStateUpdate") } - if mockHook.PostStateUpdateState.LocalValue(addrs.LocalValue{Name: "foo"}.Absolute(addrs.RootModuleInstance)) != cty.StringVal("hello") { + if os := mockHook.PostStateUpdateState.OutputValue(addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance)); os != nil && os.Value != cty.StringVal("hello") { t.Fatalf("wrong state passed to hook: %s", spew.Sdump(mockHook.PostStateUpdateState)) } } From 73a82df6fefa8e10ba03aa16c1559e00bfd07fdb Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Thu, 9 Mar 2023 16:18:34 -0800 Subject: [PATCH 12/17] core: Evaluate placeholders for local values in unexpanded modules For any local value declared beneath a module call whose expansion isn't known yet, we'll calculate a single value to serve as a placeholder for all possible valid instances of that local value, using unknown values in any situation where a value might differ between instances. --- internal/terraform/evaluate.go | 54 +++++++++++---- internal/terraform/node_local.go | 80 +++++++++++++++++++++++ internal/terraform/transform_reference.go | 4 -- 3 files changed, 120 insertions(+), 18 deletions(-) diff --git a/internal/terraform/evaluate.go b/internal/terraform/evaluate.go index 51d7531605c8..c28fcd1b7669 100644 --- a/internal/terraform/evaluate.go +++ b/internal/terraform/evaluate.go @@ -117,6 +117,27 @@ type evaluationStateData struct { Operation walkOperation } +func (d *evaluationStateData) staticModulePath() addrs.Module { + if d.PartialEval { + return d.PartialExpandedModulePath.Module() + } else { + return d.ModulePath.Module() + } +} + +func (d *evaluationStateData) modulePathDisplayAddr() string { + var ret string + if d.PartialEval { + ret = d.PartialExpandedModulePath.String() + } else { + ret = d.ModulePath.String() + } + if ret == "" { + return "root module" + } + return ret +} + // InstanceKeyEvalData is the old name for instances.RepetitionData, aliased // here for compatibility. In new code, use instances.RepetitionData instead. type InstanceKeyEvalData = instances.RepetitionData @@ -227,11 +248,11 @@ func (d *evaluationStateData) GetInputVariable(addr addrs.InputVariable, rng tfd // First we'll make sure the requested value is declared in configuration, // so we can produce a nice message if not. - moduleConfig := d.Evaluator.Config.DescendentForInstance(d.ModulePath) + moduleConfig := d.Evaluator.Config.Descendent(d.staticModulePath()) if moduleConfig == nil { // should never happen, since we can't be evaluating in a module // that wasn't mentioned in configuration. - panic(fmt.Sprintf("input variable read from %s, which has no configuration", d.ModulePath)) + panic(fmt.Sprintf("input variable read from %s, which has no configuration", d.modulePathDisplayAddr())) } config := moduleConfig.Module.Variables[addr.Name] @@ -297,11 +318,11 @@ func (d *evaluationStateData) GetLocalValue(addr addrs.LocalValue, rng tfdiags.S // First we'll make sure the requested value is declared in configuration, // so we can produce a nice message if not. - moduleConfig := d.Evaluator.Config.DescendentForInstance(d.ModulePath) + moduleConfig := d.Evaluator.Config.Descendent(d.staticModulePath()) if moduleConfig == nil { // should never happen, since we can't be evaluating in a module // that wasn't mentioned in configuration. - panic(fmt.Sprintf("local value read from %s, which has no configuration", d.ModulePath)) + panic(fmt.Sprintf("local value read from %s, which has no configuration", d.modulePathDisplayAddr())) } config := moduleConfig.Module.Locals[addr.Name] @@ -324,7 +345,13 @@ func (d *evaluationStateData) GetLocalValue(addr addrs.LocalValue, rng tfdiags.S return cty.DynamicVal, diags } - val := d.Evaluator.NamedValues.GetLocalValue(addr.Absolute(d.ModulePath)) + var val cty.Value + if d.PartialEval { + val = d.Evaluator.NamedValues.GetLocalValuePlaceholder(addrs.ObjectInPartialExpandedModule(d.PartialExpandedModulePath, addr)) + } else { + val = d.Evaluator.NamedValues.GetLocalValue(addr.Absolute(d.ModulePath)) + } + return val, diags } @@ -333,9 +360,9 @@ func (d *evaluationStateData) GetModule(addr addrs.ModuleCall, rng tfdiags.Sourc // Output results live in the module that declares them, which is one of // the child module instances of our current module path. - moduleAddr := d.ModulePath.Module().Child(addr.Name) + moduleAddr := d.staticModulePath().Child(addr.Name) - parentCfg := d.Evaluator.Config.DescendentForInstance(d.ModulePath) + parentCfg := d.Evaluator.Config.Descendent(d.staticModulePath()) callConfig, ok := parentCfg.Module.ModuleCalls[addr.Name] if !ok { diags = diags.Append(&hcl.Diagnostic{ @@ -590,11 +617,11 @@ func (d *evaluationStateData) GetPathAttr(addr addrs.PathAttr, rng tfdiags.Sourc return cty.StringVal(filepath.ToSlash(wd)), diags case "module": - moduleConfig := d.Evaluator.Config.DescendentForInstance(d.ModulePath) + moduleConfig := d.Evaluator.Config.Descendent(d.staticModulePath()) if moduleConfig == nil { // should never happen, since we can't be evaluating in a module // that wasn't mentioned in configuration. - panic(fmt.Sprintf("module.path read from module %s, which has no configuration", d.ModulePath)) + panic(fmt.Sprintf("module.path read from module %s, which has no configuration", d.modulePathDisplayAddr())) } sourceDir := moduleConfig.Module.SourceDir return cty.StringVal(filepath.ToSlash(sourceDir)), diags @@ -622,12 +649,11 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc var diags tfdiags.Diagnostics // First we'll consult the configuration to see if an resource of this // name is declared at all. - moduleAddr := d.ModulePath - moduleConfig := d.Evaluator.Config.DescendentForInstance(moduleAddr) + moduleConfig := d.Evaluator.Config.Descendent(d.staticModulePath()) if moduleConfig == nil { // should never happen, since we can't be evaluating in a module // that wasn't mentioned in configuration. - panic(fmt.Sprintf("resource value read from %s, which has no configuration", moduleAddr)) + panic(fmt.Sprintf("resource value read from %s, which has no configuration", d.modulePathDisplayAddr())) } config := moduleConfig.Module.ResourceByAddr(addr) @@ -635,7 +661,7 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: `Reference to undeclared resource`, - Detail: fmt.Sprintf(`A resource %q %q has not been declared in %s`, addr.Type, addr.Name, moduleDisplayAddr(moduleAddr)), + Detail: fmt.Sprintf(`A resource %q %q has not been declared in %s`, addr.Type, addr.Name, d.modulePathDisplayAddr()), Subject: rng.ToHCL().Ptr(), }) return cty.DynamicVal, diags @@ -680,7 +706,7 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc // (since a planned destroy cannot yet remove root outputs), we // need to return a dynamic value here to allow evaluation to // continue. - log.Printf("[ERROR] unknown instance %q referenced during %s", addr.Absolute(d.ModulePath), d.Operation) + log.Printf("[ERROR] unknown instance %s in %s referenced during %s", addr, d.modulePathDisplayAddr(), d.Operation) return cty.DynamicVal, diags } diff --git a/internal/terraform/node_local.go b/internal/terraform/node_local.go index ad5943cab95f..a8237210d28e 100644 --- a/internal/terraform/node_local.go +++ b/internal/terraform/node_local.go @@ -77,6 +77,14 @@ func (n *nodeExpandLocal) DynamicExpand(ctx EvalContext) (*Graph, tfdiags.Diagno log.Printf("[TRACE] Expanding local: adding %s as %T", o.Addr.String(), o) g.Add(o) } + for _, module := range expander.UnknownModuleInstances(n.Module) { + o := &nodeLocalUnexpandedPlaceholder{ + Addr: addrs.ObjectInPartialExpandedModule(module, n.Addr), + Config: n.Config, + } + log.Printf("[TRACE] Expanding local: adding %s as %T", o.Addr.String(), o) + g.Add(o) + } addRootNodeToGraph(&g) return &g, nil } @@ -176,3 +184,75 @@ func (n *NodeLocal) DotNode(name string, opts *dag.DotOpts) *dag.DotNode { }, } } + +type nodeLocalUnexpandedPlaceholder struct { + Addr addrs.InPartialExpandedModule[addrs.LocalValue] + Config *configs.Local +} + +var ( + _ GraphNodePartialExpandedModule = (*nodeLocalUnexpandedPlaceholder)(nil) + _ GraphNodeReferenceable = (*nodeLocalUnexpandedPlaceholder)(nil) + _ GraphNodeReferencer = (*nodeLocalUnexpandedPlaceholder)(nil) + _ GraphNodeExecutable = (*nodeLocalUnexpandedPlaceholder)(nil) + _ graphNodeTemporaryValue = (*nodeLocalUnexpandedPlaceholder)(nil) +) + +// PartialExpandedModule implements GraphNodePartialExpandedModule +func (n *nodeLocalUnexpandedPlaceholder) PartialExpandedModule() addrs.PartialExpandedModule { + return n.Addr.Module +} + +func (n *nodeLocalUnexpandedPlaceholder) Name() string { + return n.Addr.String() +} + +// graphNodeTemporaryValue +func (n *nodeLocalUnexpandedPlaceholder) temporaryValue() bool { + return true +} + +// GraphNodeReferenceable +func (n *nodeLocalUnexpandedPlaceholder) ReferenceableAddrs() []addrs.Referenceable { + return []addrs.Referenceable{n.Addr.Local} +} + +// GraphNodeReferencer +func (n *nodeLocalUnexpandedPlaceholder) References() []*addrs.Reference { + refs, _ := lang.ReferencesInExpr(addrs.ParseRef, n.Config.Expr) + return refs +} + +// Execute implements GraphNodeExecutable +func (n *nodeLocalUnexpandedPlaceholder) Execute(ctx EvalContext, op walkOperation) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + expr := n.Config.Expr + + // We ignore diags here because any problems we might find will be found + // again in EvaluateExpr below. + refs, _ := lang.ReferencesInExpr(addrs.ParseRef, expr) + for _, ref := range refs { + if ref.Subject == n.Addr.Local { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Self-referencing local value", + Detail: fmt.Sprintf("Local value %s cannot use its own result as part of its expression.", n.Addr), + Subject: ref.SourceRange.ToHCL().Ptr(), + Context: expr.Range().Ptr(), + }) + } + } + if diags.HasErrors() { + return diags + } + + val, moreDiags := ctx.EvaluateExpr(expr, cty.DynamicPseudoType, nil) + diags = diags.Append(moreDiags) + if moreDiags.HasErrors() { + return diags + } + + ctx.NamedValues().SetLocalValuePlaceholder(n.Addr, val) + + return diags +} diff --git a/internal/terraform/transform_reference.go b/internal/terraform/transform_reference.go index a6f4a82ef0d5..86f79a827285 100644 --- a/internal/terraform/transform_reference.go +++ b/internal/terraform/transform_reference.go @@ -24,8 +24,6 @@ import ( // be referenced and other methods of referencing may still be possible (such // as by path!) type GraphNodeReferenceable interface { - GraphNodeModulePath - // ReferenceableAddrs returns a list of addresses through which this can be // referenced. ReferenceableAddrs() []addrs.Referenceable @@ -34,8 +32,6 @@ type GraphNodeReferenceable interface { // GraphNodeReferencer must be implemented by nodes that reference other // Terraform items and therefore depend on them. type GraphNodeReferencer interface { - GraphNodeModulePath - // References returns a list of references made by this node, which // include both a referenced address and source location information for // the reference. From 33e909ea1871b27212c36cc3bd1ead57448d9a59 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Thu, 9 Mar 2023 19:26:49 -0800 Subject: [PATCH 13/17] states: Only track root module output values For a very long time we've had an annoying discrepancy between the in-memory state model and our state snapshot format where the in-memory format stores output values for all modules whereas the snapshot format only tracks the root module output values because those are all we actually need to preserve between runs. That design wart was a result of us using the state both as an internal and an external artifact, due to having nowhere else to store the transient values of non-root module output values while Terraform Core does its work. We now have namedvals.State to internally track all of the throwaway results from named values that don't need to persist between runs, so now we'll use that for our internal work instead and reserve the states.State model only for the data that we will preserve between runs in state snapshots. The namedvals internal model isn't really designed to support enumerating all of the output values for a particular module call, but our expression evaluator currently depends on being able to do that and so we have a temporary inefficient implementation of that which just scans the entire table of values as a stopgap just to avoid this commit growing even larger than it already is. In a future commit we'll rework the evaluator to support the PartialEval mode and at the same time move the responsiblity for enumerating all of the output values into the evaluator itself, since it should be able to determine what it's expecting by analyzing the configuration rather than just by trusting that earlier evaluation has completed correctly. Because our legacy state string serialization previously included output values for all modules, some of our context tests were accidentally depending on the implementation detail of how those got stored internally. Those tests are updated here to test only the data that is a real part of Terraform Core's result, by ensuring that the relevant data appears somewhere either in a root output value or in a resource attribute. As of this commit, what remains of the states.State model can now be entirely serialized by the state snapshot format, with no more situations where we just silently drop some data that Terraform Core uses as an implementation detail. --- internal/backend/remote/backend_state.go | 2 +- .../providers/terraform/data_source_state.go | 7 +- internal/cloud/state.go | 4 +- internal/command/apply.go | 2 +- internal/command/command_test.go | 7 +- internal/command/e2etest/primary_test.go | 4 +- internal/command/e2etest/terraform_test.go | 3 +- internal/command/jsonformat/state_test.go | 49 ++++-- internal/command/jsonstate/state.go | 4 +- internal/command/meta_backend_test.go | 10 +- internal/command/refresh.go | 2 +- internal/command/show_test.go | 15 +- internal/namedvals/state.go | 48 ++++++ internal/namedvals/values.go | 4 + .../internal/stackeval/component_instance.go | 2 +- internal/states/module.go | 38 +---- internal/states/remote/state.go | 2 +- internal/states/remote/state_test.go | 10 +- internal/states/state.go | 89 +++++------ internal/states/state_deepcopy.go | 18 +-- internal/states/state_string.go | 20 ++- internal/states/state_test.go | 141 +++++++----------- internal/states/statefile/version4.go | 5 +- internal/states/statemgr/filesystem.go | 2 +- internal/states/statemgr/statemgr_fake.go | 2 +- internal/states/statemgr/testing.go | 21 ++- internal/states/sync.go | 38 ++--- internal/terraform/context_apply2_test.go | 7 +- internal/terraform/context_apply_test.go | 91 ++++++----- internal/terraform/context_plan_test.go | 6 +- internal/terraform/context_refresh_test.go | 5 +- internal/terraform/evaluate.go | 12 +- internal/terraform/evaluate_test.go | 21 ++- internal/terraform/node_output.go | 30 +++- internal/terraform/node_output_test.go | 5 +- internal/terraform/transform_orphan_output.go | 32 +--- internal/terraform/update_state_hook_test.go | 6 +- 37 files changed, 405 insertions(+), 359 deletions(-) diff --git a/internal/backend/remote/backend_state.go b/internal/backend/remote/backend_state.go index 6c90f7f4be11..b397c6f07cdd 100644 --- a/internal/backend/remote/backend_state.go +++ b/internal/backend/remote/backend_state.go @@ -98,7 +98,7 @@ func (r *remoteClient) Put(state []byte) error { return fmt.Errorf("error reading state: %s", err) } - ov, err := jsonstate.MarshalOutputs(stateFile.State.RootModule().OutputValues) + ov, err := jsonstate.MarshalOutputs(stateFile.State.RootOutputValues) if err != nil { return fmt.Errorf("error reading output values: %s", err) } diff --git a/internal/builtin/providers/terraform/data_source_state.go b/internal/builtin/providers/terraform/data_source_state.go index 82b39d1e3440..77e1c77d83fc 100644 --- a/internal/builtin/providers/terraform/data_source_state.go +++ b/internal/builtin/providers/terraform/data_source_state.go @@ -169,11 +169,8 @@ func dataSourceRemoteStateRead(d cty.Value) (cty.Value, tfdiags.Diagnostics) { newState["outputs"] = cty.EmptyObjectVal return cty.ObjectVal(newState), diags } - mod := remoteState.RootModule() - if mod != nil { // should always have a root module in any valid state - for k, os := range mod.OutputValues { - outputs[k] = os.Value - } + for k, os := range remoteState.RootOutputValues { + outputs[k] = os.Value } newState["outputs"] = cty.ObjectVal(outputs) diff --git a/internal/cloud/state.go b/internal/cloud/state.go index 55e37d86346d..54e0def19f86 100644 --- a/internal/cloud/state.go +++ b/internal/cloud/state.go @@ -220,7 +220,7 @@ func (s *State) PersistState(schemas *terraform.Schemas) error { return fmt.Errorf("failed to read state: %w", err) } - ov, err := jsonstate.MarshalOutputs(stateFile.State.RootModule().OutputValues) + ov, err := jsonstate.MarshalOutputs(stateFile.State.RootOutputValues) if err != nil { return fmt.Errorf("failed to translate outputs: %w", err) } @@ -547,7 +547,7 @@ func (s *State) GetRootOutputValues() (map[string]*states.OutputValue, error) { return nil, ErrStateVersionUnauthorizedUpgradeState } - return state.RootModule().OutputValues, nil + return state.RootOutputValues, nil } if output.Sensitive { diff --git a/internal/command/apply.go b/internal/command/apply.go index 4a7ced21203d..e3ea24c86901 100644 --- a/internal/command/apply.go +++ b/internal/command/apply.go @@ -137,7 +137,7 @@ func (c *ApplyCommand) Run(rawArgs []string) int { if rb, isRemoteBackend := be.(BackendWithRemoteTerraformVersion); !isRemoteBackend || rb.IsLocalOperations() { view.ResourceCount(args.State.StateOutPath) if !c.Destroy && op.State != nil { - view.Outputs(op.State.RootModule().OutputValues) + view.Outputs(op.State.RootOutputValues) } } diff --git a/internal/command/command_test.go b/internal/command/command_test.go index 07a6872bab78..eafe5befdd5c 100644 --- a/internal/command/command_test.go +++ b/internal/command/command_test.go @@ -355,7 +355,10 @@ func testStateMgrCurrentLineage(mgr statemgr.Persistent) string { // // (do stuff to the state) // assertStateHasMarker(state, mark) func markStateForMatching(state *states.State, mark string) string { - state.RootModule().SetOutputValue("testing_mark", cty.StringVal(mark), false) + state.SetOutputValue( + addrs.OutputValue{Name: "testing_mark"}.Absolute(addrs.RootModuleInstance), + cty.StringVal(mark), false, + ) return mark } @@ -363,7 +366,7 @@ func markStateForMatching(state *states.State, mark string) string { // mark string previously added to the given state. If no such mark is present, // the result is an empty string. func getStateMatchingMarker(state *states.State) string { - os := state.RootModule().OutputValues["testing_mark"] + os := state.RootOutputValues["testing_mark"] if os == nil { return "" } diff --git a/internal/command/e2etest/primary_test.go b/internal/command/e2etest/primary_test.go index 26252850b0e4..c7b6eaa1ee24 100644 --- a/internal/command/e2etest/primary_test.go +++ b/internal/command/e2etest/primary_test.go @@ -204,13 +204,13 @@ func TestPrimaryChdirOption(t *testing.T) { t.Fatalf("failed to read state file: %s", err) } - gotOutput := state.RootModule().OutputValues["cwd"] + gotOutput := state.RootOutputValues["cwd"] wantOutputValue := cty.StringVal(filepath.ToSlash(tf.Path())) // path.cwd returns the original path, because path.root is how we get the overridden path if gotOutput == nil || !wantOutputValue.RawEquals(gotOutput.Value) { t.Errorf("incorrect value for cwd output\ngot: %#v\nwant Value: %#v", gotOutput, wantOutputValue) } - gotOutput = state.RootModule().OutputValues["root"] + gotOutput = state.RootOutputValues["root"] wantOutputValue = cty.StringVal(filepath.ToSlash(tf.Path("subdir"))) // path.root is a relative path, but the text fixture uses abspath on it. if gotOutput == nil || !wantOutputValue.RawEquals(gotOutput.Value) { t.Errorf("incorrect value for root output\ngot: %#v\nwant Value: %#v", gotOutput, wantOutputValue) diff --git a/internal/command/e2etest/terraform_test.go b/internal/command/e2etest/terraform_test.go index 1ceb307fe4a7..29b99ba5c3b6 100644 --- a/internal/command/e2etest/terraform_test.go +++ b/internal/command/e2etest/terraform_test.go @@ -8,7 +8,6 @@ import ( "strings" "testing" - "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/e2e" ) @@ -46,7 +45,7 @@ func TestTerraformProviderData(t *testing.T) { } // we'll check the final output to validate the resources - d := state.Module(addrs.RootModuleInstance).OutputValues["d"].Value + d := state.RootOutputValues["d"].Value input := d.GetAttr("input") output := d.GetAttr("output") if input.IsNull() { diff --git a/internal/command/jsonformat/state_test.go b/internal/command/jsonformat/state_test.go index 462b422cde29..aeb3cf8dd2b3 100644 --- a/internal/command/jsonformat/state_test.go +++ b/internal/command/jsonformat/state_test.go @@ -32,32 +32,32 @@ func TestState(t *testing.T) { Schemas *terraform.Schemas Want string }{ - { + 0: { State: &states.State{}, Schemas: &terraform.Schemas{}, Want: "The state file is empty. No resources are represented.\n", }, - { + 1: { State: basicState(t), Schemas: testSchemas(), Want: basicStateOutput, }, - { + 2: { State: nestedState(t), Schemas: testSchemas(), Want: nestedStateOutput, }, - { + 3: { State: deposedState(t), Schemas: testSchemas(), Want: deposedNestedStateOutput, }, - { + 4: { State: onlyDeposedState(t), Schemas: testSchemas(), Want: onlyDeposedOutput, }, - { + 5: { State: stateWithMoreOutputs(t), Schemas: testSchemas(), Want: stateWithMoreOutputsOutput, @@ -252,7 +252,10 @@ func basicState(t *testing.T) *states.State { t.Errorf("root module is nil; want valid object") } - rootModule.SetOutputValue("bar", cty.StringVal("bar value"), false) + state.SetOutputValue( + addrs.OutputValue{Name: "bar"}.Absolute(addrs.RootModuleInstance), + cty.StringVal("bar value"), false, + ) rootModule.SetResourceInstanceCurrent( addrs.Resource{ Mode: addrs.ManagedResourceMode, @@ -296,14 +299,30 @@ func stateWithMoreOutputs(t *testing.T) *states.State { t.Errorf("root module is nil; want valid object") } - rootModule.SetOutputValue("string_var", cty.StringVal("string value"), false) - rootModule.SetOutputValue("int_var", cty.NumberIntVal(42), false) - rootModule.SetOutputValue("bool_var", cty.BoolVal(true), false) - rootModule.SetOutputValue("sensitive_var", cty.StringVal("secret!!!"), true) - rootModule.SetOutputValue("map_var", cty.MapVal(map[string]cty.Value{ - "first": cty.StringVal("foo"), - "second": cty.StringVal("bar"), - }), false) + state.SetOutputValue( + addrs.OutputValue{Name: "string_var"}.Absolute(addrs.RootModuleInstance), + cty.StringVal("string value"), false, + ) + state.SetOutputValue( + addrs.OutputValue{Name: "int_var"}.Absolute(addrs.RootModuleInstance), + cty.NumberIntVal(42), false, + ) + state.SetOutputValue( + addrs.OutputValue{Name: "bool_var"}.Absolute(addrs.RootModuleInstance), + cty.True, false, + ) + state.SetOutputValue( + addrs.OutputValue{Name: "sensitive_var"}.Absolute(addrs.RootModuleInstance), + cty.StringVal("secret!!!"), true, + ) + state.SetOutputValue( + addrs.OutputValue{Name: "map_var"}.Absolute(addrs.RootModuleInstance), + cty.MapVal(map[string]cty.Value{ + "first": cty.StringVal("foo"), + "second": cty.StringVal("bar"), + }), + false, + ) rootModule.SetResourceInstanceCurrent( addrs.Resource{ diff --git a/internal/command/jsonstate/state.go b/internal/command/jsonstate/state.go index ae46225a2108..e7b1d07de53d 100644 --- a/internal/command/jsonstate/state.go +++ b/internal/command/jsonstate/state.go @@ -149,7 +149,7 @@ func MarshalForRenderer(sf *statefile.File, schemas *terraform.Schemas) (Module, return Module{}, nil, nil } - outputs, err := MarshalOutputs(sf.State.RootModule().OutputValues) + outputs, err := MarshalOutputs(sf.State.RootOutputValues) if err != nil { return Module{}, nil, err } @@ -193,7 +193,7 @@ func (jsonstate *state) marshalStateValues(s *states.State, schemas *terraform.S var err error // only marshal the root module outputs - sv.Outputs, err = MarshalOutputs(s.RootModule().OutputValues) + sv.Outputs, err = MarshalOutputs(s.RootOutputValues) if err != nil { return err } diff --git a/internal/command/meta_backend_test.go b/internal/command/meta_backend_test.go index 8f647fe36611..ab777fb26328 100644 --- a/internal/command/meta_backend_test.go +++ b/internal/command/meta_backend_test.go @@ -136,7 +136,10 @@ func TestMetaBackend_emptyWithDefaultState(t *testing.T) { // Write some state next := testState() - next.RootModule().SetOutputValue("foo", cty.StringVal("bar"), false) + next.SetOutputValue( + addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance), + cty.StringVal("bar"), false, + ) s.WriteState(next) if err := s.PersistState(nil); err != nil { t.Fatalf("unexpected error: %s", err) @@ -1862,7 +1865,10 @@ func TestMetaBackend_localDoesNotDeleteLocal(t *testing.T) { // // create our local state orig := states.NewState() - orig.Module(addrs.RootModuleInstance).SetOutputValue("foo", cty.StringVal("bar"), false) + orig.SetOutputValue( + addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance), + cty.StringVal("bar"), false, + ) testStateFileDefault(t, orig) m := testMetaBackend(t, nil) diff --git a/internal/command/refresh.go b/internal/command/refresh.go index 8b54bb9e47a3..c92a019ecaac 100644 --- a/internal/command/refresh.go +++ b/internal/command/refresh.go @@ -104,7 +104,7 @@ func (c *RefreshCommand) Run(rawArgs []string) int { } if op.State != nil { - view.Outputs(op.State.RootModule().OutputValues) + view.Outputs(op.State.RootOutputValues) } return op.Result.ExitStatus() diff --git a/internal/command/show_test.go b/internal/command/show_test.go index b6a999615e61..4c0cdf1ff4fe 100644 --- a/internal/command/show_test.go +++ b/internal/command/show_test.go @@ -494,12 +494,15 @@ func TestShow_plan_json(t *testing.T) { func TestShow_state(t *testing.T) { originalState := testState() - root := originalState.RootModule() - root.SetOutputValue("test", cty.ObjectVal(map[string]cty.Value{ - "attr": cty.NullVal(cty.DynamicPseudoType), - "null": cty.NullVal(cty.String), - "list": cty.ListVal([]cty.Value{cty.NullVal(cty.Number)}), - }), false) + originalState.SetOutputValue( + addrs.OutputValue{Name: "test"}.Absolute(addrs.RootModuleInstance), + cty.ObjectVal(map[string]cty.Value{ + "attr": cty.NullVal(cty.DynamicPseudoType), + "null": cty.NullVal(cty.String), + "list": cty.ListVal([]cty.Value{cty.NullVal(cty.Number)}), + }), + false, + ) statePath := testStateFile(t, originalState) defer os.RemoveAll(filepath.Dir(statePath)) diff --git a/internal/namedvals/state.go b/internal/namedvals/state.go index a96f10c6e3b1..0b4a2ea5aa7c 100644 --- a/internal/namedvals/state.go +++ b/internal/namedvals/state.go @@ -107,6 +107,54 @@ func (s *State) GetOutputValue(addr addrs.AbsOutputValue) cty.Value { return s.outputs.GetExactResult(addr) } +func (s *State) GetOutputValuesForModuleCall(parentAddr addrs.ModuleInstance, callAddr addrs.ModuleCall) addrs.Map[addrs.AbsOutputValue, cty.Value] { + s.mu.Lock() + defer s.mu.Unlock() + + // HACK: The "values" data structure isn't really designed to support + // this operation, since it tries to be general over all different named + // value address types but that makes it unable to generically handle + // the problem of finding the module instance for a particular absolute + // address. We'd need a ModuleInstance equivalent of + // addrs.InPartialExpandedModule to achieve that, but our "Abs" address + // types are all hand-written and predate Go having support for generic + // types. + // + // This operation is just a stop-gap until we make the evaluator work + // in a different way to handle placeholder values, so we'll accept it + // being clunky and slow just as a checkpoint to make everything still + // work similarly to how it used to, and then delete this function again + // later once we can implement what we need using just + // [State.GetOutputValue] by having the caller determine which output + // values it should be asking for using the configuration. + + ret := addrs.MakeMap[addrs.AbsOutputValue, cty.Value]() + all := s.outputs.GetExactResults() + + for _, elem := range all.Elems { + outputMod := elem.Key.Module + if outputMod.IsRoot() { + // We cannot enumerate the root module output values with this + // function, because the root module has no "call". + continue + } + callingMod, call := outputMod.Call() + if call != callAddr { + continue + } + if !callingMod.Equal(parentAddr) { + continue + } + + // If we get here then the output value we're holding belongs to + // one of the instances of the call indicated in this function's + // arguments. + ret.PutElement(elem) + } + + return ret +} + func (s *State) HasOutputValue(addr addrs.AbsOutputValue) bool { s.mu.Lock() defer s.mu.Unlock() diff --git a/internal/namedvals/values.go b/internal/namedvals/values.go index eb559ae68f9e..f24a89c16221 100644 --- a/internal/namedvals/values.go +++ b/internal/namedvals/values.go @@ -90,6 +90,10 @@ func (v *values[LocalType, AbsType]) GetExactResult(addr AbsType) cty.Value { return v.exact.Get(addr) } +func (v *values[LocalType, AbsType]) GetExactResults() addrs.Map[AbsType, cty.Value] { + return v.exact +} + func (v *values[LocalType, AbsType]) SetPlaceholderResult(addr addrs.InPartialExpandedModule[LocalType], val cty.Value) { modAddr := addr.Module.Module() if !v.placeholder.Has(modAddr) { diff --git a/internal/stacks/stackruntime/internal/stackeval/component_instance.go b/internal/stacks/stackruntime/internal/stackeval/component_instance.go index 1643e58f58db..9eb2b8b2a5a1 100644 --- a/internal/stacks/stackruntime/internal/stackeval/component_instance.go +++ b/internal/stacks/stackruntime/internal/stackeval/component_instance.go @@ -908,7 +908,7 @@ func (c *ComponentInstance) ResultValue(ctx context.Context, phase EvalPhase) ct // For apply and inspect phases we use the root module output values // from the state to construct our value. - outputVals := state.RootModule().OutputValues + outputVals := state.RootOutputValues attrs := make(map[string]cty.Value, len(outputVals)) for _, ov := range outputVals { name := ov.Addr.OutputValue.Name diff --git a/internal/states/module.go b/internal/states/module.go index 3ed2485601e9..16b3c7564c21 100644 --- a/internal/states/module.go +++ b/internal/states/module.go @@ -4,8 +4,6 @@ package states import ( - "github.com/zclconf/go-cty/cty" - "github.com/hashicorp/terraform/internal/addrs" ) @@ -16,18 +14,13 @@ type Module struct { // Resources contains the state for each resource. The keys in this map are // an implementation detail and must not be used by outside callers. Resources map[string]*Resource - - // OutputValues contains the state for each output value. The keys in this - // map are output value names. - OutputValues map[string]*OutputValue } // NewModule constructs an empty module state for the given module address. func NewModule(addr addrs.ModuleInstance) *Module { return &Module{ - Addr: addr, - Resources: map[string]*Resource{}, - OutputValues: map[string]*OutputValue{}, + Addr: addr, + Resources: map[string]*Resource{}, } } @@ -248,30 +241,6 @@ func (ms *Module) maybeRestoreResourceInstanceDeposed(addr addrs.ResourceInstanc return true } -// SetOutputValue writes an output value into the state, overwriting any -// existing value of the same name. -func (ms *Module) SetOutputValue(name string, value cty.Value, sensitive bool) *OutputValue { - os := &OutputValue{ - Addr: addrs.AbsOutputValue{ - Module: ms.Addr, - OutputValue: addrs.OutputValue{ - Name: name, - }, - }, - Value: value, - Sensitive: sensitive, - } - ms.OutputValues[name] = os - return os -} - -// RemoveOutputValue removes the output value of the given name from the state, -// if it exists. This method is a no-op if there is no value of the given -// name. -func (ms *Module) RemoveOutputValue(name string) { - delete(ms.OutputValues, name) -} - // PruneResourceHusks is a specialized method that will remove any Resource // objects that do not contain any instances, even if they have an EachMode. // @@ -300,6 +269,5 @@ func (ms *Module) empty() bool { // This must be updated to cover any new collections added to Module // in future. - return (len(ms.Resources) == 0 && - len(ms.OutputValues) == 0) + return len(ms.Resources) == 0 } diff --git a/internal/states/remote/state.go b/internal/states/remote/state.go index ebcbc59e745d..ca08ab789ff2 100644 --- a/internal/states/remote/state.go +++ b/internal/states/remote/state.go @@ -70,7 +70,7 @@ func (s *State) GetRootOutputValues() (map[string]*states.OutputValue, error) { state = states.NewState() } - return state.RootModule().OutputValues, nil + return state.RootOutputValues, nil } // StateForMigration is part of our implementation of statemgr.Migrator. diff --git a/internal/states/remote/state_test.go b/internal/states/remote/state_test.go index 826d5f5a80ef..ce34ff7badab 100644 --- a/internal/states/remote/state_test.go +++ b/internal/states/remote/state_test.go @@ -233,7 +233,10 @@ func TestStatePersist(t *testing.T) { name: "add output to state", mutationFunc: func(mgr *State) (*states.State, func()) { s := mgr.State() - s.RootModule().SetOutputValue("foo", cty.StringVal("bar"), false) + s.SetOutputValue( + addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance), + cty.StringVal("bar"), false, + ) return s, func() {} }, expectedRequests: []mockClientRequest{ @@ -261,7 +264,10 @@ func TestStatePersist(t *testing.T) { name: "mutate state bar -> baz", mutationFunc: func(mgr *State) (*states.State, func()) { s := mgr.State() - s.RootModule().SetOutputValue("foo", cty.StringVal("baz"), false) + s.SetOutputValue( + addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance), + cty.StringVal("baz"), false, + ) return s, func() {} }, expectedRequests: []mockClientRequest{ diff --git a/internal/states/state.go b/internal/states/state.go index 497c299f0841..e3e8f2d961ca 100644 --- a/internal/states/state.go +++ b/internal/states/state.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/getproviders" + "github.com/zclconf/go-cty/cty" ) // State is the top-level type of a Terraform state. @@ -27,6 +28,14 @@ type State struct { // an implementation detail and must not be used by outside callers. Modules map[string]*Module + // OutputValues contains the state for each output value defined in the + // root module. + // + // Output values in other modules don't persist anywhere between runs, + // so Terraform Core tracks those only internally and does not expose + // them in any artifacts that survive between runs. + RootOutputValues map[string]*OutputValue + // 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 @@ -46,7 +55,8 @@ func NewState() *State { modules := map[string]*Module{} modules[addrs.RootModuleInstance.String()] = NewModule(addrs.RootModuleInstance) return &State{ - Modules: modules, + Modules: modules, + RootOutputValues: make(map[string]*OutputValue), } } @@ -66,11 +76,11 @@ func (s *State) Empty() bool { if s == nil { return true } + if len(s.RootOutputValues) != 0 { + return false + } for _, ms := range s.Modules { - if len(ms.Resources) != 0 { - return false - } - if len(ms.OutputValues) != 0 { + if !ms.empty() { return false } } @@ -97,35 +107,6 @@ func (s *State) ModuleInstances(addr addrs.Module) []*Module { return ms } -// ModuleOutputs returns all outputs for the given module call under the -// parentAddr instance. -func (s *State) ModuleOutputs(parentAddr addrs.ModuleInstance, module addrs.ModuleCall) []*OutputValue { - var os []*OutputValue - for _, m := range s.Modules { - // can't get outputs from the root module - if m.Addr.IsRoot() { - continue - } - - parent, call := m.Addr.Call() - // make sure this is a descendent in the correct path - if !parentAddr.Equal(parent) { - continue - } - - // and check if this is the correct child - if call.Name != module.Name { - continue - } - - for _, o := range m.OutputValues { - os = append(os, o) - } - } - - return os -} - // RemoveModule removes the module with the given address from the state, // unless it is the root module. The root module cannot be deleted, and so // this method will panic if that is attempted. @@ -306,12 +287,39 @@ func (s *State) ResourceInstanceObjectSrc(addr addrs.AbsResourceInstanceObject) // OutputValue returns the state for the output value with the given address, // or nil if no such output value is tracked in the state. +// +// Only root module output values are tracked in the state, so this always +// returns nil for output values in any other module. func (s *State) OutputValue(addr addrs.AbsOutputValue) *OutputValue { - ms := s.Module(addr.Module) - if ms == nil { + if !addr.Module.IsRoot() { return nil } - return ms.OutputValues[addr.OutputValue.Name] + return s.RootOutputValues[addr.OutputValue.Name] +} + +// SetOutputValue updates the value stored for the given output value if and +// only if it's a root module output value. +// +// All other output values will just be silently ignored, because we don't +// store those here anymore. (They live in a namedvals.State object hidden +// in the internals of Terraform Core.) +func (s *State) SetOutputValue(addr addrs.AbsOutputValue, value cty.Value, sensitive bool) { + if !addr.Module.IsRoot() { + return + } + s.RootOutputValues[addr.OutputValue.Name] = &OutputValue{ + Addr: addr, + Value: value, + Sensitive: sensitive, + } +} + +// RemoveOutputValue removes the record of a previously-stored output value. +func (s *State) RemoveOutputValue(addr addrs.AbsOutputValue) { + if !addr.Module.IsRoot() { + return + } + delete(s.RootOutputValues, addr.OutputValue.Name) } // ProviderAddrs returns a list of all of the provider configuration addresses @@ -554,13 +562,6 @@ func (s *State) MoveModuleInstance(src, dst addrs.ModuleInstance) { r.Addr.Module = dst } } - - // Update any OutputValues's addresses. - if srcMod.OutputValues != nil { - for _, ov := range srcMod.OutputValues { - ov.Addr.Module = dst - } - } } // MaybeMoveModuleInstance moves the given src ModuleInstance's current state to diff --git a/internal/states/state_deepcopy.go b/internal/states/state_deepcopy.go index 156a644a05f4..5bd88f77772a 100644 --- a/internal/states/state_deepcopy.go +++ b/internal/states/state_deepcopy.go @@ -31,9 +31,14 @@ func (s *State) DeepCopy() *State { for k, m := range s.Modules { modules[k] = m.DeepCopy() } + outputValues := make(map[string]*OutputValue, len(s.RootOutputValues)) + for k, v := range s.RootOutputValues { + outputValues[k] = v.DeepCopy() + } return &State{ - Modules: modules, - CheckResults: s.CheckResults.DeepCopy(), + Modules: modules, + RootOutputValues: outputValues, + CheckResults: s.CheckResults.DeepCopy(), } } @@ -54,15 +59,10 @@ func (ms *Module) DeepCopy() *Module { for k, r := range ms.Resources { resources[k] = r.DeepCopy() } - outputValues := make(map[string]*OutputValue, len(ms.OutputValues)) - for k, v := range ms.OutputValues { - outputValues[k] = v.DeepCopy() - } return &Module{ - Addr: ms.Addr, // technically mutable, but immutable by convention - Resources: resources, - OutputValues: outputValues, + Addr: ms.Addr, // technically mutable, but immutable by convention + Resources: resources, } } diff --git a/internal/states/state_string.go b/internal/states/state_string.go index 2e34834a5518..6e6796ea2d9a 100644 --- a/internal/states/state_string.go +++ b/internal/states/state_string.go @@ -38,7 +38,7 @@ func (s *State) String() string { var buf bytes.Buffer for _, name := range modules { m := s.Modules[name] - mStr := m.testString() + mStr := m.testString(s) // If we're the root module, we just write the output directly. if m.Addr.IsRoot() { @@ -76,7 +76,7 @@ func (s *State) String() string { // testString is used to produce part of the output of State.String. It should // never be used directly. -func (ms *Module) testString() string { +func (ms *Module) testString(state *State) string { var buf bytes.Buffer if len(ms.Resources) == 0 { @@ -204,17 +204,25 @@ func (ms *Module) testString() string { } } - if len(ms.OutputValues) > 0 { + // This is a bit weird because we used to store output values for all + // modules in the state, but now we use it only for the root output + // values since they are the only ones that persist between runs. + // + // To keep this long-suffering legacy string representation compatible + // (since so many of our older tests depend on it) we have this structured + // in as close as possible to the same way it was when OutputValues was + // a field of ms, instead of RootOutputValues in State. + if ms.Addr.IsRoot() && len(state.RootOutputValues) != 0 { buf.WriteString("\nOutputs:\n\n") - ks := make([]string, 0, len(ms.OutputValues)) - for k := range ms.OutputValues { + ks := make([]string, 0, len(state.RootOutputValues)) + for k := range state.RootOutputValues { ks = append(ks, k) } sort.Strings(ks) for _, k := range ks { - v := ms.OutputValues[k] + v := state.RootOutputValues[k] lv := hcl2shim.ConfigValueFromHCL2(v.Value) switch vTyped := lv.(type) { case string: diff --git a/internal/states/state_test.go b/internal/states/state_test.go index c1120823003a..a13e7d45b241 100644 --- a/internal/states/state_test.go +++ b/internal/states/state_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/go-test/deep" + "github.com/google/go-cmp/cmp" "github.com/zclconf/go-cty/cty" "github.com/hashicorp/terraform/internal/addrs" @@ -27,8 +28,14 @@ func TestState(t *testing.T) { t.Errorf("root module is nil; want valid object") } - rootModule.SetOutputValue("bar", cty.StringVal("bar value"), false) - rootModule.SetOutputValue("secret", cty.StringVal("secret value"), true) + state.SetOutputValue( + addrs.OutputValue{Name: "bar"}.Absolute(addrs.RootModuleInstance), + cty.StringVal("bar value"), false, + ) + state.SetOutputValue( + addrs.OutputValue{Name: "secret"}.Absolute(addrs.RootModuleInstance), + cty.StringVal("secret value"), true, + ) rootModule.SetResourceInstanceCurrent( addrs.Resource{ Mode: addrs.ManagedResourceMode, @@ -46,37 +53,43 @@ func TestState(t *testing.T) { }, ) + // State silently ignores attempts to write to non-root outputs, because + // historically we did track those here but these days we track them in + // namedvals.State instead, and we're being gracious to existing callers + // that might not know yet that they need to treat root module output + // values in a special way. childModule := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) - childModule.SetOutputValue("pizza", cty.StringVal("hawaiian"), false) + state.SetOutputValue(addrs.OutputValue{Name: "pizza"}.Absolute(childModule.Addr), cty.StringVal("hawaiian"), false) multiModA := state.EnsureModule(addrs.RootModuleInstance.Child("multi", addrs.StringKey("a"))) - multiModA.SetOutputValue("pizza", cty.StringVal("cheese"), false) + state.SetOutputValue(addrs.OutputValue{Name: "pizza"}.Absolute(multiModA.Addr), cty.StringVal("cheese"), false) multiModB := state.EnsureModule(addrs.RootModuleInstance.Child("multi", addrs.StringKey("b"))) - multiModB.SetOutputValue("pizza", cty.StringVal("sausage"), false) + state.SetOutputValue(addrs.OutputValue{Name: "pizza"}.Absolute(multiModB.Addr), cty.StringVal("sausage"), false) want := &State{ - Modules: map[string]*Module{ - "": { - Addr: addrs.RootModuleInstance, - OutputValues: map[string]*OutputValue{ - "bar": { - Addr: addrs.AbsOutputValue{ - OutputValue: addrs.OutputValue{ - Name: "bar", - }, - }, - Value: cty.StringVal("bar value"), - Sensitive: false, + RootOutputValues: map[string]*OutputValue{ + "bar": { + Addr: addrs.AbsOutputValue{ + OutputValue: addrs.OutputValue{ + Name: "bar", }, - "secret": { - Addr: addrs.AbsOutputValue{ - OutputValue: addrs.OutputValue{ - Name: "secret", - }, - }, - Value: cty.StringVal("secret value"), - Sensitive: true, + }, + Value: cty.StringVal("bar value"), + Sensitive: false, + }, + "secret": { + Addr: addrs.AbsOutputValue{ + OutputValue: addrs.OutputValue{ + Name: "secret", }, }, + Value: cty.StringVal("secret value"), + Sensitive: true, + }, + }, + + Modules: map[string]*Module{ + "": { + Addr: addrs.RootModuleInstance, Resources: map[string]*Resource{ "test_thing.baz": { Addr: addrs.Resource{ @@ -103,51 +116,15 @@ func TestState(t *testing.T) { }, }, "module.child": { - Addr: addrs.RootModuleInstance.Child("child", addrs.NoKey), - OutputValues: map[string]*OutputValue{ - "pizza": { - Addr: addrs.AbsOutputValue{ - Module: addrs.RootModuleInstance.Child("child", addrs.NoKey), - OutputValue: addrs.OutputValue{ - Name: "pizza", - }, - }, - Value: cty.StringVal("hawaiian"), - Sensitive: false, - }, - }, + Addr: addrs.RootModuleInstance.Child("child", addrs.NoKey), Resources: map[string]*Resource{}, }, `module.multi["a"]`: { - Addr: addrs.RootModuleInstance.Child("multi", addrs.StringKey("a")), - OutputValues: map[string]*OutputValue{ - "pizza": { - Addr: addrs.AbsOutputValue{ - Module: addrs.RootModuleInstance.Child("multi", addrs.StringKey("a")), - OutputValue: addrs.OutputValue{ - Name: "pizza", - }, - }, - Value: cty.StringVal("cheese"), - Sensitive: false, - }, - }, + Addr: addrs.RootModuleInstance.Child("multi", addrs.StringKey("a")), Resources: map[string]*Resource{}, }, `module.multi["b"]`: { - Addr: addrs.RootModuleInstance.Child("multi", addrs.StringKey("b")), - OutputValues: map[string]*OutputValue{ - "pizza": { - Addr: addrs.AbsOutputValue{ - Module: addrs.RootModuleInstance.Child("multi", addrs.StringKey("b")), - OutputValue: addrs.OutputValue{ - Name: "pizza", - }, - }, - Value: cty.StringVal("sausage"), - Sensitive: false, - }, - }, + Addr: addrs.RootModuleInstance.Child("multi", addrs.StringKey("b")), Resources: map[string]*Resource{}, }, }, @@ -166,27 +143,8 @@ func TestState(t *testing.T) { }() } - for _, problem := range deep.Equal(state, want) { - t.Error(problem) - } - - expectedOutputs := map[string]string{ - `module.multi["a"].output.pizza`: "cheese", - `module.multi["b"].output.pizza`: "sausage", - } - - for _, o := range state.ModuleOutputs(addrs.RootModuleInstance, addrs.ModuleCall{Name: "multi"}) { - addr := o.Addr.String() - expected := expectedOutputs[addr] - delete(expectedOutputs, addr) - - if expected != o.Value.AsString() { - t.Fatalf("expected %q:%q, got %q", addr, expected, o.Value.AsString()) - } - } - - for addr, o := range expectedOutputs { - t.Fatalf("missing output %q:%q", addr, o) + if diff := cmp.Diff(want.String(), state.String()); diff != "" { + t.Errorf("wrong result\n%s", diff) } } @@ -224,8 +182,14 @@ func TestStateDeepCopy(t *testing.T) { t.Errorf("root module is nil; want valid object") } - rootModule.SetOutputValue("bar", cty.StringVal("bar value"), false) - rootModule.SetOutputValue("secret", cty.StringVal("secret value"), true) + state.SetOutputValue( + addrs.OutputValue{Name: "bar"}.Absolute(rootModule.Addr), + cty.StringVal("bar value"), false, + ) + state.SetOutputValue( + addrs.OutputValue{Name: "secret"}.Absolute(rootModule.Addr), + cty.StringVal("secret value"), true, + ) rootModule.SetResourceInstanceCurrent( addrs.Resource{ Mode: addrs.ManagedResourceMode, @@ -280,8 +244,7 @@ func TestStateDeepCopy(t *testing.T) { }, ) - childModule := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) - childModule.SetOutputValue("pizza", cty.StringVal("hawaiian"), false) + state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) stateCopy := state.DeepCopy() if !state.Equal(stateCopy) { diff --git a/internal/states/statefile/version4.go b/internal/states/statefile/version4.go index 52939e8cb896..f3b21c6028b1 100644 --- a/internal/states/statefile/version4.go +++ b/internal/states/statefile/version4.go @@ -260,7 +260,6 @@ func prepareStateV4(sV4 *stateV4) (*File, tfdiags.Diagnostics) { // need to reload them now. (For descendent modules we just re-calculate // them based on the latest configuration on each run.) { - rootModule := state.RootModule() for name, fos := range sV4.RootOutputs { os := &states.OutputValue{ Addr: addrs.AbsOutputValue{ @@ -292,7 +291,7 @@ func prepareStateV4(sV4 *stateV4) (*File, tfdiags.Diagnostics) { } os.Value = val - rootModule.OutputValues[name] = os + state.RootOutputValues[name] = os } } @@ -338,7 +337,7 @@ func writeStateV4(file *File, w io.Writer) tfdiags.Diagnostics { Resources: []resourceStateV4{}, } - for name, os := range file.State.RootModule().OutputValues { + for name, os := range file.State.RootOutputValues { src, err := ctyjson.Marshal(os.Value, os.Value.Type()) if err != nil { diags = diags.Append(tfdiags.Sourceless( diff --git a/internal/states/statemgr/filesystem.go b/internal/states/statemgr/filesystem.go index a69a48cff4c2..42ea8b46319e 100644 --- a/internal/states/statemgr/filesystem.go +++ b/internal/states/statemgr/filesystem.go @@ -248,7 +248,7 @@ func (s *Filesystem) GetRootOutputValues() (map[string]*states.OutputValue, erro state = states.NewState() } - return state.RootModule().OutputValues, nil + return state.RootOutputValues, nil } func (s *Filesystem) refreshState() error { diff --git a/internal/states/statemgr/statemgr_fake.go b/internal/states/statemgr/statemgr_fake.go index db12945b5316..11eb8ebbf1ed 100644 --- a/internal/states/statemgr/statemgr_fake.go +++ b/internal/states/statemgr/statemgr_fake.go @@ -70,7 +70,7 @@ func (m *fakeFull) PersistState(schemas *terraform.Schemas) error { } func (m *fakeFull) GetRootOutputValues() (map[string]*states.OutputValue, error) { - return m.State().RootModule().OutputValues, nil + return m.State().RootOutputValues, nil } func (m *fakeFull) Lock(info *LockInfo) (string, error) { diff --git a/internal/states/statemgr/testing.go b/internal/states/statemgr/testing.go index d002a95b35bf..1174cb293022 100644 --- a/internal/states/statemgr/testing.go +++ b/internal/states/statemgr/testing.go @@ -48,7 +48,10 @@ func TestFull(t *testing.T, s Full) { current := s.State() // Write a new state and verify that we have it - current.RootModule().SetOutputValue("bar", cty.StringVal("baz"), false) + current.SetOutputValue( + addrs.OutputValue{Name: "bar"}.Absolute(addrs.RootModuleInstance), + cty.StringVal("baz"), false, + ) if err := s.WriteState(current); err != nil { t.Fatalf("err: %s", err) @@ -101,9 +104,11 @@ func TestFull(t *testing.T, s Full) { // Change the serial current = current.DeepCopy() - current.EnsureModule(addrs.RootModuleInstance).SetOutputValue( - "serialCheck", cty.StringVal("true"), false, + current.SetOutputValue( + addrs.OutputValue{Name: "serialCheck"}.Absolute(addrs.RootModuleInstance), + cty.StringVal("true"), false, ) + if err := s.WriteState(current); err != nil { t.Fatalf("err: %s", err) } @@ -159,8 +164,14 @@ func TestFullInitialState() *states.State { } childMod.SetResourceProvider(rAddr, providerAddr) - state.RootModule().SetOutputValue("sensitive_output", cty.StringVal("it's a secret"), true) - state.RootModule().SetOutputValue("nonsensitive_output", cty.StringVal("hello, world!"), false) + state.SetOutputValue( + addrs.OutputValue{Name: "sensitive_output"}.Absolute(addrs.RootModuleInstance), + cty.StringVal("it's a secret"), true, + ) + state.SetOutputValue( + addrs.OutputValue{Name: "nonsensitive_output"}.Absolute(addrs.RootModuleInstance), + cty.StringVal("hello, world!"), false, + ) return state } diff --git a/internal/states/sync.go b/internal/states/sync.go index 8b61ce462870..ec8bf45dbe6f 100644 --- a/internal/states/sync.go +++ b/internal/states/sync.go @@ -52,18 +52,6 @@ func (s *SyncState) Module(addr addrs.ModuleInstance) *Module { return ret } -// ModuleOutputs returns the set of OutputValues that matches the given path. -func (s *SyncState) ModuleOutputs(parentAddr addrs.ModuleInstance, module addrs.ModuleCall) []*OutputValue { - s.lock.RLock() - defer s.lock.RUnlock() - var os []*OutputValue - - for _, o := range s.state.ModuleOutputs(parentAddr, module) { - os = append(os, o.DeepCopy()) - } - return os -} - // RemoveModule removes the entire state for the given module, taking with // it any resources associated with the module. This should generally be // called only for modules whose resources have all been destroyed, but @@ -90,31 +78,33 @@ func (s *SyncState) OutputValue(addr addrs.AbsOutputValue) *OutputValue { // SetOutputValue writes a given output value into the state, overwriting // any existing value of the same name. // -// If the module containing the output is not yet tracked in state then it -// be added as a side-effect. +// The state only tracks output values for the root module, so attempts to +// write output values for any other module will be quietly ignored. func (s *SyncState) SetOutputValue(addr addrs.AbsOutputValue, value cty.Value, sensitive bool) { + if !addr.Module.IsRoot() { + return + } + s.lock.Lock() defer s.lock.Unlock() - ms := s.state.EnsureModule(addr.Module) - ms.SetOutputValue(addr.OutputValue.Name, value, sensitive) + s.state.SetOutputValue(addr, value, sensitive) } // RemoveOutputValue removes the stored value for the output value with the // given address. // -// If this results in its containing module being empty, the module will be -// pruned from the state as a side-effect. +// The state only tracks output values for the root module, so attempts to +// remove output values for any other module will be quietly ignored. func (s *SyncState) RemoveOutputValue(addr addrs.AbsOutputValue) { + if !addr.Module.IsRoot() { + return + } + s.lock.Lock() defer s.lock.Unlock() - ms := s.state.Module(addr.Module) - if ms == nil { - return - } - ms.RemoveOutputValue(addr.OutputValue.Name) - s.maybePruneModule(addr.Module) + s.state.RemoveOutputValue(addr) } // Resource returns a snapshot of the state of the resource with the given diff --git a/internal/terraform/context_apply2_test.go b/internal/terraform/context_apply2_test.go index 2d102340f7f8..72590c4173dc 100644 --- a/internal/terraform/context_apply2_test.go +++ b/internal/terraform/context_apply2_test.go @@ -621,7 +621,7 @@ func TestContext2Apply_nullableVariables(t *testing.T) { t.Fatalf("apply: %s", diags.Err()) } - outputs := state.Module(addrs.RootModuleInstance).OutputValues + outputs := state.RootOutputValues // we check for null outputs be seeing that they don't exists if _, ok := outputs["nullable_null_default"]; ok { t.Error("nullable_null_default: expected no output value") @@ -1686,7 +1686,10 @@ output "from_resource" { }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), ) - mod.SetOutputValue("from_resource", cty.StringVal("wrong val"), false) + state.SetOutputValue( + addrs.OutputValue{Name: "from_resource"}.Absolute(addrs.RootModuleInstance), + cty.StringVal("wrong val"), false, + ) ctx := testContext2(t, &ContextOpts{ Providers: map[addrs.Provider]providers.Factory{ diff --git a/internal/terraform/context_apply_test.go b/internal/terraform/context_apply_test.go index 6bbc13372f48..81d61cebd774 100644 --- a/internal/terraform/context_apply_test.go +++ b/internal/terraform/context_apply_test.go @@ -717,10 +717,7 @@ module.test: null_resource.noop: ID = foo provider = provider["registry.terraform.io/hashicorp/null"] - - Outputs: - - amis_out = {eu-west-1:ami-789012 eu-west-2:ami-989484 us-west-1:ami-123456 us-west-2:ami-456789 }`) +`) if actual != expected { t.Fatalf("expected: \n%s\n\ngot: \n%s\n", expected, actual) } @@ -3296,10 +3293,6 @@ module.A: provider = provider["registry.terraform.io/hashicorp/aws"] foo = bar type = aws_instance - - Outputs: - - value = foo module.B: aws_instance.bar: ID = foo @@ -3605,7 +3598,7 @@ func TestContext2Apply_multiVar(t *testing.T) { t.Fatalf("diags: %s", diags.Err()) } - actual := state.RootModule().OutputValues["output"] + actual := state.RootOutputValues["output"] expected := cty.StringVal("bar0,bar1,bar2") if actual == nil || actual.Value != expected { t.Fatalf("wrong value\ngot: %#v\nwant: %#v", actual.Value, expected) @@ -3639,7 +3632,7 @@ func TestContext2Apply_multiVar(t *testing.T) { t.Logf("End state: %s", state.String()) - actual := state.RootModule().OutputValues["output"] + actual := state.RootOutputValues["output"] if actual == nil { t.Fatal("missing output") } @@ -3846,7 +3839,7 @@ func TestContext2Apply_multiVarComprehensive(t *testing.T) { }, } got := map[string]interface{}{} - for k, s := range state.RootModule().OutputValues { + for k, s := range state.RootOutputValues { got[k] = hcl2shim.ConfigValueFromHCL2(s.Value) } if !reflect.DeepEqual(got, want) { @@ -3882,7 +3875,7 @@ func TestContext2Apply_multiVarOrder(t *testing.T) { t.Logf("State: %s", state.String()) - actual := state.RootModule().OutputValues["should-be-11"] + actual := state.RootOutputValues["should-be-11"] expected := cty.StringVal("index-11") if actual == nil || actual.Value != expected { t.Fatalf("wrong value\ngot: %#v\nwant: %#v", actual.Value, expected) @@ -3913,7 +3906,7 @@ func TestContext2Apply_multiVarOrderInterp(t *testing.T) { t.Logf("State: %s", state.String()) - actual := state.RootModule().OutputValues["should-be-11"] + actual := state.RootOutputValues["should-be-11"] expected := cty.StringVal("baz-index-11") if actual == nil || actual.Value != expected { t.Fatalf("wrong value\ngot: %#v\nwant: %#v", actual.Value, expected) @@ -4070,9 +4063,14 @@ func TestContext2Apply_outputOrphan(t *testing.T) { p.PlanResourceChangeFn = testDiffFn state := states.NewState() - root := state.EnsureModule(addrs.RootModuleInstance) - root.SetOutputValue("foo", cty.StringVal("bar"), false) - root.SetOutputValue("bar", cty.StringVal("baz"), false) + state.SetOutputValue( + addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance), + cty.StringVal("bar"), false, + ) + state.SetOutputValue( + addrs.OutputValue{Name: "bar"}.Absolute(addrs.RootModuleInstance), + cty.StringVal("baz"), false, + ) ctx := testContext2(t, &ContextOpts{ Providers: map[addrs.Provider]providers.Factory{ @@ -7158,7 +7156,10 @@ func TestContext2Apply_targetedDestroy(t *testing.T) { }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), ) - root.SetOutputValue("out", cty.StringVal("bar"), false) + state.SetOutputValue( + addrs.OutputValue{Name: "out"}.Absolute(addrs.RootModuleInstance), + cty.StringVal("bar"), false, + ) child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) child.SetResourceInstanceCurrent( @@ -7217,8 +7218,8 @@ func TestContext2Apply_targetedDestroy(t *testing.T) { // TODO: Future refactoring may enable us to remove the output from state in // this case, and that would be Just Fine - this test can be modified to // expect 0 outputs. - if len(mod.OutputValues) != 1 { - t.Fatalf("expected 1 outputs, got: %#v", mod.OutputValues) + if len(state.RootOutputValues) != 1 { + t.Fatalf("expected 1 outputs, got: %#v", state.RootOutputValues) } // the module instance should remain @@ -7521,6 +7522,7 @@ func TestContext2Apply_targetedModuleDep(t *testing.T) { t.Fatalf("diags: %s", diags.Err()) } + // The output from module.child is copied into aws_instance.foo.foo checkStateString(t, state, ` aws_instance.foo: ID = foo @@ -7536,11 +7538,7 @@ module.child: ID = foo provider = provider["registry.terraform.io/hashicorp/aws"] type = aws_instance - - Outputs: - - output = foo - `) +`) } // GH-10911 untargeted outputs should not be in the graph, and therefore @@ -7588,10 +7586,6 @@ module.child2: ID = foo provider = provider["registry.terraform.io/hashicorp/aws"] type = aws_instance - - Outputs: - - instance_id = foo `) } @@ -8667,7 +8661,7 @@ func TestContext2Apply_terraformWorkspace(t *testing.T) { t.Fatalf("diags: %s", diags.Err()) } - actual := state.RootModule().OutputValues["output"] + actual := state.RootOutputValues["output"] expected := cty.StringVal("foo") if actual == nil || actual.Value != expected { t.Fatalf("wrong value\ngot: %#v\nwant: %#v", actual.Value, expected) @@ -8786,7 +8780,10 @@ func TestContext2Apply_destroyWithLocals(t *testing.T) { }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), ) - root.SetOutputValue("name", cty.StringVal("test-bar"), false) + state.SetOutputValue( + addrs.OutputValue{Name: "name"}.Absolute(addrs.RootModuleInstance), + cty.StringVal("test-bar"), false, + ) ctx := testContext2(t, &ContextOpts{ Providers: map[addrs.Provider]providers.Factory{ @@ -9094,7 +9091,10 @@ func TestContext2Apply_plannedDestroyInterpolatedCount(t *testing.T) { }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), ) - root.SetOutputValue("out", cty.ListVal([]cty.Value{cty.StringVal("foo"), cty.StringVal("foo")}), false) + state.SetOutputValue( + addrs.OutputValue{Name: "out"}.Absolute(addrs.RootModuleInstance), + cty.ListVal([]cty.Value{cty.StringVal("foo"), cty.StringVal("foo")}), false, + ) ctx := testContext2(t, &ContextOpts{ Providers: providers, @@ -10833,6 +10833,14 @@ module "mod2" { source = "./mod" in = module.mod1["a"].out } + +output "mod1" { + value = module.mod1 +} + +output "mod2" { + value = module.mod2 +} `, "mod/main.tf": ` resource "aws_instance" "foo" { @@ -10870,16 +10878,17 @@ output "out" { } expected := ` +Outputs: + +mod1 = {a:map[out:foo] } +mod2 = {out:foo } + module.mod1["a"]: aws_instance.foo: ID = foo provider = provider["registry.terraform.io/hashicorp/aws"] foo = default type = aws_instance - - Outputs: - - out = foo module.mod2: aws_instance.foo: ID = foo @@ -11317,7 +11326,7 @@ locals { } // check the output, as those can't cause an error planning the value - out := state.RootModule().OutputValues["out"].Value.AsString() + out := state.RootOutputValues["out"].Value.AsString() if out != "a0" { t.Fatalf(`expected output "a0", got: %q`, out) } @@ -11372,7 +11381,7 @@ locals { } // check the output, as those can't cause an error planning the value - out = state.RootModule().OutputValues["out"].Value.AsString() + out = state.RootOutputValues["out"].Value.AsString() if out != "" { t.Fatalf(`expected output "", got: %q`, out) } @@ -12212,7 +12221,7 @@ output "out" { t.Fatal(diags.ErrWithWarnings()) } - got := state.RootModule().OutputValues["out"].Value + got := state.RootOutputValues["out"].Value want := cty.ObjectVal(map[string]cty.Value{ "required": cty.StringVal("boop"), @@ -12271,7 +12280,7 @@ output "out" { t.Fatal(diags.ErrWithWarnings()) } - got := state.RootModule().OutputValues["out"].Value + got := state.RootOutputValues["out"].Value want := cty.ObjectVal(map[string]cty.Value{ "required": cty.StringVal("boop"), @@ -12321,7 +12330,7 @@ output "out" { t.Fatal(diags.ErrWithWarnings()) } - got := state.RootModule().OutputValues["out"].Value + got := state.RootOutputValues["out"].Value // The null default value should be bound, after type converting to the // full object type want := cty.TupleVal([]cty.Value{cty.NullVal(cty.Object(map[string]cty.Type{ @@ -12384,7 +12393,7 @@ output "out" { t.Fatal(diags.ErrWithWarnings()) } - got := state.RootModule().OutputValues["out"].Value + got := state.RootOutputValues["out"].Value want := cty.ListVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "a": cty.SetVal([]cty.Value{cty.StringVal("foo")}), diff --git a/internal/terraform/context_plan_test.go b/internal/terraform/context_plan_test.go index 0d759398b446..16d1d37b10f9 100644 --- a/internal/terraform/context_plan_test.go +++ b/internal/terraform/context_plan_test.go @@ -6853,8 +6853,10 @@ output "planned" { ctx := testContext2(t, &ContextOpts{}) state := states.BuildState(func(s *states.SyncState) { - r := s.Module(addrs.RootModuleInstance) - r.SetOutputValue("planned", cty.NullVal(cty.DynamicPseudoType), false) + s.SetOutputValue( + addrs.OutputValue{Name: "planned"}.Absolute(addrs.RootModuleInstance), + cty.NullVal(cty.DynamicPseudoType), false, + ) }) plan, diags := ctx.Plan(m, state, DefaultPlanOpts) if diags.HasErrors() { diff --git a/internal/terraform/context_refresh_test.go b/internal/terraform/context_refresh_test.go index 8e4970160676..7eeec9bda404 100644 --- a/internal/terraform/context_refresh_test.go +++ b/internal/terraform/context_refresh_test.go @@ -721,7 +721,10 @@ func TestContext2Refresh_output(t *testing.T) { state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) testSetResourceInstanceCurrent(root, "aws_instance.web", `{"id":"foo","foo":"bar"}`, `provider["registry.terraform.io/hashicorp/aws"]`) - root.SetOutputValue("foo", cty.StringVal("foo"), false) + state.SetOutputValue( + addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance), + cty.StringVal("foo"), false, + ) ctx := testContext2(t, &ContextOpts{ Providers: map[addrs.Provider]providers.Factory{ diff --git a/internal/terraform/evaluate.go b/internal/terraform/evaluate.go index c28fcd1b7669..672cc2096439 100644 --- a/internal/terraform/evaluate.go +++ b/internal/terraform/evaluate.go @@ -389,20 +389,18 @@ func (d *evaluationStateData) GetModule(addr addrs.ModuleCall, rng tfdiags.Sourc // We know the instance path up to this point, and the child module name, // so we only need to store these by instance key. stateMap := map[addrs.InstanceKey]map[string]cty.Value{} - for _, output := range d.Evaluator.State.ModuleOutputs(d.ModulePath, addr) { - val := output.Value - if output.Sensitive { - val = val.Mark(marks.Sensitive) - } + for _, elem := range d.Evaluator.NamedValues.GetOutputValuesForModuleCall(d.ModulePath, addr).Elems { + outputAddr := elem.Key + val := elem.Value - _, callInstance := output.Addr.Module.CallInstance() + _, callInstance := outputAddr.Module.CallInstance() instance, ok := stateMap[callInstance.Key] if !ok { instance = map[string]cty.Value{} stateMap[callInstance.Key] = instance } - instance[output.Addr.OutputValue.Name] = val + instance[outputAddr.OutputValue.Name] = val } // Get all changes that reside for this module call within our path. diff --git a/internal/terraform/evaluate_test.go b/internal/terraform/evaluate_test.go index c7f96473c076..e7a3e841add1 100644 --- a/internal/terraform/evaluate_test.go +++ b/internal/terraform/evaluate_test.go @@ -25,6 +25,7 @@ func TestEvaluatorGetTerraformAttr(t *testing.T) { Meta: &ContextMeta{ Env: "foo", }, + NamedValues: namedvals.NewState(), } data := &evaluationStateData{ Evaluator: evaluator, @@ -55,6 +56,7 @@ func TestEvaluatorGetPathAttr(t *testing.T) { SourceDir: "bar/baz", }, }, + NamedValues: namedvals.NewState(), } data := &evaluationStateData{ Evaluator: evaluator, @@ -266,7 +268,8 @@ func TestEvaluatorGetResource(t *testing.T) { }, }, }, - State: stateSync, + State: stateSync, + NamedValues: namedvals.NewState(), Plugins: schemaOnlyProvidersForTesting(map[addrs.Provider]providers.ProviderSchema{ addrs.NewDefaultProvider("test"): { ResourceTypes: map[string]providers.Schema{ @@ -503,8 +506,9 @@ func TestEvaluatorGetResource_changes(t *testing.T) { }, }, }, - State: stateSync, - Plugins: schemaOnlyProvidersForTesting(schemas.Providers), + State: stateSync, + NamedValues: namedvals.NewState(), + Plugins: schemaOnlyProvidersForTesting(schemas.Providers), } data := &evaluationStateData{ @@ -542,6 +546,10 @@ func TestEvaluatorGetModule(t *testing.T) { ) }).SyncWrapper() evaluator := evaluatorForModule(stateSync, plans.NewChanges().SyncWrapper()) + evaluator.NamedValues.SetOutputValue( + addrs.OutputValue{Name: "out"}.Absolute(addrs.ModuleInstance{addrs.ModuleInstanceStep{Name: "mod"}}), + cty.StringVal("bar").Mark(marks.Sensitive), + ) data := &evaluationStateData{ Evaluator: evaluator, } @@ -555,7 +563,7 @@ func TestEvaluatorGetModule(t *testing.T) { t.Errorf("unexpected diagnostics %s", spew.Sdump(diags)) } if !got.RawEquals(want) { - t.Errorf("wrong result %#v; want %#v", got, want) + t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, want) } // Changes should override the state value @@ -632,8 +640,9 @@ func evaluatorForModule(stateSync *states.SyncState, changesSync *plans.ChangesS }, }, }, - State: stateSync, - Changes: changesSync, + State: stateSync, + Changes: changesSync, + NamedValues: namedvals.NewState(), } } diff --git a/internal/terraform/node_output.go b/internal/terraform/node_output.go index 249365a65f3c..e83d37517376 100644 --- a/internal/terraform/node_output.go +++ b/internal/terraform/node_output.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform/internal/dag" "github.com/hashicorp/terraform/internal/lang" "github.com/hashicorp/terraform/internal/lang/marks" + "github.com/hashicorp/terraform/internal/namedvals" "github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/states" "github.com/hashicorp/terraform/internal/tfdiags" @@ -390,7 +391,7 @@ If you do intend to export this data, annotate the output value as sensitive by // if we're continuing, make sure the output is included, and // marked as unknown. If the evaluator was able to find a type // for the value in spite of the error then we'll use it. - n.setValue(state, changes, cty.UnknownVal(val.Type())) + n.setValue(ctx.NamedValues(), state, changes, cty.UnknownVal(val.Type())) // Keep existing warnings, while converting errors to warnings. // This is not meant to be the normal path, so there no need to @@ -410,13 +411,13 @@ If you do intend to export this data, annotate the output value as sensitive by } return diags } - n.setValue(state, changes, val) + n.setValue(ctx.NamedValues(), state, changes, val) // If we were able to evaluate a new value, we can update that in the // refreshed state as well. if state = ctx.RefreshState(); state != nil && val.IsWhollyKnown() { // we only need to update the state, do not pass in the changes again - n.setValue(state, nil, val) + n.setValue(nil, state, nil, val) } return diags @@ -472,7 +473,10 @@ func (n *NodeDestroyableOutput) Execute(ctx EvalContext, op walkOperation) tfdia before := cty.NullVal(cty.DynamicPseudoType) mod := state.Module(n.Addr.Module) if n.Addr.Module.IsRoot() && mod != nil { - if o, ok := mod.OutputValues[n.Addr.OutputValue.Name]; ok { + s := state.Lock() + rootOutputs := s.RootOutputValues + state.Unlock() + if o, ok := rootOutputs[n.Addr.OutputValue.Name]; ok { sensitiveBefore = o.Sensitive before = o.Value } else { @@ -521,7 +525,7 @@ func (n *NodeDestroyableOutput) DotNode(name string, opts *dag.DotOpts) *dag.Dot } } -func (n *NodeApplyableOutput) setValue(state *states.SyncState, changes *plans.ChangesSync, val cty.Value) { +func (n *NodeApplyableOutput) setValue(namedVals *namedvals.State, state *states.SyncState, changes *plans.ChangesSync, val cty.Value) { if changes != nil && n.Planning { // if this is a root module, try to get a before value from the state for // the diff @@ -533,7 +537,10 @@ func (n *NodeApplyableOutput) setValue(state *states.SyncState, changes *plans.C mod := state.Module(n.Addr.Module) if n.Addr.Module.IsRoot() && mod != nil { - for name, o := range mod.OutputValues { + s := state.Lock() + rootOutputs := s.RootOutputValues + state.Unlock() + for name, o := range rootOutputs { if name == n.Addr.OutputValue.Name { before = o.Value sensitiveBefore = o.Sensitive @@ -606,8 +613,17 @@ func (n *NodeApplyableOutput) setValue(state *states.SyncState, changes *plans.C return } - log.Printf("[TRACE] setValue: Saving value for %s in state", n.Addr) + // caller leaves namedVals nil if they've already called this function + // with a different state, since we only have one namedVals regardless + // of how many states are involved in an operation. + if namedVals != nil { + namedVals.SetOutputValue(n.Addr, val) + } + // The state itself doesn't represent unknown values, so we null them + // out here and then we'll save the real unknown value in the planned + // changeset, if we have one on this graph walk. + log.Printf("[TRACE] setValue: Saving value for %s in state", n.Addr) // non-root outputs need to keep sensitive marks for evaluation, but are // not serialized. if n.Addr.Module.IsRoot() { diff --git a/internal/terraform/node_output_test.go b/internal/terraform/node_output_test.go index 97af2986eac6..1e007ff703e0 100644 --- a/internal/terraform/node_output_test.go +++ b/internal/terraform/node_output_test.go @@ -153,7 +153,10 @@ func TestNodeDestroyableOutputExecute(t *testing.T) { outputAddr := addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance) state := states.NewState() - state.Module(addrs.RootModuleInstance).SetOutputValue("foo", cty.StringVal("bar"), false) + state.SetOutputValue( + addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance), + cty.StringVal("bar"), false, + ) state.OutputValue(outputAddr) ctx := &MockEvalContext{ diff --git a/internal/terraform/transform_orphan_output.go b/internal/terraform/transform_orphan_output.go index 7a8406e80fad..866d6e9dd999 100644 --- a/internal/terraform/transform_orphan_output.go +++ b/internal/terraform/transform_orphan_output.go @@ -26,37 +26,13 @@ func (t *OrphanOutputTransformer) Transform(g *Graph) error { return nil } - for _, ms := range t.State.Modules { - if err := t.transform(g, ms); err != nil { - return err - } - } - return nil -} - -func (t *OrphanOutputTransformer) transform(g *Graph, ms *states.Module) error { - if ms == nil { - return nil - } - - moduleAddr := ms.Addr - - // Get the config for this path, which is nil if the entire module has been - // removed. - var outputs map[string]*configs.Output - if c := t.Config.DescendentForInstance(moduleAddr); c != nil { - outputs = c.Module.Outputs - } - - // An output is "orphaned" if it's present in the state but not declared - // in the configuration. - for name := range ms.OutputValues { - if _, exists := outputs[name]; exists { + cfgs := t.Config.Module.Outputs + for name := range t.State.RootOutputValues { + if _, exists := cfgs[name]; exists { continue } - g.Add(&NodeDestroyableOutput{ - Addr: addrs.OutputValue{Name: name}.Absolute(moduleAddr), + Addr: addrs.OutputValue{Name: name}.Absolute(addrs.RootModuleInstance), Planning: t.Planning, }) } diff --git a/internal/terraform/update_state_hook_test.go b/internal/terraform/update_state_hook_test.go index 01b6ee662bd2..c7e1b6f28c96 100644 --- a/internal/terraform/update_state_hook_test.go +++ b/internal/terraform/update_state_hook_test.go @@ -17,8 +17,10 @@ func TestUpdateStateHook(t *testing.T) { mockHook := new(MockHook) state := states.NewState() - state.Module(addrs.RootModuleInstance).SetOutputValue("foo", cty.StringVal("hello"), false) - state.Module(addrs.RootModuleInstance) + state.SetOutputValue( + addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance), + cty.StringVal("hello"), false, + ) ctx := new(MockEvalContext) ctx.HookHook = mockHook From c1593dcf59043ab972921e612bef0317169c0597 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Fri, 10 Mar 2023 11:01:48 -0800 Subject: [PATCH 14/17] core: The expression evaluator has access to the instances.Expander This will allow it to determine which instances _should_ be present rather than just trusting which instances _are_ present, which will make it harder to accidentally hide graph ordering bugs behind fallback behavior and, more importantly, will allow the evaluator to recognize the difference between there being no instances at all or the instance keys not yet being known. --- internal/terraform/evaluate.go | 7 +++++++ internal/terraform/graph_walk_context.go | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/internal/terraform/evaluate.go b/internal/terraform/evaluate.go index 672cc2096439..0c2504869698 100644 --- a/internal/terraform/evaluate.go +++ b/internal/terraform/evaluate.go @@ -43,6 +43,13 @@ type Evaluator struct { // variables, local values, and output values. NamedValues *namedvals.State + // Instances tracks the "expansion" (from one configuration object into + // multiple instances of that object) of modules and resources. Other + // parts of Terraform Core update this object during the graph walk, + // and the evaluator uses it to return correctly-shaped data structures + // representing those expandable objects. + Instances *instances.Expander + // Plugins is the library of available plugin components (providers and // provisioners) that we have available to help us evaluate expressions // that interact with plugin-provided objects. diff --git a/internal/terraform/graph_walk_context.go b/internal/terraform/graph_walk_context.go index edc3f9d35d62..56137afc058f 100644 --- a/internal/terraform/graph_walk_context.go +++ b/internal/terraform/graph_walk_context.go @@ -106,8 +106,9 @@ func (w *ContextGraphWalker) EvalContext() EvalContext { Operation: w.Operation, State: w.State, Changes: w.Changes, - Plugins: w.Context.plugins, NamedValues: w.NamedValues, + Instances: w.InstanceExpander, + Plugins: w.Context.plugins, PlanTimestamp: w.PlanTimestamp, } From 0d994a515565ea34e5222471cd22c6f0963804f5 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Fri, 10 Mar 2023 16:50:27 -0800 Subject: [PATCH 15/17] addrs: InstanceKeyType.String method --- internal/addrs/instance_key.go | 2 ++ internal/addrs/instancekeytype_string.go | 37 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 internal/addrs/instancekeytype_string.go diff --git a/internal/addrs/instance_key.go b/internal/addrs/instance_key.go index bb2ac0149b49..22667eb2313c 100644 --- a/internal/addrs/instance_key.go +++ b/internal/addrs/instance_key.go @@ -134,6 +134,8 @@ func instanceKeyType(k InstanceKey) InstanceKeyType { // of those types. type InstanceKeyType rune +//go:generate go run golang.org/x/tools/cmd/stringer -type=InstanceKeyType instance_key.go + const ( NoKeyType InstanceKeyType = 0 IntKeyType InstanceKeyType = 'I' diff --git a/internal/addrs/instancekeytype_string.go b/internal/addrs/instancekeytype_string.go new file mode 100644 index 000000000000..b67fef078be1 --- /dev/null +++ b/internal/addrs/instancekeytype_string.go @@ -0,0 +1,37 @@ +// Code generated by "stringer -type=InstanceKeyType instance_key.go"; DO NOT EDIT. + +package addrs + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[NoKeyType-0] + _ = x[IntKeyType-73] + _ = x[StringKeyType-83] + _ = x[UnknownKeyType-63] +} + +const ( + _InstanceKeyType_name_0 = "NoKeyType" + _InstanceKeyType_name_1 = "UnknownKeyType" + _InstanceKeyType_name_2 = "IntKeyType" + _InstanceKeyType_name_3 = "StringKeyType" +) + +func (i InstanceKeyType) String() string { + switch { + case i == 0: + return _InstanceKeyType_name_0 + case i == 63: + return _InstanceKeyType_name_1 + case i == 73: + return _InstanceKeyType_name_2 + case i == 83: + return _InstanceKeyType_name_3 + default: + return "InstanceKeyType(" + strconv.FormatInt(int64(i), 10) + ")" + } +} From 6f577d225c0d72535c1f89612ed6a77e074517f9 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Fri, 10 Mar 2023 16:55:35 -0800 Subject: [PATCH 16/17] core: Expression evaluator can handle partial-eval in GetModule This is a totally different approach to GetModule which uses the configuration and previously-registered expansion to determine what ought to exist in our named values state, rather than treating the values in the named values state as the source of truth. As a result we get an overall simpler implementation which is able to panic if other components aren't behaving correctly, and we can return placeholder results in partial evaluation mode, at least as long as we're working with a single-instance module. There are some further opportunities for simplification and improving the detail of the unknown results if we make broader changes in future, but for the moment this is just enough to mimic the previous behavior using a new strategy. --- internal/terraform/evaluate.go | 292 ++++++++++++---------------- internal/terraform/evaluate_test.go | 66 ++----- internal/terraform/node_output.go | 89 ++++++++- 3 files changed, 220 insertions(+), 227 deletions(-) diff --git a/internal/terraform/evaluate.go b/internal/terraform/evaluate.go index 0c2504869698..68c265fc1216 100644 --- a/internal/terraform/evaluate.go +++ b/internal/terraform/evaluate.go @@ -392,193 +392,153 @@ func (d *evaluationStateData) GetModule(addr addrs.ModuleCall, rng tfdiags.Sourc } outputConfigs := moduleConfig.Module.Outputs - // Collect all the relevant outputs that current exist in the state. - // We know the instance path up to this point, and the child module name, - // so we only need to store these by instance key. - stateMap := map[addrs.InstanceKey]map[string]cty.Value{} - for _, elem := range d.Evaluator.NamedValues.GetOutputValuesForModuleCall(d.ModulePath, addr).Elems { - outputAddr := elem.Key - val := elem.Value - - _, callInstance := outputAddr.Module.CallInstance() - instance, ok := stateMap[callInstance.Key] - if !ok { - instance = map[string]cty.Value{} - stateMap[callInstance.Key] = instance + // The module won't be expanded during validation, so we need to return an + // unknown value. This will ensure the types looks correct, since we built + // the objects based on the configuration. + // FIXME: If we arrange for all module calls to have unknown expansion + // during the validate walk then we could avoid this special case by + // entering the d.PartialEval branch below instead, but we've retained + // this special case for now to limit the size of the change that + // introduced the partial evaluation mode. + if d.Operation == walkValidate { + attrTypes := make(map[string]cty.Type, len(outputConfigs)) + for name := range outputConfigs { + attrTypes[name] = cty.DynamicPseudoType } - instance[outputAddr.OutputValue.Name] = val - } - - // Get all changes that reside for this module call within our path. - // The change contains the full addr, so we can key these with strings. - changesMap := map[addrs.InstanceKey]map[string]*plans.OutputChangeSrc{} - for _, change := range d.Evaluator.Changes.GetOutputChanges(d.ModulePath, addr) { - _, callInstance := change.Addr.Module.CallInstance() - instance, ok := changesMap[callInstance.Key] - if !ok { - instance = map[string]*plans.OutputChangeSrc{} - changesMap[callInstance.Key] = instance + // While we know the type here and it would be nice to validate whether + // indexes are valid or not, because tuples and objects have fixed + // numbers of elements we can't simply return an unknown value of the + // same type since we have not expanded any instances during + // validation. + // + // In order to validate the expression a little precisely, we'll create + // an unknown map or list here to get more type information. This + // is technically a little incorrect because in all other cases we + // return object types instead of maps and tuple types instead of lists, + // but the validation results don't carry forward into planning so + // this small untruth won't typically hurt anything and gives a little + // more information to the type checker during validation. + ty := cty.Object(attrTypes) + switch { + case callConfig.Count != nil: + return cty.UnknownVal(cty.List(ty)), diags + case callConfig.ForEach != nil: + return cty.UnknownVal(cty.Map(ty)), diags + default: + return cty.UnknownVal(ty), diags } - - instance[change.Addr.OutputValue.Name] = change } - // Build up all the module objects, creating a map of values for each - // module instance. - moduleInstances := map[addrs.InstanceKey]map[string]cty.Value{} - - // create a dummy object type for validation below - unknownMap := map[string]cty.Type{} - - // the structure is based on the configuration, so iterate through all the - // defined outputs, and add any instance state or changes we find. - for _, cfg := range outputConfigs { - // record the output names for validation - unknownMap[cfg.Name] = cty.DynamicPseudoType - - // get all instance output for this path from the state - for key, states := range stateMap { - outputState, ok := states[cfg.Name] - if !ok { - continue - } - - instance, ok := moduleInstances[key] - if !ok { - instance = map[string]cty.Value{} - moduleInstances[key] = instance - } - - instance[cfg.Name] = outputState + if d.PartialEval { + // If we are under an unexpanded module then we cannot predict our + // instance keys unless this is a singleton module call, in which + // has it has only one "no-key" instance. + if callConfig.Count != nil || callConfig.ForEach != nil { + // A multi-instance module call appears as an object type or + // tuple type, which means we can't say anything at all about + // the type until we know exactly what the instance keys will be. + // TODO: If we ever support explicitly-type-constrained output + // values like in https://github.com/hashicorp/terraform/pull/31728 + // then we could return unknown values of list of object and map of + // object types here in any case where all of the output values + // are exactly specified, which would then enable better type + // checking of uses in the calling module. + return cty.DynamicVal, diags } - // any pending changes override the state state values - for key, changes := range changesMap { - changeSrc, ok := changes[cfg.Name] - if !ok { - continue - } - - instance, ok := moduleInstances[key] - if !ok { - instance = map[string]cty.Value{} - moduleInstances[key] = instance - } - - change, err := changeSrc.Decode() - if err != nil { - // This should happen only if someone has tampered with a plan - // file, so we won't bother with a pretty error for it. - diags = diags.Append(fmt.Errorf("planned change for %s could not be decoded: %s", addr, err)) - instance[cfg.Name] = cty.DynamicVal - continue - } - - instance[cfg.Name] = change.After - - if change.Sensitive { - instance[cfg.Name] = change.After.Mark(marks.Sensitive) - } + // If we get here then this _is_ a singleton and our result will be + // an object type built from the placeholder values that represent + // the results for _all_ instances of this module call. + attrs := make(map[string]cty.Value, len(outputConfigs)) + for name := range outputConfigs { + outputAddr := addrs.OutputValue{Name: name} + placeholderAddr := addrs.ObjectInPartialExpandedModule(d.PartialExpandedModulePath.Child(addr), outputAddr) + attrs[name] = d.Evaluator.NamedValues.GetOutputValuePlaceholder(placeholderAddr) } - } - - var ret cty.Value - - // compile the outputs into the correct value type for the each mode - switch { - case callConfig.Count != nil: - // figure out what the last index we have is - length := -1 - for key := range moduleInstances { - intKey, ok := key.(addrs.IntKey) - if !ok { - // old key from state which is being dropped - continue - } - if int(intKey) >= length { - length = int(intKey) + 1 - } + return cty.ObjectVal(attrs), diags + } else { + keyType, knownKeys, unknownKeys := d.Evaluator.Instances.GetModuleCallInstanceKeys(addr.Absolute(d.ModulePath)) + if keyType == addrs.UnknownKeyType || unknownKeys { + // If we don't know our full set of instance keys then we can't say + // anything at all about the expected final result. + return cty.DynamicVal, diags } - if length > 0 { - vals := make([]cty.Value, length) - for key, instance := range moduleInstances { - intKey, ok := key.(addrs.IntKey) - if !ok { - // old key from state which is being dropped - continue - } - - vals[int(intKey)] = cty.ObjectVal(instance) + switch keyType { + case addrs.NoKeyType: + // The following two conditions should always hold for for a module + // call which has addrs.NoKeyType as its key type. + if len(knownKeys) != 1 { + panic(fmt.Sprintf("instance expander produced %d instance keys for a singleton module call", len(knownKeys))) } - - // Insert unknown values where there are any missing instances - for i, v := range vals { - if v.IsNull() { - vals[i] = cty.DynamicVal - continue - } + if knownKeys[0] != addrs.NoKey { + panic(fmt.Sprintf("instance expander produced instance key %#v for a singleton module call", knownKeys[0])) } - ret = cty.TupleVal(vals) - } else { - ret = cty.EmptyTupleVal - } - case callConfig.ForEach != nil: - vals := make(map[string]cty.Value) - for key, instance := range moduleInstances { - strKey, ok := key.(addrs.StringKey) - if !ok { - continue + // Now we've proven that we do indeed have only one instance key + // and it's NoKey, we can safely disregard knownKeys when building + // the result. + // The output values actually live in the child module instance, + // so we need to extend our module instance address to include that. + moduleAddr := d.ModulePath.Child(addr.Name, addrs.NoKey) + attrs := d.moduleCallInstanceOutputValues(moduleAddr, outputConfigs) + return cty.ObjectVal(attrs), diags + + case addrs.IntKeyType: + // For IntKeyType the expander should return the keys in index order, + // and we'll assume that while constructing this tuple. + insts := make([]cty.Value, len(knownKeys)) + for i, instKey := range knownKeys { + // The output values actually live in the child module instance, + // so we need to extend our module instance address to include that. + moduleAddr := d.ModulePath.Child(addr.Name, instKey) + attrs := d.moduleCallInstanceOutputValues(moduleAddr, outputConfigs) + insts[i] = cty.ObjectVal(attrs) + } + return cty.TupleVal(insts), diags + + case addrs.StringKeyType: + insts := make(map[string]cty.Value, len(knownKeys)) + for _, instKey := range knownKeys { + // The output values actually live in the child module instance, + // so we need to extend our module instance address to include that. + moduleAddr := d.ModulePath.Child(addr.Name, instKey) + attrs := d.moduleCallInstanceOutputValues(moduleAddr, outputConfigs) + insts[string(instKey.(addrs.StringKey))] = cty.ObjectVal(attrs) } + return cty.ObjectVal(insts), diags - vals[string(strKey)] = cty.ObjectVal(instance) + default: + // The above cases should be exhaustive for all key types except + // for UnknownKeyType, which we handled in the if statement above. + panic(fmt.Sprintf("unsupported instance key type %s", keyType)) } + } +} - if len(vals) > 0 { - ret = cty.ObjectVal(vals) +func (d *evaluationStateData) moduleCallInstanceOutputValues(moduleAddr addrs.ModuleInstance, outputConfigs map[string]*configs.Output) map[string]cty.Value { + attrs := make(map[string]cty.Value, len(outputConfigs)) + for name, cfg := range outputConfigs { + outputAddr := addrs.OutputValue{Name: name}.Absolute(moduleAddr) + // In situations where Terraform can prove that a particular expression + // refers only to a single output value of a module instance the + // graph allows evaluating the object representing a module before + // all of the output values have been evaluated, so we must generate + // placeholders for any that aren't ready yet. This oddity is just + // so we can reuse the GetModule method as a "get individual output" + // method rather than having to implement essentially the same logic + // in two different ways. + if !d.Evaluator.NamedValues.HasOutputValue(outputAddr) { + attrs[name] = cty.DynamicVal } else { - ret = cty.EmptyObjectVal - } - - default: - val, ok := moduleInstances[addrs.NoKey] - if !ok { - // create the object if there wasn't one known - val = map[string]cty.Value{} - for k := range outputConfigs { - val[k] = cty.DynamicVal - } + attrs[name] = d.Evaluator.NamedValues.GetOutputValue(outputAddr) } - - ret = cty.ObjectVal(val) - } - - // The module won't be expanded during validation, so we need to return an - // unknown value. This will ensure the types looks correct, since we built - // the objects based on the configuration. - if d.Operation == walkValidate { - // While we know the type here and it would be nice to validate whether - // indexes are valid or not, because tuples and objects have fixed - // numbers of elements we can't simply return an unknown value of the - // same type since we have not expanded any instances during - // validation. - // - // In order to validate the expression a little precisely, we'll create - // an unknown map or list here to get more type information. - ty := cty.Object(unknownMap) - switch { - case callConfig.Count != nil: - ret = cty.UnknownVal(cty.List(ty)) - case callConfig.ForEach != nil: - ret = cty.UnknownVal(cty.Map(ty)) - default: - ret = cty.UnknownVal(ty) + if cfg.Sensitive { + attrs[name] = attrs[name].Mark(marks.Sensitive) } } - - return ret, diags + return attrs } func (d *evaluationStateData) GetPathAttr(addr addrs.PathAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) { diff --git a/internal/terraform/evaluate_test.go b/internal/terraform/evaluate_test.go index e7a3e841add1..6a3e90bc36b2 100644 --- a/internal/terraform/evaluate_test.go +++ b/internal/terraform/evaluate_test.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/configs/configschema" + "github.com/hashicorp/terraform/internal/instances" "github.com/hashicorp/terraform/internal/lang/marks" "github.com/hashicorp/terraform/internal/namedvals" "github.com/hashicorp/terraform/internal/plans" @@ -26,6 +27,7 @@ func TestEvaluatorGetTerraformAttr(t *testing.T) { Env: "foo", }, NamedValues: namedvals.NewState(), + Instances: instances.NewExpander(), } data := &evaluationStateData{ Evaluator: evaluator, @@ -57,6 +59,7 @@ func TestEvaluatorGetPathAttr(t *testing.T) { }, }, NamedValues: namedvals.NewState(), + Instances: instances.NewExpander(), } data := &evaluationStateData{ Evaluator: evaluator, @@ -191,6 +194,7 @@ func TestEvaluatorGetInputVariable(t *testing.T) { }, }, NamedValues: namedValues, + Instances: instances.NewExpander(), } data := &evaluationStateData{ @@ -270,6 +274,7 @@ func TestEvaluatorGetResource(t *testing.T) { }, State: stateSync, NamedValues: namedvals.NewState(), + Instances: instances.NewExpander(), Plugins: schemaOnlyProvidersForTesting(map[addrs.Provider]providers.ProviderSchema{ addrs.NewDefaultProvider("test"): { ResourceTypes: map[string]providers.Schema{ @@ -508,6 +513,7 @@ func TestEvaluatorGetResource_changes(t *testing.T) { }, State: stateSync, NamedValues: namedvals.NewState(), + Instances: instances.NewExpander(), Plugins: schemaOnlyProvidersForTesting(schemas.Providers), } @@ -537,15 +543,7 @@ func TestEvaluatorGetResource_changes(t *testing.T) { } func TestEvaluatorGetModule(t *testing.T) { - // Create a new evaluator with an existing state - stateSync := states.BuildState(func(ss *states.SyncState) { - ss.SetOutputValue( - addrs.OutputValue{Name: "out"}.Absolute(addrs.ModuleInstance{addrs.ModuleInstanceStep{Name: "mod"}}), - cty.StringVal("bar"), - true, - ) - }).SyncWrapper() - evaluator := evaluatorForModule(stateSync, plans.NewChanges().SyncWrapper()) + evaluator := evaluatorForModule(states.NewState().SyncWrapper(), plans.NewChanges().SyncWrapper()) evaluator.NamedValues.SetOutputValue( addrs.OutputValue{Name: "out"}.Absolute(addrs.ModuleInstance{addrs.ModuleInstanceStep{Name: "mod"}}), cty.StringVal("bar").Mark(marks.Sensitive), @@ -565,55 +563,12 @@ func TestEvaluatorGetModule(t *testing.T) { if !got.RawEquals(want) { t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, want) } - - // Changes should override the state value - changesSync := plans.NewChanges().SyncWrapper() - change := &plans.OutputChange{ - Addr: addrs.OutputValue{Name: "out"}.Absolute(addrs.ModuleInstance{addrs.ModuleInstanceStep{Name: "mod"}}), - Sensitive: true, - Change: plans.Change{ - After: cty.StringVal("baz"), - }, - } - cs, _ := change.Encode() - changesSync.AppendOutputChange(cs) - evaluator = evaluatorForModule(stateSync, changesSync) - data = &evaluationStateData{ - Evaluator: evaluator, - } - scope = evaluator.Scope(data, nil, nil) - want = cty.ObjectVal(map[string]cty.Value{"out": cty.StringVal("baz").Mark(marks.Sensitive)}) - got, diags = scope.Data.GetModule(addrs.ModuleCall{ - Name: "mod", - }, tfdiags.SourceRange{}) - - if len(diags) != 0 { - t.Errorf("unexpected diagnostics %s", spew.Sdump(diags)) - } - if !got.RawEquals(want) { - t.Errorf("wrong result %#v; want %#v", got, want) - } - - // Test changes with empty state - evaluator = evaluatorForModule(states.NewState().SyncWrapper(), changesSync) - data = &evaluationStateData{ - Evaluator: evaluator, - } - scope = evaluator.Scope(data, nil, nil) - want = cty.ObjectVal(map[string]cty.Value{"out": cty.StringVal("baz").Mark(marks.Sensitive)}) - got, diags = scope.Data.GetModule(addrs.ModuleCall{ - Name: "mod", - }, tfdiags.SourceRange{}) - - if len(diags) != 0 { - t.Errorf("unexpected diagnostics %s", spew.Sdump(diags)) - } - if !got.RawEquals(want) { - t.Errorf("wrong result %#v; want %#v", got, want) - } } func evaluatorForModule(stateSync *states.SyncState, changesSync *plans.ChangesSync) *Evaluator { + insts := instances.NewExpander() + insts.SetModuleSingle(addrs.RootModuleInstance, addrs.ModuleCall{Name: "mod"}) + return &Evaluator{ Meta: &ContextMeta{ Env: "foo", @@ -643,6 +598,7 @@ func evaluatorForModule(stateSync *states.SyncState, changesSync *plans.ChangesS State: stateSync, Changes: changesSync, NamedValues: namedvals.NewState(), + Instances: insts, } } diff --git a/internal/terraform/node_output.go b/internal/terraform/node_output.go index e83d37517376..e37e8cac199f 100644 --- a/internal/terraform/node_output.go +++ b/internal/terraform/node_output.go @@ -120,7 +120,14 @@ func (n *nodeExpandOutput) DynamicExpand(ctx EvalContext) (*Graph, tfdiags.Diagn } } - log.Printf("[TRACE] Expanding output: adding %s as %T", absAddr.String(), node) + log.Printf("[TRACE] nodeExpandOutput: adding %s as %T", absAddr.String(), node) + g.Add(node) + } + for _, module := range expander.UnknownModuleInstances(n.Module) { + node := &nodeOutputUnexpandedPlaceholder{ + Addr: addrs.ObjectInPartialExpandedModule(module, n.Addr), + Config: n.Config, + } g.Add(node) } addRootNodeToGraph(&g) @@ -269,9 +276,10 @@ func referenceableAddrsForOutput(addr addrs.AbsOutputValue) []addrs.Referenceabl // relative to the calling module, not the module where the output // was declared. _, outp := addr.ModuleCallOutput() - _, call := addr.Module.CallInstance() + _, callInst := addr.Module.CallInstance() + _, call := addr.Module.Call() - return []addrs.Referenceable{outp, call} + return []addrs.Referenceable{outp, callInst, call} } // GraphNodeReferenceable @@ -594,7 +602,7 @@ func (n *NodeApplyableOutput) setValue(namedVals *namedvals.State, state *states // Should never happen, since we just constructed this right above panic(fmt.Sprintf("planned change for %s could not be encoded: %s", n.Addr, err)) } - log.Printf("[TRACE] setValue: Saving %s change for %s in changeset", change.Action, n.Addr) + log.Printf("[TRACE] NodeApplyableOutput.setValue: Saving %s change for %s in changeset", change.Action, n.Addr) changes.AppendOutputChange(cs) // add the new planned change } @@ -608,7 +616,7 @@ func (n *NodeApplyableOutput) setValue(namedVals *namedvals.State, state *states // evaluated. Null root outputs are removed entirely, which is always fine // because they can't be referenced by anything else in the configuration. if n.Addr.Module.IsRoot() && val.IsNull() { - log.Printf("[TRACE] setValue: Removing %s from state (it is now null)", n.Addr) + log.Printf("[TRACE] NodeApplyableOutput.setValue: Removing %s from state (it is now null)", n.Addr) state.RemoveOutputValue(n.Addr) return } @@ -623,7 +631,7 @@ func (n *NodeApplyableOutput) setValue(namedVals *namedvals.State, state *states // The state itself doesn't represent unknown values, so we null them // out here and then we'll save the real unknown value in the planned // changeset, if we have one on this graph walk. - log.Printf("[TRACE] setValue: Saving value for %s in state", n.Addr) + log.Printf("[TRACE] NodeApplyableOutput.setValue: Saving value for %s in state", n.Addr) // non-root outputs need to keep sensitive marks for evaluation, but are // not serialized. if n.Addr.Module.IsRoot() { @@ -633,3 +641,72 @@ func (n *NodeApplyableOutput) setValue(namedVals *namedvals.State, state *states state.SetOutputValue(n.Addr, val, n.Config.Sensitive) } + +type nodeOutputUnexpandedPlaceholder struct { + Addr addrs.InPartialExpandedModule[addrs.OutputValue] + Config *configs.Output +} + +var ( + _ GraphNodePartialExpandedModule = (*nodeOutputUnexpandedPlaceholder)(nil) + _ GraphNodeReferenceable = (*nodeOutputUnexpandedPlaceholder)(nil) + _ GraphNodeReferencer = (*nodeOutputUnexpandedPlaceholder)(nil) + _ GraphNodeReferenceOutside = (*nodeOutputUnexpandedPlaceholder)(nil) + _ GraphNodeExecutable = (*nodeOutputUnexpandedPlaceholder)(nil) + _ graphNodeTemporaryValue = (*nodeOutputUnexpandedPlaceholder)(nil) +) + +func (n *nodeOutputUnexpandedPlaceholder) temporaryValue() bool { + return !n.Addr.Module.Module().IsRoot() +} + +func (n *nodeOutputUnexpandedPlaceholder) Name() string { + return n.Addr.String() +} + +// GraphNodePartialExpandedModule +func (n *nodeOutputUnexpandedPlaceholder) PartialExpandedModule() addrs.PartialExpandedModule { + return n.Addr.Module +} + +// GraphNodeReferenceOutside implementation +func (n *nodeOutputUnexpandedPlaceholder) ReferenceOutside() (selfPath, referencePath addrs.Module) { + // Output values have their expressions resolved in the context of the + // module where they are defined. + referencePath = n.Addr.Module.Module() + + // ...but they are referenced in the context of their calling module. + selfPath = referencePath.Parent() + + return // uses named return values +} + +// GraphNodeReferenceable +func (n *nodeOutputUnexpandedPlaceholder) ReferenceableAddrs() []addrs.Referenceable { + // For an output inside an unexpanded module we don't actually have any + // specific module call instance addresses to return; the best we can + // do is the entire call that we're declared within, which should then + // soak up any access to any instance of it, and therefore any output + // value within any instance of it. + moduleAddr := n.Addr.Module.Module() + callName := moduleAddr[len(moduleAddr)-1] + call := addrs.ModuleCall{Name: callName} + return []addrs.Referenceable{call} +} + +func (n *nodeOutputUnexpandedPlaceholder) References() []*addrs.Reference { + return referencesForOutput(n.Config) +} + +// Execute implements GraphNodeExecutable +func (n *nodeOutputUnexpandedPlaceholder) Execute(ctx EvalContext, op walkOperation) tfdiags.Diagnostics { + val, diags := ctx.EvaluateExpr(n.Config.Expr, cty.DynamicPseudoType, nil) + if diags.HasErrors() { + val = cty.DynamicVal + } + diags = diags.Append(validateDependsOn(ctx, n.Config.DependsOn)) + + ctx.NamedValues().SetOutputValuePlaceholder(n.Addr, val) + + return diags +} From 14f54c5e55f838f8e9a89616f676d158b2ff40c9 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Tue, 14 Mar 2023 19:06:17 -0700 Subject: [PATCH 17/17] core: Beginnings of placeholders for resources with unknown expansion This doesn't actually do anything useful yet, but at least stubs out how evaluation for these might work in later commits. --- internal/addrs/module_scope.go | 20 ++ internal/addrs/partial_expanded.go | 39 ++++ internal/terraform/graph.go | 23 ++- .../terraform/graph_interface_subgraph.go | 15 ++ internal/terraform/node_resource_abstract.go | 32 ++-- internal/terraform/node_resource_plan.go | 145 ++++++++++----- .../node_resource_plan_unexpanded.go | 175 ++++++++++++++++++ 7 files changed, 383 insertions(+), 66 deletions(-) create mode 100644 internal/addrs/module_scope.go create mode 100644 internal/terraform/node_resource_plan_unexpanded.go diff --git a/internal/addrs/module_scope.go b/internal/addrs/module_scope.go new file mode 100644 index 000000000000..4a39a486c96a --- /dev/null +++ b/internal/addrs/module_scope.go @@ -0,0 +1,20 @@ +package addrs + +// ModuleEvalScope represents the different kinds of module scope that +// Terraform Core can evaluate expressions within. +// +// Its only two implementations are [ModuleInstance] and +// [PartialExpandedModule], where the latter represents the evaluation context +// for the "partial evaluation" mode which produces placeholder values for +// not-yet-expanded modules. +// +// A nil ModuleEvalScope represents no evaluation scope at all, whereas a +// typed ModuleEvalScope represents either an exact expanded module or a +// partial-expanded module. +type ModuleEvalScope interface { + moduleEvalScopeSigil() +} + +func (ModuleInstance) moduleEvalScopeSigil() {} + +func (PartialExpandedModule) moduleEvalScopeSigil() {} diff --git a/internal/addrs/partial_expanded.go b/internal/addrs/partial_expanded.go index d324f018cb25..1b8cea469764 100644 --- a/internal/addrs/partial_expanded.go +++ b/internal/addrs/partial_expanded.go @@ -346,6 +346,45 @@ func (per PartialExpandedResource) AbsResource() (AbsResource, bool) { }, true } +// FullyExpandedModuleInstance returns the [ModuleInstance] that the receiver +// belongs to if and only if it belongs to a fully-known module path. +// +// The second return value is true only if the first return value is valid. +func (per PartialExpandedResource) FullyExpandedModuleInstance() (ModuleInstance, bool) { + if len(per.module.unexpandedSuffix) != 0 { + return nil, false + } + return per.module.expandedPrefix, true +} + +// PartialExpandedModuleInstance returns the [PartialExpandedModule] that the +// receiver belongs to if and only if the module path is not fully known. +// For a fully-known path use [PartialExpandedResource.FullyExpandedModuleInstance] +// instead, to obtain a [ModuleInstance] +func (per PartialExpandedResource) PartialExpandedModuleInstance() (PartialExpandedModule, bool) { + // We can only reveal our module field's value if it has at least one + // expanded element, because otherwise it will violate the assumptions + // made in the exported API of PartialExpandedModule. + if len(per.module.unexpandedSuffix) == 0 { + return PartialExpandedModule{}, false + } + return per.module, true +} + +// ModuleEvalScope returns the [ModuleEvalScope] that the receiver should +// have its expressions evaluated in. +func (per PartialExpandedResource) ModuleEvalScope() ModuleEvalScope { + if addr, ok := per.FullyExpandedModuleInstance(); ok { + return addr + } else if addr, ok := per.PartialExpandedModuleInstance(); ok { + return addr + } else { + // Should never get here because we should always have exactly one + // of the two address types above. + panic("unexpected ModuleEvalScope type for PartialExpandedResource") + } +} + // ConfigResource returns the unexpanded resource address that this // partially-expanded resource address originates from. func (per PartialExpandedResource) ConfigResource() ConfigResource { diff --git a/internal/terraform/graph.go b/internal/terraform/graph.go index e68da3bd7838..f29fa4d44543 100644 --- a/internal/terraform/graph.go +++ b/internal/terraform/graph.go @@ -71,13 +71,30 @@ func (g *Graph) walk(walker GraphWalker) tfdiags.Diagnostics { // is normally the context of our graph but can be overridden // with a GraphNodeModuleInstance impl. vertexCtx := ctx - if pn, ok := v.(GraphNodeModuleInstance); ok { + switch pn := v.(type) { + case GraphNodeModuleInstance: vertexCtx = walker.EnterPath(pn.Path()) defer walker.ExitPath(pn.Path()) - } - if pn, ok := v.(GraphNodePartialExpandedModule); ok { + case GraphNodePartialExpandedModule: vertexCtx = walker.EnterPartialExpandedPath(pn.PartialExpandedModule()) defer walker.ExitPartialExpandedPath(pn.PartialExpandedModule()) + case GraphNodeModuleEvalScope: + // This is a dynamic variant that combines the two cases above and + // the possibility to have no scope at all, for situations where + // the same node type needs to handle two or more kinds of + // evaluation. + switch evalScope := pn.ModuleEvalScope().(type) { + case addrs.ModuleInstance: + vertexCtx = walker.EnterPath(evalScope) + defer walker.ExitPath(evalScope) + case addrs.PartialExpandedModule: + vertexCtx = walker.EnterPartialExpandedPath(evalScope) + defer walker.ExitPartialExpandedPath(evalScope) + case nil: + // Fine but we don't have anything to do in this case. + default: + panic(fmt.Sprintf("unsupported addrs.ModuleEvalScope implementation %T", evalScope)) + } } if g.checkAndApplyOverrides(ctx.Overrides(), v) { diff --git a/internal/terraform/graph_interface_subgraph.go b/internal/terraform/graph_interface_subgraph.go index 025b6518cc21..a194c99ad130 100644 --- a/internal/terraform/graph_interface_subgraph.go +++ b/internal/terraform/graph_interface_subgraph.go @@ -26,3 +26,18 @@ type GraphNodeModulePath interface { type GraphNodePartialExpandedModule interface { PartialExpandedModule() addrs.PartialExpandedModule } + +// GraphNodeModuleEvalScope is a dynamic variant of both [GraphNodeModuleInstance] +// and [GraphNodePartialExpandedModule] combined, which a node can implement +// to dynamically choose between: +// +// - nil, representing no module evaluation scope at all +// - an [addrs.ModuleInstance], representing an exact module instance with similar effect to implementing [GraphNodeModuleInstance] +// - an [addrs.PartialExpandedModule], representing a partial-expanded module instance with similar effect to implementing [GraphNodePartialExpandedModule] +// +// When calling a node's [GraphNodeExecutable] implementation, the graph +// walker will pass an [EvalContext] with the appropriate module evaluation +// scope (if any) pre-assigned. +type GraphNodeModuleEvalScope interface { + ModuleEvalScope() addrs.ModuleEvalScope +} diff --git a/internal/terraform/node_resource_abstract.go b/internal/terraform/node_resource_abstract.go index 437371cfbedd..b21f9aa7467a 100644 --- a/internal/terraform/node_resource_abstract.go +++ b/internal/terraform/node_resource_abstract.go @@ -142,15 +142,20 @@ func (n *NodeAbstractResource) ReferenceableAddrs() []addrs.Referenceable { // GraphNodeReferencer func (n *NodeAbstractResource) References() []*addrs.Reference { + return referencesForResource(n.Addr, n.Config, n.Schema, n.ProvisionerSchemas) +} + +func referencesForResource(addr addrs.ConfigResource, config *configs.Resource, schema *configschema.Block, provisionerSchemas map[string]*configschema.Block) []*addrs.Reference { var result []*addrs.Reference // If we have a config then we prefer to use that. - if c := n.Config; c != nil { - result = append(result, n.DependsOn()...) + if c := config; c != nil { + result = append(result, resourceDependsOn(addr, config)...) - if n.Schema == nil { + if schema == nil { // Should never happen, but we'll log if it does so that we can // see this easily when debugging. - log.Printf("[WARN] no schema is attached to %s, so config references cannot be detected", n.Name()) + log.Printf("[WARN] no schema is attached to %s, so config references cannot be detected", addr) + } refs, _ := lang.ReferencesInExpr(addrs.ParseRef, c.Count) @@ -164,8 +169,8 @@ func (n *NodeAbstractResource) References() []*addrs.Reference { } // ReferencesInBlock() requires a schema - if n.Schema != nil { - refs, _ = lang.ReferencesInBlock(addrs.ParseRef, c.Config, n.Schema) + if schema != nil { + refs, _ = lang.ReferencesInBlock(addrs.ParseRef, c.Config, schema) result = append(result, refs...) } @@ -184,9 +189,9 @@ func (n *NodeAbstractResource) References() []*addrs.Reference { result = append(result, refs...) } - schema := n.ProvisionerSchemas[p.Type] + schema := provisionerSchemas[p.Type] if schema == nil { - log.Printf("[WARN] no schema for provisioner %q is attached to %s, so provisioner block references cannot be detected", p.Type, n.Name()) + log.Printf("[WARN] no schema for provisioner %q is attached to %s, so provisioner block references cannot be detected", p.Type, addr) } refs, _ = lang.ReferencesInBlock(addrs.ParseRef, p.Config, schema) result = append(result, refs...) @@ -227,16 +232,19 @@ func (n *NodeAbstractResource) ImportReferences() []*addrs.Reference { } func (n *NodeAbstractResource) DependsOn() []*addrs.Reference { - var result []*addrs.Reference - if c := n.Config; c != nil { + return resourceDependsOn(n.Addr, n.Config) +} - for _, traversal := range c.DependsOn { +func resourceDependsOn(addr addrs.ConfigResource, config *configs.Resource) []*addrs.Reference { + var result []*addrs.Reference + if config != nil { + for _, traversal := range config.DependsOn { ref, diags := addrs.ParseRef(traversal) if diags.HasErrors() { // We ignore this here, because this isn't a suitable place to return // errors. This situation should be caught and rejected during // validation. - log.Printf("[ERROR] Can't parse %#v from depends_on as reference: %s", traversal, diags.Err()) + log.Printf("[ERROR] Can't parse %#v from depends_on of %s as reference: %s", traversal, addr, diags.Err()) continue } diff --git a/internal/terraform/node_resource_plan.go b/internal/terraform/node_resource_plan.go index 041d414293bd..17bc2be7f05b 100644 --- a/internal/terraform/node_resource_plan.go +++ b/internal/terraform/node_resource_plan.go @@ -108,57 +108,6 @@ func (n *nodeExpandPlannableResource) DynamicExpand(ctx EvalContext) (*Graph, tf expander := ctx.InstanceExpander() moduleInstances := expander.ExpandModule(n.Addr.Module) - // Lock the state while we inspect it - state := ctx.State().Lock() - - var orphans []*states.Resource - for _, res := range state.Resources(n.Addr) { - found := false - for _, m := range moduleInstances { - if m.Equal(res.Addr.Module) { - found = true - break - } - } - // The module instance of the resource in the state doesn't exist - // in the current config, so this whole resource is orphaned. - if !found { - orphans = append(orphans, res) - } - } - - // We'll no longer use the state directly here, and the other functions - // we'll call below may use it so we'll release the lock. - state = nil - ctx.State().Unlock() - - // The concrete resource factory we'll use for orphans - concreteResourceOrphan := func(a *NodeAbstractResourceInstance) *NodePlannableResourceInstanceOrphan { - // Add the config and state since we don't do that via transforms - a.Config = n.Config - a.ResolvedProvider = n.ResolvedProvider - a.Schema = n.Schema - a.ProvisionerSchemas = n.ProvisionerSchemas - a.ProviderMetas = n.ProviderMetas - a.Dependencies = n.dependencies - - return &NodePlannableResourceInstanceOrphan{ - NodeAbstractResourceInstance: a, - skipRefresh: n.skipRefresh, - skipPlanChanges: n.skipPlanChanges, - } - } - - for _, res := range orphans { - for key := range res.Instances { - addr := res.Addr.Instance(key) - abs := NewNodeAbstractResourceInstance(addr) - abs.AttachResourceState(res) - n := concreteResourceOrphan(abs) - g.Add(n) - } - } - // The above dealt with the expansion of the containing module, so now // we need to deal with the expansion of the resource itself across all // instances of the module. @@ -189,6 +138,32 @@ func (n *nodeExpandPlannableResource) DynamicExpand(ctx EvalContext) (*Graph, tf checkState.ReportCheckableObjects(n.NodeAbstractResource.Addr, n.expandedInstances) } + // Any resources that belong to module prefixes that aren't expanded yet + // get placeholder nodes that we'll use to record that they are deferred + // and produce a placeholder object for continued downstream partial + // evaluation. + // (This must come after calling n.expandResourceInstances because + // otherwise we won't have registered the expansion for the resource + // itself yet.) + possibleInstances := expander.UnknownResourceInstances(n.Addr) + for _, possibleInsts := range possibleInstances { + log.Printf("[TRACE] nodeExpandPlannableResource: exact instances not yet known for %s", possibleInsts) + n := &nodePartialExpandedResource{ + Addr: possibleInsts, + Config: n.Config, + Schema: n.Schema, + SchemaVersion: n.SchemaVersion, + ProvisionerSchemas: n.ProvisionerSchemas, + Targets: n.Targets, + ResolvedProvider: n.ResolvedProvider, + skipRefresh: n.skipRefresh, + skipPlanChanges: n.skipPlanChanges, + } + g.Add(n) + } + + n.expandResourceInstanceOrphans(&g, ctx, moduleInstances, possibleInstances) + addRootNodeToGraph(&g) return &g, diags @@ -327,6 +302,74 @@ func (n *nodeExpandPlannableResource) expandResourceInstances(globalCtx EvalCont return diags } +func (n *nodeExpandPlannableResource) expandResourceInstanceOrphans(g *Graph, ctx EvalContext, moduleInstances []addrs.ModuleInstance, possibleInstances addrs.Set[addrs.PartialExpandedResource]) error { + var diags tfdiags.Diagnostics + + // Lock the state while we inspect it + state := ctx.State().Lock() + + var orphans []*states.Resource + for _, res := range state.Resources(n.Addr) { + found := false + for _, m := range moduleInstances { + if m.Equal(res.Addr.Module) { + found = true + break + } + } + // The existing instance might belong to a resource or module whose + // expansion isn't known yet, so we don't know yet if it will still + // exist in desired state once the expansion is known; we'll just + // treat it as a placeholder for this run. + deferred := false + for _, possibleInsts := range possibleInstances { + if possibleInsts.MatchesResource(res.Addr) { + deferred = true + break + } + } + // The module instance of the resource in the state doesn't exist + // in the current config, so this whole resource is orphaned. + if !found && !deferred { + orphans = append(orphans, res) + } + } + + // We'll no longer use the state directly here, and the other functions + // we'll call below may use it so we'll release the lock. + state = nil + ctx.State().Unlock() + + // The concrete resource factory we'll use for orphans + concreteResourceOrphan := func(a *NodeAbstractResourceInstance) *NodePlannableResourceInstanceOrphan { + // Add the config and state since we don't do that via transforms + a.Config = n.Config + a.ResolvedProvider = n.ResolvedProvider + a.Schema = n.Schema + a.ProvisionerSchemas = n.ProvisionerSchemas + a.ProviderMetas = n.ProviderMetas + a.Dependencies = n.dependencies + + return &NodePlannableResourceInstanceOrphan{ + NodeAbstractResourceInstance: a, + skipRefresh: n.skipRefresh, + skipPlanChanges: n.skipPlanChanges, + } + } + + for _, res := range orphans { + for key := range res.Instances { + addr := res.Addr.Instance(key) + abs := NewNodeAbstractResourceInstance(addr) + abs.AttachResourceState(res) + n := concreteResourceOrphan(abs) + g.Add(n) + } + } + + return diags.ErrWithWarnings() +} + // Import blocks are expanded in conjunction with their associated resource block. func (n nodeExpandPlannableResource) expandResourceImports(ctx EvalContext, addr addrs.AbsResource, instanceAddrs []addrs.AbsResourceInstance) (addrs.Map[addrs.AbsResourceInstance, string], tfdiags.Diagnostics) { // Imports maps the target address to an import ID. diff --git a/internal/terraform/node_resource_plan_unexpanded.go b/internal/terraform/node_resource_plan_unexpanded.go new file mode 100644 index 000000000000..0de268d1c129 --- /dev/null +++ b/internal/terraform/node_resource_plan_unexpanded.go @@ -0,0 +1,175 @@ +package terraform + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/internal/addrs" + "github.com/hashicorp/terraform/internal/configs" + "github.com/hashicorp/terraform/internal/configs/configschema" + "github.com/hashicorp/terraform/internal/instances" + "github.com/hashicorp/terraform/internal/plans/objchange" + "github.com/hashicorp/terraform/internal/tfdiags" + "github.com/zclconf/go-cty/cty" +) + +// nodePartialExpandedResource represents an undetermined set of resource +// instances that all share the same [addrs.ConfigResource] address but +// may have certain module instance keys known. +// +// Terraform uses nodes of this type to produce "placeholder plans" that +// are not applyable but approximate the final state of all of the instances +// by using unknown values to stand in for any values that might vary between +// the instances. The goal here is just to help the operator get a sense of +// how their resource configurations will be interpreted even when we don't +// have enough information to plan individual instances fully. +type nodePartialExpandedResource struct { + Addr addrs.PartialExpandedResource + + Config *configs.Resource + Schema *configschema.Block + SchemaVersion uint64 // Schema version of "Schema", as decided by the provider + + ProvisionerSchemas map[string]*configschema.Block + + // Set from GraphNodeTargetable + Targets []addrs.Targetable + + // The address of the provider this resource will use + ResolvedProvider addrs.AbsProviderConfig + + // skipRefresh indicates that we should skip refreshing individual instances + skipRefresh bool + + // skipPlanChanges indicates we should skip trying to plan change actions + // for any instances. + skipPlanChanges bool +} + +func (n *nodePartialExpandedResource) Name() string { + return n.Addr.String() +} + +var ( + _ GraphNodeModuleEvalScope = (*nodePartialExpandedResource)(nil) + _ GraphNodeExecutable = (*nodePartialExpandedResource)(nil) + _ GraphNodeReferenceable = (*nodePartialExpandedResource)(nil) + _ GraphNodeReferencer = (*nodePartialExpandedResource)(nil) +) + +// ModuleEvalScope implements GraphNodeModuleEvalScope +func (n *nodePartialExpandedResource) ModuleEvalScope() addrs.ModuleEvalScope { + // This could either be an addrs.ModuleInstance or an + // addrs.PartialExpandedModule depending on whether it's just the + // resource's own expansion that isn't known, or if some or all of the + // module address is also unknown. + return n.Addr.ModuleEvalScope() +} + +// Execute implements GraphNodeExecutable +func (n *nodePartialExpandedResource) Execute(ctx EvalContext, op walkOperation) tfdiags.Diagnostics { + // The ctx we get here can perform expression evaluation but might do so + // in the "partial evaluation" mode if this node is representing instances + // of a resource in a module whose expansion isn't known yet either. + log.Printf("[TRACE] nodePartialExpandedResource: Generate placeholder object for all instances matching %s", n.Addr) + + configVal, schema, diags := n.evaluateConfig(ctx, op) + if diags.HasErrors() { + return diags + } + + switch mode := n.Addr.Resource().Mode; mode { + case addrs.ManagedResourceMode: + return n.executeManagedResource(ctx, op, configVal, schema) + case addrs.DataResourceMode: + return n.executeDataResource(ctx, op, configVal, schema) + default: + panic(fmt.Sprintf("unsupported resource mode %s", mode)) + } +} + +func (n *nodePartialExpandedResource) evaluateConfig(ctx EvalContext, op walkOperation) (cty.Value, *configschema.Block, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + + // TODO: Preconditions + + _, providerSchema, err := getProvider(ctx, n.ResolvedProvider) + diags = diags.Append(err) + if diags.HasErrors() { + return cty.DynamicVal, nil, diags + } + + config := n.Config + schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.Resource()) + if schema == nil { + // Should be caught during validation, so we don't bother with a pretty error here + diags = diags.Append(fmt.Errorf("provider does not support resource type %q", n.Addr.Resource().Type)) + return cty.DynamicVal, nil, diags + } + + var keyData instances.RepetitionData + switch { + case config.ForEach != nil: + // TODO: Somehow determine the for_each type here, ideally without + // re-evaluating it, so that the keyData can have a more specific + // type constraint for its each.value value. + keyData = instances.UnknownForEachRepetitionData(cty.DynamicPseudoType) + case config.Count != nil: + keyData = instances.UnknownCountRepetitionData + } + + configVal, _, configDiags := ctx.EvaluateBlock(config.Config, schema, nil, keyData) + diags = diags.Append(configDiags) + return configVal, schema, diags +} + +func (n *nodePartialExpandedResource) executeManagedResource(ctx EvalContext, op walkOperation, configVal cty.Value, schema *configschema.Block) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + // TODO: Ask the provider to validate the configuration. + + proposedNewVal := objchange.ProposedNew(schema, cty.NullVal(schema.ImpliedType()), configVal) + + // TODO: Ask the provider to PlanResourceChange with the proposed new + // value so we can find out about any plan-time-checked problems early + // and to potentially generate a more complete placeholder value. + + log.Printf("[TRACE] nodePartialExpandedResource: all %s are %#v", n.Addr, proposedNewVal) + + // TODO: Postconditions + + return diags +} + +func (n *nodePartialExpandedResource) executeDataResource(ctx EvalContext, op walkOperation, configVal cty.Value, schema *configschema.Block) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + // TODO: Ask the provider to validate the configuration. + + // TODO: If the configuration is fully known and the dependencies have no + // pending changes then we should proactively read the data source now + // and use its result as the placeholder for all instances of this + // resource. + + // TODO: If the configuration isn't fully known then we should put a plan + // to read all instances of this data resource into the bucket of deferred + // actions. + proposedNewVal := objchange.PlannedDataResourceObject(schema, configVal) + log.Printf("[TRACE] nodePartialExpandedResource: all %s are %#v", n.Addr, proposedNewVal) + + // TODO: Postconditions + + return diags +} + +// GraphNodeReferenceable +func (n *nodePartialExpandedResource) ReferenceableAddrs() []addrs.Referenceable { + return []addrs.Referenceable{ + n.Addr.ConfigResource().Resource, + } +} + +// GraphNodeReferencer +func (n *nodePartialExpandedResource) References() []*addrs.Reference { + return referencesForResource(n.Addr.ConfigResource(), n.Config, n.Schema, n.ProvisionerSchemas) +}