From 8d193ad268c6601ac917194e24f581ad36bb91d0 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 29 Sep 2021 15:51:09 -0700 Subject: [PATCH] core: Simplify and centralize plugin availability checks Historically the responsibility for making sure that all of the available providers are of suitable versions and match the appropriate checksums has been split rather inexplicably over multiple different layers, with some of the checks happening as late as creating a terraform.Context. We're gradually iterating towards making that all be handled in one place, but in this step we're just cleaning up some old remnants from the main "terraform" package, which is now no longer responsible for any version or checksum verification and instead just assumes it's been provided with suitable factory functions by its caller. We do still have a pre-check here to make sure that we at least have a factory function for each plugin the configuration seems to depend on, because if we don't do that up front then it ends up getting caught instead deep inside the Terraform runtime, often inside a concurrent graph walk and thus it's not deterministic which codepath will happen to catch it on a particular run. As of this commit, this actually does leave some holes in our checks: the command package is using the dependency lock file to make sure we have exactly the provider packages we expect (exact versions and checksums), which is the most crucial part, but we don't yet have any spot where we make sure that the lock file is consistent with the current configuration, and we are no longer preserving the provider checksums as part of a saved plan. Both of those will come in subsequent commits. While it's unusual to have a series of commits that briefly subtracts functionality and then adds back in equivalent functionality later, the lock file checking is the only part that's crucial for security reasons, with everything else mainly just being to give better feedback when folks seem to be using Terraform incorrectly. The other bits are therefore mostly cosmetic and okay to be absent briefly as we work towards a better design that is clearer about where that responsibility belongs. --- internal/backend/local/backend_local.go | 5 - internal/command/meta.go | 24 -- internal/command/plan_test.go | 2 +- internal/command/plugins_lock.go | 7 - .../plans/internal/planproto/planfile.pb.go | 369 +++++++----------- .../plans/internal/planproto/planfile.proto | 16 - internal/plans/plan.go | 1 - internal/plans/planfile/planfile_test.go | 1 - internal/plans/planfile/tfplan.go | 17 - internal/plans/planfile/tfplan_test.go | 8 - internal/terraform/context.go | 183 +++++---- internal/terraform/context_plan.go | 7 +- internal/terraform/context_plan_test.go | 7 - internal/terraform/context_plugins.go | 10 + internal/terraform/context_refresh_test.go | 5 +- internal/terraform/context_test.go | 266 +++++-------- internal/terraform/context_validate.go | 6 +- 17 files changed, 364 insertions(+), 570 deletions(-) diff --git a/internal/backend/local/backend_local.go b/internal/backend/local/backend_local.go index 0ba6e66fc..5ce5929ee 100644 --- a/internal/backend/local/backend_local.go +++ b/internal/backend/local/backend_local.go @@ -252,11 +252,6 @@ func (b *Local) localRunForPlanFile(pf *planfile.Reader, run *backend.LocalRun, // we need to apply the plan. run.Plan = plan - // When we're applying a saved plan, our context must verify that all of - // the providers it ends up using are identical to those which created - // the plan. - coreOpts.ProviderSHA256s = plan.ProviderSHA256s - tfCtx, moreDiags := terraform.NewContext(coreOpts) diags = diags.Append(moreDiags) if moreDiags.HasErrors() { diff --git a/internal/command/meta.go b/internal/command/meta.go index 79e1b6f84..0a4029ef0 100644 --- a/internal/command/meta.go +++ b/internal/command/meta.go @@ -478,32 +478,8 @@ func (m *Meta) contextOpts() (*terraform.ContextOpts, error) { } opts.Providers = providerFactories opts.Provisioners = m.provisionerFactories() - - // Read the dependency locks so that they can be verified against the - // provider requirements in the configuration - lockedDependencies, diags := m.lockedDependencies() - - // If the locks file is invalid, we should fail early rather than - // ignore it. A missing locks file will return no error. - if diags.HasErrors() { - return nil, diags.Err() - } - opts.LockedDependencies = lockedDependencies - - // If any unmanaged providers or dev overrides are enabled, they must - // be listed in the context so that they can be ignored when verifying - // the locks against the configuration - opts.ProvidersInDevelopment = make(map[addrs.Provider]struct{}) - for provider := range m.UnmanagedProviders { - opts.ProvidersInDevelopment[provider] = struct{}{} - } - for provider := range m.ProviderDevOverrides { - opts.ProvidersInDevelopment[provider] = struct{}{} - } } - opts.ProviderSHA256s = m.providerPluginsLock().Read() - opts.Meta = &terraform.ContextMeta{ Env: workspace, OriginalWorkingDir: m.WorkingDir.OriginalWorkingDir(), diff --git a/internal/command/plan_test.go b/internal/command/plan_test.go index 880f2e971..68f051f55 100644 --- a/internal/command/plan_test.go +++ b/internal/command/plan_test.go @@ -1051,7 +1051,7 @@ func TestPlan_init_required(t *testing.T) { t.Fatalf("expected error, got success") } got := output.Stderr() - if !strings.Contains(got, `failed to read schema for test_instance.foo in registry.terraform.io/hashicorp/test`) { + if !strings.Contains(got, "Error: Missing required provider") { t.Fatal("wrong error message in output:", got) } } diff --git a/internal/command/plugins_lock.go b/internal/command/plugins_lock.go index b7f3c6b4e..03f11b71d 100644 --- a/internal/command/plugins_lock.go +++ b/internal/command/plugins_lock.go @@ -5,15 +5,8 @@ import ( "fmt" "io/ioutil" "log" - "path/filepath" ) -func (m *Meta) providerPluginsLock() *pluginSHA256LockFile { - return &pluginSHA256LockFile{ - Filename: filepath.Join(m.pluginDir(), "lock.json"), - } -} - type pluginSHA256LockFile struct { Filename string } diff --git a/internal/plans/internal/planproto/planfile.pb.go b/internal/plans/internal/planproto/planfile.pb.go index beb50852a..a8810e0d7 100644 --- a/internal/plans/internal/planproto/planfile.pb.go +++ b/internal/plans/internal/planproto/planfile.pb.go @@ -257,9 +257,6 @@ type Plan struct { ForceReplaceAddrs []string `protobuf:"bytes,16,rep,name=force_replace_addrs,json=forceReplaceAddrs,proto3" json:"force_replace_addrs,omitempty"` // The version string for the Terraform binary that created this plan. TerraformVersion string `protobuf:"bytes,14,opt,name=terraform_version,json=terraformVersion,proto3" json:"terraform_version,omitempty"` - // SHA256 digests of all of the provider plugin binaries that were used - // in the creation of this plan. - ProviderHashes map[string]*Hash `protobuf:"bytes,15,rep,name=provider_hashes,json=providerHashes,proto3" json:"provider_hashes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Backend is a description of the backend configuration and other related // settings at the time the plan was created. Backend *Backend `protobuf:"bytes,13,opt,name=backend,proto3" json:"backend,omitempty"` @@ -360,13 +357,6 @@ func (x *Plan) GetTerraformVersion() string { return "" } -func (x *Plan) GetProviderHashes() map[string]*Hash { - if x != nil { - return x.ProviderHashes - } - return nil -} - func (x *Plan) GetBackend() *Backend { if x != nil { return x.Backend @@ -785,61 +775,6 @@ func (x *DynamicValue) GetMsgpack() []byte { return nil } -// Hash represents a hash value. -// -// At present hashes always use the SHA256 algorithm. In future other hash -// algorithms may be used, possibly with a transitional period of including -// both as separate attributes of this type. Consumers must ignore attributes -// they don't support and fail if no supported attribute is present. The -// top-level format version will not be incremented for changes to the set of -// hash algorithms. -type Hash struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Sha256 []byte `protobuf:"bytes,1,opt,name=sha256,proto3" json:"sha256,omitempty"` -} - -func (x *Hash) Reset() { - *x = Hash{} - if protoimpl.UnsafeEnabled { - mi := &file_planfile_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Hash) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Hash) ProtoMessage() {} - -func (x *Hash) ProtoReflect() protoreflect.Message { - mi := &file_planfile_proto_msgTypes[6] - 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 Hash.ProtoReflect.Descriptor instead. -func (*Hash) Descriptor() ([]byte, []int) { - return file_planfile_proto_rawDescGZIP(), []int{6} -} - -func (x *Hash) GetSha256() []byte { - if x != nil { - return x.Sha256 - } - return nil -} - // Path represents a set of steps to traverse into a data structure. It is // used to refer to a sub-structure within a dynamic data structure presented // separately. @@ -854,7 +789,7 @@ type Path struct { func (x *Path) Reset() { *x = Path{} if protoimpl.UnsafeEnabled { - mi := &file_planfile_proto_msgTypes[7] + mi := &file_planfile_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -867,7 +802,7 @@ func (x *Path) String() string { func (*Path) ProtoMessage() {} func (x *Path) ProtoReflect() protoreflect.Message { - mi := &file_planfile_proto_msgTypes[7] + mi := &file_planfile_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -880,7 +815,7 @@ func (x *Path) ProtoReflect() protoreflect.Message { // Deprecated: Use Path.ProtoReflect.Descriptor instead. func (*Path) Descriptor() ([]byte, []int) { - return file_planfile_proto_rawDescGZIP(), []int{7} + return file_planfile_proto_rawDescGZIP(), []int{6} } func (x *Path) GetSteps() []*Path_Step { @@ -904,7 +839,7 @@ type Path_Step struct { func (x *Path_Step) Reset() { *x = Path_Step{} if protoimpl.UnsafeEnabled { - mi := &file_planfile_proto_msgTypes[10] + mi := &file_planfile_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -917,7 +852,7 @@ func (x *Path_Step) String() string { func (*Path_Step) ProtoMessage() {} func (x *Path_Step) ProtoReflect() protoreflect.Message { - mi := &file_planfile_proto_msgTypes[10] + mi := &file_planfile_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -930,7 +865,7 @@ func (x *Path_Step) ProtoReflect() protoreflect.Message { // Deprecated: Use Path_Step.ProtoReflect.Descriptor instead. func (*Path_Step) Descriptor() ([]byte, []int) { - return file_planfile_proto_rawDescGZIP(), []int{7, 0} + return file_planfile_proto_rawDescGZIP(), []int{6, 0} } func (m *Path_Step) GetSelector() isPath_Step_Selector { @@ -978,7 +913,7 @@ var File_planfile_proto protoreflect.FileDescriptor var file_planfile_proto_rawDesc = []byte{ 0x0a, 0x0e, 0x70, 0x6c, 0x61, 0x6e, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x12, 0x06, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x22, 0xec, 0x05, 0x0a, 0x04, 0x50, 0x6c, 0x61, + 0x12, 0x06, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x22, 0xd0, 0x04, 0x0a, 0x04, 0x50, 0x6c, 0x61, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x07, 0x75, 0x69, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0c, 0x2e, 0x74, @@ -1007,123 +942,111 @@ var file_planfile_proto_rawDesc = []byte{ 0x72, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x41, 0x64, 0x64, 0x72, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x74, 0x65, 0x72, 0x72, - 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x49, 0x0a, 0x0f, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, - 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, - 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x48, 0x61, 0x73, 0x68, - 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, - 0x6e, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, - 0x6e, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, - 0x6e, 0x64, 0x1a, 0x52, 0x0a, 0x0e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x44, - 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4f, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x22, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, - 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x69, 0x0a, 0x07, 0x42, 0x61, 0x63, 0x6b, 0x65, - 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, - 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x22, 0xe4, 0x01, 0x0a, 0x06, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x26, 0x0a, - 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, - 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x44, - 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x16, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x5f, 0x73, 0x65, - 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, - 0x68, 0x52, 0x14, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, - 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x40, 0x0a, 0x15, 0x61, 0x66, 0x74, 0x65, 0x72, - 0x5f, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, - 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, - 0x50, 0x61, 0x74, 0x68, 0x52, 0x13, 0x61, 0x66, 0x74, 0x65, 0x72, 0x53, 0x65, 0x6e, 0x73, 0x69, - 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x73, 0x22, 0xd3, 0x02, 0x0a, 0x16, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0d, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x22, 0x0a, 0x0d, 0x70, 0x72, 0x65, 0x76, - 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x70, 0x72, 0x65, 0x76, 0x52, 0x75, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, - 0x64, 0x65, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, - 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x06, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x66, 0x70, 0x6c, - 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x0a, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x10, 0x72, - 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x18, - 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, - 0x61, 0x74, 0x68, 0x52, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x52, 0x65, 0x70, - 0x6c, 0x61, 0x63, 0x65, 0x12, 0x49, 0x0a, 0x0d, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, - 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x74, 0x66, - 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61, 0x73, 0x6f, - 0x6e, 0x52, 0x0c, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, - 0x68, 0x0a, 0x0c, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, - 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, - 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x22, 0x28, 0x0a, 0x0c, 0x44, 0x79, 0x6e, - 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x73, 0x67, - 0x70, 0x61, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x73, 0x67, 0x70, - 0x61, 0x63, 0x6b, 0x22, 0x1e, 0x0a, 0x04, 0x48, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x73, - 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x68, 0x61, - 0x32, 0x35, 0x36, 0x22, 0xa5, 0x01, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, 0x27, 0x0a, 0x05, - 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, - 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x2e, 0x53, 0x74, 0x65, 0x70, 0x52, 0x05, - 0x73, 0x74, 0x65, 0x70, 0x73, 0x1a, 0x74, 0x0a, 0x04, 0x53, 0x74, 0x65, 0x70, 0x12, 0x27, 0x0a, - 0x0e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, - 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x48, 0x00, 0x52, 0x0a, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x42, - 0x0a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2a, 0x31, 0x0a, 0x04, 0x4d, - 0x6f, 0x64, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x00, 0x12, - 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4f, 0x59, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, - 0x52, 0x45, 0x46, 0x52, 0x45, 0x53, 0x48, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x02, 0x2a, 0x70, - 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4f, 0x50, - 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x01, 0x12, 0x08, - 0x0a, 0x04, 0x52, 0x45, 0x41, 0x44, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x50, 0x44, 0x41, - 0x54, 0x45, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x05, - 0x12, 0x16, 0x0a, 0x12, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4e, 0x5f, - 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x43, 0x52, 0x45, 0x41, - 0x54, 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4e, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x07, - 0x2a, 0xa7, 0x02, 0x0a, 0x1c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61, 0x73, 0x6f, - 0x6e, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x52, - 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x54, - 0x41, 0x49, 0x4e, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x45, 0x50, 0x4c, - 0x41, 0x43, 0x45, 0x5f, 0x42, 0x59, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x10, 0x02, - 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, - 0x55, 0x53, 0x45, 0x5f, 0x43, 0x41, 0x4e, 0x4e, 0x4f, 0x54, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, - 0x45, 0x10, 0x03, 0x12, 0x25, 0x0a, 0x21, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, - 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x4e, 0x4f, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, - 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x04, 0x12, 0x23, 0x0a, 0x1f, 0x44, 0x45, - 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x57, 0x52, 0x4f, - 0x4e, 0x47, 0x5f, 0x52, 0x45, 0x50, 0x45, 0x54, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x05, 0x12, - 0x1e, 0x0a, 0x1a, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, - 0x45, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x49, 0x4e, 0x44, 0x45, 0x58, 0x10, 0x06, 0x12, - 0x1b, 0x0a, 0x17, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, - 0x45, 0x5f, 0x45, 0x41, 0x43, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x07, 0x12, 0x1c, 0x0a, 0x18, - 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x4e, - 0x4f, 0x5f, 0x4d, 0x4f, 0x44, 0x55, 0x4c, 0x45, 0x10, 0x08, 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, - 0x72, 0x70, 0x2f, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x2f, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x61, 0x6e, 0x73, 0x2f, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x61, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x07, + 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, + 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x07, + 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x1a, 0x52, 0x0a, 0x0e, 0x56, 0x61, 0x72, 0x69, 0x61, + 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x61, 0x6e, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x69, 0x0a, 0x07, 0x42, + 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x61, 0x6e, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0xe4, 0x01, 0x0a, 0x06, 0x43, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x12, 0x26, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x0e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x06, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x61, 0x6e, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x16, 0x62, 0x65, 0x66, 0x6f, 0x72, + 0x65, 0x5f, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, + 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x14, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x6e, + 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x40, 0x0a, 0x15, 0x61, + 0x66, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, + 0x61, 0x74, 0x68, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x13, 0x61, 0x66, 0x74, 0x65, 0x72, 0x53, + 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x73, 0x22, 0xd3, 0x02, + 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, + 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x22, 0x0a, 0x0d, + 0x70, 0x72, 0x65, 0x76, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0e, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x65, 0x76, 0x52, 0x75, 0x6e, 0x41, 0x64, 0x64, 0x72, + 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x4b, 0x65, + 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x26, 0x0a, + 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, + 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x06, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, + 0x37, 0x0a, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x70, 0x6c, + 0x61, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x64, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x12, 0x49, 0x0a, 0x0d, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x24, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x0c, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61, + 0x73, 0x6f, 0x6e, 0x22, 0x68, 0x0a, 0x0c, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x43, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, + 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, + 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x22, 0x28, 0x0a, + 0x0c, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x6d, 0x73, 0x67, 0x70, 0x61, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, + 0x6d, 0x73, 0x67, 0x70, 0x61, 0x63, 0x6b, 0x22, 0xa5, 0x01, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, + 0x12, 0x27, 0x0a, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x2e, 0x53, 0x74, + 0x65, 0x70, 0x52, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x1a, 0x74, 0x0a, 0x04, 0x53, 0x74, 0x65, + 0x70, 0x12, 0x27, 0x0a, 0x0e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0d, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x65, 0x6c, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x4b, 0x65, 0x79, 0x42, 0x0a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2a, + 0x31, 0x0a, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x4f, 0x52, 0x4d, 0x41, + 0x4c, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4f, 0x59, 0x10, 0x01, + 0x12, 0x10, 0x0a, 0x0c, 0x52, 0x45, 0x46, 0x52, 0x45, 0x53, 0x48, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, + 0x10, 0x02, 0x2a, 0x70, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x08, 0x0a, 0x04, + 0x4e, 0x4f, 0x4f, 0x50, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, + 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x52, 0x45, 0x41, 0x44, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, + 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x4c, 0x45, + 0x54, 0x45, 0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x54, + 0x48, 0x45, 0x4e, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, + 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4e, 0x5f, 0x44, 0x45, 0x4c, 0x45, + 0x54, 0x45, 0x10, 0x07, 0x2a, 0xa7, 0x02, 0x0a, 0x1c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, + 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, + 0x53, 0x45, 0x5f, 0x54, 0x41, 0x49, 0x4e, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, + 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x5f, 0x42, 0x59, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, + 0x53, 0x54, 0x10, 0x02, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x5f, + 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x43, 0x41, 0x4e, 0x4e, 0x4f, 0x54, 0x5f, 0x55, + 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x03, 0x12, 0x25, 0x0a, 0x21, 0x44, 0x45, 0x4c, 0x45, 0x54, + 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x4e, 0x4f, 0x5f, 0x52, 0x45, 0x53, + 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x04, 0x12, 0x23, + 0x0a, 0x1f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, + 0x5f, 0x57, 0x52, 0x4f, 0x4e, 0x47, 0x5f, 0x52, 0x45, 0x50, 0x45, 0x54, 0x49, 0x54, 0x49, 0x4f, + 0x4e, 0x10, 0x05, 0x12, 0x1e, 0x0a, 0x1a, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, + 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x49, 0x4e, 0x44, 0x45, + 0x58, 0x10, 0x06, 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, + 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x45, 0x41, 0x43, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x07, + 0x12, 0x1c, 0x0a, 0x18, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, + 0x53, 0x45, 0x5f, 0x4e, 0x4f, 0x5f, 0x4d, 0x4f, 0x44, 0x55, 0x4c, 0x45, 0x10, 0x08, 0x42, 0x42, + 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, + 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x61, 0x6e, 0x73, 0x2f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x61, 0x6e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1139,7 +1062,7 @@ func file_planfile_proto_rawDescGZIP() []byte { } var file_planfile_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_planfile_proto_msgTypes = make([]protoimpl.MessageInfo, 11) +var file_planfile_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_planfile_proto_goTypes = []interface{}{ (Mode)(0), // 0: tfplan.Mode (Action)(0), // 1: tfplan.Action @@ -1150,38 +1073,34 @@ var file_planfile_proto_goTypes = []interface{}{ (*ResourceInstanceChange)(nil), // 6: tfplan.ResourceInstanceChange (*OutputChange)(nil), // 7: tfplan.OutputChange (*DynamicValue)(nil), // 8: tfplan.DynamicValue - (*Hash)(nil), // 9: tfplan.Hash - (*Path)(nil), // 10: tfplan.Path - nil, // 11: tfplan.Plan.VariablesEntry - nil, // 12: tfplan.Plan.ProviderHashesEntry - (*Path_Step)(nil), // 13: tfplan.Path.Step + (*Path)(nil), // 9: tfplan.Path + nil, // 10: tfplan.Plan.VariablesEntry + (*Path_Step)(nil), // 11: tfplan.Path.Step } var file_planfile_proto_depIdxs = []int32{ 0, // 0: tfplan.Plan.ui_mode:type_name -> tfplan.Mode - 11, // 1: tfplan.Plan.variables:type_name -> tfplan.Plan.VariablesEntry + 10, // 1: tfplan.Plan.variables:type_name -> tfplan.Plan.VariablesEntry 6, // 2: tfplan.Plan.resource_changes:type_name -> tfplan.ResourceInstanceChange 6, // 3: tfplan.Plan.resource_drift:type_name -> tfplan.ResourceInstanceChange 7, // 4: tfplan.Plan.output_changes:type_name -> tfplan.OutputChange - 12, // 5: tfplan.Plan.provider_hashes:type_name -> tfplan.Plan.ProviderHashesEntry - 4, // 6: tfplan.Plan.backend:type_name -> tfplan.Backend - 8, // 7: tfplan.Backend.config:type_name -> tfplan.DynamicValue - 1, // 8: tfplan.Change.action:type_name -> tfplan.Action - 8, // 9: tfplan.Change.values:type_name -> tfplan.DynamicValue - 10, // 10: tfplan.Change.before_sensitive_paths:type_name -> tfplan.Path - 10, // 11: tfplan.Change.after_sensitive_paths:type_name -> tfplan.Path - 5, // 12: tfplan.ResourceInstanceChange.change:type_name -> tfplan.Change - 10, // 13: tfplan.ResourceInstanceChange.required_replace:type_name -> tfplan.Path - 2, // 14: tfplan.ResourceInstanceChange.action_reason:type_name -> tfplan.ResourceInstanceActionReason - 5, // 15: tfplan.OutputChange.change:type_name -> tfplan.Change - 13, // 16: tfplan.Path.steps:type_name -> tfplan.Path.Step - 8, // 17: tfplan.Plan.VariablesEntry.value:type_name -> tfplan.DynamicValue - 9, // 18: tfplan.Plan.ProviderHashesEntry.value:type_name -> tfplan.Hash - 8, // 19: tfplan.Path.Step.element_key:type_name -> tfplan.DynamicValue - 20, // [20:20] is the sub-list for method output_type - 20, // [20:20] is the sub-list for method input_type - 20, // [20:20] is the sub-list for extension type_name - 20, // [20:20] is the sub-list for extension extendee - 0, // [0:20] is the sub-list for field type_name + 4, // 5: tfplan.Plan.backend:type_name -> tfplan.Backend + 8, // 6: tfplan.Backend.config:type_name -> tfplan.DynamicValue + 1, // 7: tfplan.Change.action:type_name -> tfplan.Action + 8, // 8: tfplan.Change.values:type_name -> tfplan.DynamicValue + 9, // 9: tfplan.Change.before_sensitive_paths:type_name -> tfplan.Path + 9, // 10: tfplan.Change.after_sensitive_paths:type_name -> tfplan.Path + 5, // 11: tfplan.ResourceInstanceChange.change:type_name -> tfplan.Change + 9, // 12: tfplan.ResourceInstanceChange.required_replace:type_name -> tfplan.Path + 2, // 13: tfplan.ResourceInstanceChange.action_reason:type_name -> tfplan.ResourceInstanceActionReason + 5, // 14: tfplan.OutputChange.change:type_name -> tfplan.Change + 11, // 15: tfplan.Path.steps:type_name -> tfplan.Path.Step + 8, // 16: tfplan.Plan.VariablesEntry.value:type_name -> tfplan.DynamicValue + 8, // 17: tfplan.Path.Step.element_key:type_name -> tfplan.DynamicValue + 18, // [18:18] is the sub-list for method output_type + 18, // [18:18] is the sub-list for method input_type + 18, // [18:18] is the sub-list for extension type_name + 18, // [18:18] is the sub-list for extension extendee + 0, // [0:18] is the sub-list for field type_name } func init() { file_planfile_proto_init() } @@ -1263,18 +1182,6 @@ func file_planfile_proto_init() { } } file_planfile_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Hash); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_planfile_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Path); i { case 0: return &v.state @@ -1286,7 +1193,7 @@ func file_planfile_proto_init() { return nil } } - file_planfile_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + file_planfile_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Path_Step); i { case 0: return &v.state @@ -1299,7 +1206,7 @@ func file_planfile_proto_init() { } } } - file_planfile_proto_msgTypes[10].OneofWrappers = []interface{}{ + file_planfile_proto_msgTypes[8].OneofWrappers = []interface{}{ (*Path_Step_AttributeName)(nil), (*Path_Step_ElementKey)(nil), } @@ -1309,7 +1216,7 @@ func file_planfile_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_planfile_proto_rawDesc, NumEnums: 3, - NumMessages: 11, + NumMessages: 9, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/plans/internal/planproto/planfile.proto b/internal/plans/internal/planproto/planfile.proto index 1bbe7425e..752abd77a 100644 --- a/internal/plans/internal/planproto/planfile.proto +++ b/internal/plans/internal/planproto/planfile.proto @@ -58,10 +58,6 @@ message Plan { // The version string for the Terraform binary that created this plan. string terraform_version = 14; - // SHA256 digests of all of the provider plugin binaries that were used - // in the creation of this plan. - map provider_hashes = 15; - // Backend is a description of the backend configuration and other related // settings at the time the plan was created. Backend backend = 13; @@ -218,18 +214,6 @@ message DynamicValue { bytes msgpack = 1; } -// Hash represents a hash value. -// -// At present hashes always use the SHA256 algorithm. In future other hash -// algorithms may be used, possibly with a transitional period of including -// both as separate attributes of this type. Consumers must ignore attributes -// they don't support and fail if no supported attribute is present. The -// top-level format version will not be incremented for changes to the set of -// hash algorithms. -message Hash { - bytes sha256 = 1; -} - // Path represents a set of steps to traverse into a data structure. It is // used to refer to a sub-structure within a dynamic data structure presented // separately. diff --git a/internal/plans/plan.go b/internal/plans/plan.go index a96a05648..da824f24a 100644 --- a/internal/plans/plan.go +++ b/internal/plans/plan.go @@ -34,7 +34,6 @@ type Plan struct { DriftedResources []*ResourceInstanceChangeSrc TargetAddrs []addrs.Targetable ForceReplaceAddrs []addrs.AbsResourceInstance - ProviderSHA256s map[string][]byte Backend Backend // PrevRunState and PriorState both describe the situation that the plan diff --git a/internal/plans/planfile/planfile_test.go b/internal/plans/planfile/planfile_test.go index 14d23c87a..13e456666 100644 --- a/internal/plans/planfile/planfile_test.go +++ b/internal/plans/planfile/planfile_test.go @@ -52,7 +52,6 @@ func TestRoundtrip(t *testing.T) { Outputs: []*plans.OutputChangeSrc{}, }, DriftedResources: []*plans.ResourceInstanceChangeSrc{}, - ProviderSHA256s: map[string][]byte{}, VariableValues: map[string]plans.DynamicValue{ "foo": plans.DynamicValue([]byte("foo placeholder")), }, diff --git a/internal/plans/planfile/tfplan.go b/internal/plans/planfile/tfplan.go index 87d21822e..47f95a2a3 100644 --- a/internal/plans/planfile/tfplan.go +++ b/internal/plans/planfile/tfplan.go @@ -57,8 +57,6 @@ func readTfplan(r io.Reader) (*plans.Plan, error) { Resources: []*plans.ResourceInstanceChangeSrc{}, }, DriftedResources: []*plans.ResourceInstanceChangeSrc{}, - - ProviderSHA256s: map[string][]byte{}, } switch rawPlan.UiMode { @@ -125,14 +123,6 @@ func readTfplan(r io.Reader) (*plans.Plan, error) { plan.ForceReplaceAddrs = append(plan.ForceReplaceAddrs, addr) } - for name, rawHashObj := range rawPlan.ProviderHashes { - if len(rawHashObj.Sha256) == 0 { - return nil, fmt.Errorf("no SHA256 hash for provider %q plugin", name) - } - - plan.ProviderSHA256s[name] = rawHashObj.Sha256 - } - for name, rawVal := range rawPlan.Variables { val, err := valueFromTfplan(rawVal) if err != nil { @@ -358,7 +348,6 @@ func writeTfplan(plan *plans.Plan, w io.Writer) error { rawPlan := &planproto.Plan{ Version: tfplanFormatVersion, TerraformVersion: version.String(), - ProviderHashes: map[string]*planproto.Hash{}, Variables: map[string]*planproto.DynamicValue{}, OutputChanges: []*planproto.OutputChange{}, @@ -426,12 +415,6 @@ func writeTfplan(plan *plans.Plan, w io.Writer) error { rawPlan.ForceReplaceAddrs = append(rawPlan.ForceReplaceAddrs, replaceAddr.String()) } - for name, hash := range plan.ProviderSHA256s { - rawPlan.ProviderHashes[name] = &planproto.Hash{ - Sha256: hash, - } - } - for name, val := range plan.VariableValues { rawPlan.Variables[name] = valueToTfplan(val) } diff --git a/internal/plans/planfile/tfplan_test.go b/internal/plans/planfile/tfplan_test.go index 7ab62de53..7d5be4dc5 100644 --- a/internal/plans/planfile/tfplan_test.go +++ b/internal/plans/planfile/tfplan_test.go @@ -165,14 +165,6 @@ func TestTFPlanRoundTrip(t *testing.T) { Name: "woot", }.Absolute(addrs.RootModuleInstance), }, - ProviderSHA256s: map[string][]byte{ - "test": []byte{ - 0xba, 0x5e, 0x1e, 0x55, 0xb0, 0x1d, 0xfa, 0xce, - 0xef, 0xfe, 0xc7, 0xed, 0x1a, 0xbe, 0x11, 0xed, - 0x5c, 0xa1, 0xab, 0x1e, 0xda, 0x7a, 0xba, 0x5e, - 0x70, 0x7a, 0x11, 0xed, 0xb0, 0x07, 0xab, 0x1e, - }, - }, Backend: plans.Backend{ Type: "local", Config: mustNewDynamicValue( diff --git a/internal/terraform/context.go b/internal/terraform/context.go index e05200e1e..2174b0fe0 100644 --- a/internal/terraform/context.go +++ b/internal/terraform/context.go @@ -4,10 +4,9 @@ import ( "context" "fmt" "log" - "strings" + "sort" "sync" - "github.com/apparentlymart/go-versions/versions" "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/providers" @@ -16,8 +15,6 @@ import ( "github.com/hashicorp/terraform/internal/tfdiags" "github.com/zclconf/go-cty/cty" - "github.com/hashicorp/terraform/internal/depsfile" - "github.com/hashicorp/terraform/internal/getproviders" _ "github.com/hashicorp/terraform/internal/logging" ) @@ -43,18 +40,6 @@ type ContextOpts struct { Providers map[addrs.Provider]providers.Factory Provisioners map[string]provisioners.Factory - // If non-nil, will apply as additional constraints on the provider - // plugins that will be requested from the provider resolver. - ProviderSHA256s map[string][]byte - - // If non-nil, will be verified to ensure that provider requirements from - // configuration can be satisfied by the set of locked dependencies. - LockedDependencies *depsfile.Locks - - // Set of providers to exclude from the requirements check process, as they - // are marked as in local development. - ProvidersInDevelopment map[addrs.Provider]struct{} - UIInput UIInput } @@ -88,9 +73,7 @@ type Context struct { // operations. meta *ContextMeta - plugins *contextPlugins - dependencyLocks *depsfile.Locks - providersInDevelopment map[addrs.Provider]struct{} + plugins *contextPlugins hooks []Hook sh *stopHook @@ -99,7 +82,6 @@ type Context struct { l sync.Mutex // Lock acquired during any task parallelSem Semaphore providerInputConfig map[string]map[string]cty.Value - providerSHA256s map[string][]byte runCond *sync.Cond runContext context.Context runContextCancel context.CancelFunc @@ -153,13 +135,10 @@ func NewContext(opts *ContextOpts) (*Context, tfdiags.Diagnostics) { meta: opts.Meta, uiInput: opts.UIInput, - plugins: plugins, - dependencyLocks: opts.LockedDependencies, - providersInDevelopment: opts.ProvidersInDevelopment, + plugins: plugins, parallelSem: NewSemaphore(par), providerInputConfig: make(map[string]map[string]cty.Value), - providerSHA256s: opts.ProviderSHA256s, sh: sh, }, diags } @@ -174,50 +153,6 @@ func (c *Context) Schemas(config *configs.Config, state *states.State) (*Schemas var diags tfdiags.Diagnostics - // If we have a configuration and a set of locked dependencies, verify that - // the provider requirements from the configuration can be satisfied by the - // locked dependencies. - if c.dependencyLocks != nil && config != nil { - reqs, providerDiags := config.ProviderRequirements() - diags = diags.Append(providerDiags) - - locked := c.dependencyLocks.AllProviders() - unmetReqs := make(getproviders.Requirements) - for provider, versionConstraints := range reqs { - // Builtin providers are not listed in the locks file - if provider.IsBuiltIn() { - continue - } - // Development providers must be excluded from this check - if _, ok := c.providersInDevelopment[provider]; ok { - continue - } - // If the required provider doesn't exist in the lock, or the - // locked version doesn't meet the constraints, mark the - // requirement unmet - acceptable := versions.MeetingConstraints(versionConstraints) - if lock, ok := locked[provider]; !ok || !acceptable.Has(lock.Version()) { - unmetReqs[provider] = versionConstraints - } - } - - if len(unmetReqs) > 0 { - var buf strings.Builder - for provider, versionConstraints := range unmetReqs { - fmt.Fprintf(&buf, "\n- %s", provider) - if len(versionConstraints) > 0 { - fmt.Fprintf(&buf, " (%s)", getproviders.VersionConstraintsString(versionConstraints)) - } - } - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider requirements cannot be satisfied by locked dependencies", - fmt.Sprintf("The following required providers are not installed:\n%s\n\nPlease run \"terraform init\".", buf.String()), - )) - return nil, diags - } - } - ret, err := loadSchemas(config, state, c.plugins) if err != nil { diags = diags.Append(tfdiags.Sourceless( @@ -381,3 +316,115 @@ func (c *Context) watchStop(walker *ContextGraphWalker) (chan struct{}, <-chan s return stop, wait } + +// checkConfigDependencies checks whether the recieving context is able to +// support the given configuration, returning error diagnostics if not. +// +// Currently this function checks whether the current Terraform CLI version +// matches the version requirements of all of the modules, and whether our +// plugin library contains all of the plugin names/addresses needed. +// +// This function does *not* check that external modules are installed (that's +// the responsibility of the configuration loader) and doesn't check that the +// plugins are of suitable versions to match any version constraints (which is +// the responsibility of the code which installed the plugins and then +// constructed the Providers/Provisioners maps passed in to NewContext). +// +// In most cases we should typically catch the problems this function detects +// before we reach this point, but this function can come into play in some +// unusual cases outside of the main workflow, and can avoid some +// potentially-more-confusing errors from later operations. +func (c *Context) checkConfigDependencies(config *configs.Config) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + // This checks the Terraform CLI version constraints specified in all of + // the modules. + diags = diags.Append(CheckCoreVersionRequirements(config)) + + // We only check that we have a factory for each required provider, and + // assume the caller already assured that any separately-installed + // plugins are of a suitable version, match expected checksums, etc. + providerReqs, hclDiags := config.ProviderRequirements() + diags = diags.Append(hclDiags) + if hclDiags.HasErrors() { + return diags + } + for providerAddr := range providerReqs { + if !c.plugins.HasProvider(providerAddr) { + if !providerAddr.IsBuiltIn() { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Missing required provider", + fmt.Sprintf( + "This configuration requires provider %s, but that provider isn't available. You may be able to install it automatically by running:\n terraform init", + providerAddr, + ), + )) + } else { + // Built-in providers can never be installed by "terraform init", + // so no point in confusing the user by suggesting that. + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Missing required provider", + fmt.Sprintf( + "This configuration requires built-in provider %s, but that provider isn't available in this Terraform version.", + providerAddr, + ), + )) + } + } + } + + // Our handling of provisioners is much less sophisticated than providers + // because they are in many ways a legacy system. We need to go hunting + // for them more directly in the configuration. + config.DeepEach(func(modCfg *configs.Config) { + if modCfg == nil || modCfg.Module == nil { + return // should not happen, but we'll be robust + } + for _, rc := range modCfg.Module.ManagedResources { + if rc.Managed == nil { + continue // should not happen, but we'll be robust + } + for _, pc := range rc.Managed.Provisioners { + if !c.plugins.HasProvisioner(pc.Type) { + // This is not a very high-quality error, because really + // the caller of terraform.NewContext should've already + // done equivalent checks when doing plugin discovery. + // This is just to make sure we return a predictable + // error in a central place, rather than failing somewhere + // later in the non-deterministically-ordered graph walk. + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Missing required provisioner plugin", + fmt.Sprintf( + "This configuration requires provisioner plugin %q, which isn't available. If you're intending to use an external provisioner plugin, you must install it manually into one of the plugin search directories before running Terraform.", + pc.Type, + ), + )) + } + } + } + }) + + // Because we were doing a lot of map iteration above, and we're only + // generating sourceless diagnostics anyway, our diagnostics will not be + // in a deterministic order. To ensure stable output when there are + // multiple errors to report, we'll sort these particular diagnostics + // so they are at least always consistent alone. This ordering is + // arbitrary and not a compatibility constraint. + sort.Slice(diags, func(i, j int) bool { + // Because these are sourcelss diagnostics and we know they are all + // errors, we know they'll only differ in their description fields. + descI := diags[i].Description() + descJ := diags[j].Description() + switch { + case descI.Summary != descJ.Summary: + return descI.Summary < descJ.Summary + default: + return descI.Detail < descJ.Detail + } + }) + + return diags +} diff --git a/internal/terraform/context_plan.go b/internal/terraform/context_plan.go index 5610a445b..3f860ef1b 100644 --- a/internal/terraform/context_plan.go +++ b/internal/terraform/context_plan.go @@ -55,10 +55,10 @@ func (c *Context) Plan(config *configs.Config, prevRunState *states.State, opts } } - moreDiags := CheckCoreVersionRequirements(config) + moreDiags := c.checkConfigDependencies(config) diags = diags.Append(moreDiags) - // If version constraints are not met then we'll bail early since otherwise - // we're likely to just see a bunch of other errors related to + // If required dependencies are not available then we'll bail early since + // otherwise we're likely to just see a bunch of other errors related to // incompatibilities, which could be overwhelming for the user. if diags.HasErrors() { return nil, diags @@ -161,7 +161,6 @@ The -target option is not for routine use, and is provided only for exceptional if plan != nil { plan.VariableValues = varVals plan.TargetAddrs = opts.Targets - plan.ProviderSHA256s = c.providerSHA256s } else if !diags.HasErrors() { panic("nil plan but no errors") } diff --git a/internal/terraform/context_plan_test.go b/internal/terraform/context_plan_test.go index 9cb4c8925..cfd51da8c 100644 --- a/internal/terraform/context_plan_test.go +++ b/internal/terraform/context_plan_test.go @@ -33,9 +33,6 @@ func TestContext2Plan_basic(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - ProviderSHA256s: map[string][]byte{ - "aws": []byte("placeholder"), - }, }) plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) @@ -47,10 +44,6 @@ func TestContext2Plan_basic(t *testing.T) { t.Fatalf("wrong number of resources %d; want fewer than two\n%s", l, spew.Sdump(plan.Changes.Resources)) } - if !reflect.DeepEqual(plan.ProviderSHA256s, ctx.providerSHA256s) { - t.Errorf("wrong ProviderSHA256s %#v; want %#v", plan.ProviderSHA256s, ctx.providerSHA256s) - } - schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() for _, r := range plan.Changes.Resources { diff --git a/internal/terraform/context_plugins.go b/internal/terraform/context_plugins.go index 4fbdf84d0..4b3071cf6 100644 --- a/internal/terraform/context_plugins.go +++ b/internal/terraform/context_plugins.go @@ -43,6 +43,11 @@ func (cp *contextPlugins) init() { cp.provisionerSchemas = make(map[string]*configschema.Block, len(cp.provisionerFactories)) } +func (cp *contextPlugins) HasProvider(addr addrs.Provider) bool { + _, ok := cp.providerFactories[addr] + return ok +} + func (cp *contextPlugins) NewProviderInstance(addr addrs.Provider) (providers.Interface, error) { f, ok := cp.providerFactories[addr] if !ok { @@ -53,6 +58,11 @@ func (cp *contextPlugins) NewProviderInstance(addr addrs.Provider) (providers.In } +func (cp *contextPlugins) HasProvisioner(typ string) bool { + _, ok := cp.provisionerFactories[typ] + return ok +} + func (cp *contextPlugins) NewProvisionerInstance(typ string) (provisioners.Interface, error) { f, ok := cp.provisionerFactories[typ] if !ok { diff --git a/internal/terraform/context_refresh_test.go b/internal/terraform/context_refresh_test.go index 49cd02e0e..aa2239dbe 100644 --- a/internal/terraform/context_refresh_test.go +++ b/internal/terraform/context_refresh_test.go @@ -2,7 +2,6 @@ package terraform import ( "reflect" - "regexp" "sort" "strings" "sync" @@ -1051,8 +1050,8 @@ func TestContext2Refresh_unknownProvider(t *testing.T) { t.Fatal("successfully refreshed; want error") } - if !regexp.MustCompile(`failed to instantiate provider ".+"`).MatchString(diags.Err().Error()) { - t.Fatalf("wrong error: %s", diags.Err()) + if got, want := diags.Err().Error(), "Missing required provider"; !strings.Contains(got, want) { + t.Errorf("missing expected error\nwant substring: %s\ngot:\n%s", want, got) } } diff --git a/internal/terraform/context_test.go b/internal/terraform/context_test.go index 02addb4db..9ef2603db 100644 --- a/internal/terraform/context_test.go +++ b/internal/terraform/context_test.go @@ -15,12 +15,10 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/hashicorp/go-version" - "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/configs/configload" "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/configs/hcl2shim" - "github.com/hashicorp/terraform/internal/depsfile" "github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/plans/planfile" "github.com/hashicorp/terraform/internal/providers" @@ -122,184 +120,82 @@ func TestNewContextRequiredVersion(t *testing.T) { } } -func TestNewContext_lockedDependencies(t *testing.T) { - // TODO: Remove this test altogether once we've factored out the version - // and checksum verification to be exclusively the caller's responsibility. - t.Skip("only one step away from locked dependencies being the caller's responsibility") +func TestContext_missingPlugins(t *testing.T) { + ctx, diags := NewContext(&ContextOpts{}) + assertNoDiagnostics(t, diags) - configBeepGreaterThanOne := ` + configSrc := ` terraform { - required_providers { - beep = { - source = "example.com/foo/beep" - version = ">= 1.0.0" - } - } -} -` - configBeepLessThanOne := ` -terraform { - required_providers { - beep = { - source = "example.com/foo/beep" - version = "< 1.0.0" - } - } -} -` - configBuiltin := ` -terraform { - required_providers { - terraform = { - source = "terraform.io/builtin/terraform" + required_providers { + explicit = { + source = "example.com/foo/beep" + } + builtin = { + source = "terraform.io/builtin/nonexist" + } } - } } -` - locksBeepGreaterThanOne := ` -provider "example.com/foo/beep" { - version = "1.0.0" - constraints = ">= 1.0.0" - hashes = [ - "h1:does-not-match", - ] -} -` - configBeepBoop := ` -terraform { - required_providers { - beep = { - source = "example.com/foo/beep" - version = "< 1.0.0" # different from locks - } - boop = { - source = "example.com/foo/boop" - version = ">= 2.0.0" - } - } -} -` - locksBeepBoop := ` -provider "example.com/foo/beep" { - version = "1.0.0" - constraints = ">= 1.0.0" - hashes = [ - "h1:does-not-match", - ] -} -provider "example.com/foo/boop" { - version = "2.3.4" - constraints = ">= 2.0.0" - hashes = [ - "h1:does-not-match", - ] -} -` - beepAddr := addrs.MustParseProviderSourceString("example.com/foo/beep") - boopAddr := addrs.MustParseProviderSourceString("example.com/foo/boop") - testCases := map[string]struct { - Config string - LockFile string - DevProviders []addrs.Provider - WantErr string - }{ - "dependencies met": { - Config: configBeepGreaterThanOne, - LockFile: locksBeepGreaterThanOne, - }, - "no locks given": { - Config: configBeepGreaterThanOne, - }, - "builtin provider with empty locks": { - Config: configBuiltin, - LockFile: `# This file is maintained automatically by "terraform init".`, - }, - "multiple providers, one in development": { - Config: configBeepBoop, - LockFile: locksBeepBoop, - DevProviders: []addrs.Provider{beepAddr}, - }, - "development provider with empty locks": { - Config: configBeepGreaterThanOne, - LockFile: `# This file is maintained automatically by "terraform init".`, - DevProviders: []addrs.Provider{beepAddr}, - }, - "multiple providers, one in development, one missing": { - Config: configBeepBoop, - LockFile: locksBeepGreaterThanOne, - DevProviders: []addrs.Provider{beepAddr}, - WantErr: `Provider requirements cannot be satisfied by locked dependencies: The following required providers are not installed: - -- example.com/foo/boop (>= 2.0.0) - -Please run "terraform init".`, - }, - "wrong provider version": { - Config: configBeepLessThanOne, - LockFile: locksBeepGreaterThanOne, - WantErr: `Provider requirements cannot be satisfied by locked dependencies: The following required providers are not installed: - -- example.com/foo/beep (< 1.0.0) - -Please run "terraform init".`, - }, - "empty locks": { - Config: configBeepGreaterThanOne, - LockFile: `# This file is maintained automatically by "terraform init".`, - WantErr: `Provider requirements cannot be satisfied by locked dependencies: The following required providers are not installed: - -- example.com/foo/beep (>= 1.0.0) - -Please run "terraform init".`, - }, +resource "implicit_thing" "a" { + provisioner "nonexist" { } - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - var locks *depsfile.Locks - if tc.LockFile != "" { - var diags tfdiags.Diagnostics - locks, diags = depsfile.LoadLocksFromBytes([]byte(tc.LockFile), "test.lock.hcl") - if len(diags) > 0 { - t.Fatalf("unexpected error loading locks file: %s", diags.Err()) - } - } - devProviders := make(map[addrs.Provider]struct{}) - for _, provider := range tc.DevProviders { - devProviders[provider] = struct{}{} - } - opts := &ContextOpts{ - LockedDependencies: locks, - ProvidersInDevelopment: devProviders, - Providers: map[addrs.Provider]providers.Factory{ - beepAddr: testProviderFuncFixed(testProvider("beep")), - boopAddr: testProviderFuncFixed(testProvider("boop")), - addrs.NewBuiltInProvider("terraform"): testProviderFuncFixed(testProvider("terraform")), - }, - } +} - m := testModuleInline(t, map[string]string{ - "main.tf": tc.Config, - }) +resource "implicit_thing" "b" { + provider = implicit2 +} +` - c, diags := NewContext(opts) - if diags.HasErrors() { - t.Fatalf("unexpected NewContext error: %s", diags.Err()) - } + cfg := testModuleInline(t, map[string]string{ + "main.tf": configSrc, + }) - diags = c.Validate(m) - if tc.WantErr != "" { - if len(diags) == 0 { - t.Fatal("expected diags but none returned") - } - if got, want := diags.Err().Error(), tc.WantErr; got != want { - t.Errorf("wrong diags\n got: %s\nwant: %s", got, want) - } - } else { - if len(diags) > 0 { - t.Errorf("unexpected diags: %s", diags.Err()) - } - } + // Validate and Plan are the two entry points where we explicitly verify + // the available plugins match what the configuration needs. For other + // operations we typically fail more deeply in Terraform Core, with + // potentially-less-helpful error messages, because getting there would + // require doing some pretty weird things that aren't common enough to + // be worth the complexity to check for them. + + validateDiags := ctx.Validate(cfg) + _, planDiags := ctx.Plan(cfg, nil, DefaultPlanOpts) + + tests := map[string]tfdiags.Diagnostics{ + "validate": validateDiags, + "plan": planDiags, + } + + for testName, gotDiags := range tests { + t.Run(testName, func(t *testing.T) { + var wantDiags tfdiags.Diagnostics + wantDiags = wantDiags.Append( + tfdiags.Sourceless( + tfdiags.Error, + "Missing required provider", + "This configuration requires built-in provider terraform.io/builtin/nonexist, but that provider isn't available in this Terraform version.", + ), + tfdiags.Sourceless( + tfdiags.Error, + "Missing required provider", + "This configuration requires provider example.com/foo/beep, but that provider isn't available. You may be able to install it automatically by running:\n terraform init", + ), + tfdiags.Sourceless( + tfdiags.Error, + "Missing required provider", + "This configuration requires provider registry.terraform.io/hashicorp/implicit, but that provider isn't available. You may be able to install it automatically by running:\n terraform init", + ), + tfdiags.Sourceless( + tfdiags.Error, + "Missing required provider", + "This configuration requires provider registry.terraform.io/hashicorp/implicit2, but that provider isn't available. You may be able to install it automatically by running:\n terraform init", + ), + tfdiags.Sourceless( + tfdiags.Error, + "Missing required provisioner plugin", + `This configuration requires provisioner plugin "nonexist", which isn't available. If you're intending to use an external provisioner plugin, you must install it manually into one of the plugin search directories before running Terraform.`, + ), + ) + assertDiagnosticsMatch(t, gotDiags, wantDiags) }) } } @@ -779,9 +675,13 @@ func contextOptsForPlanViaFile(configSnap *configload.Snapshot, plan *plans.Plan return nil, nil, nil, err } - return &ContextOpts{ - ProviderSHA256s: plan.ProviderSHA256s, - }, config, plan, nil + // Note: This has grown rather silly over the course of ongoing refactoring, + // because ContextOpts is no longer actually responsible for carrying + // any information from a plan file and instead all of the information + // lives inside the config and plan objects. We continue to return a + // silly empty ContextOpts here just to keep all of the calling tests + // working. + return &ContextOpts{}, config, plan, nil } // legacyPlanComparisonString produces a string representation of the changes @@ -994,6 +894,24 @@ func assertNoErrors(t *testing.T, diags tfdiags.Diagnostics) { t.FailNow() } +// assertDiagnosticsMatch fails the test in progress (using t.Fatal) if the +// two sets of diagnostics don't match after being normalized using the +// "ForRPC" processing step, which eliminates the specific type information +// and HCL expression information of each diagnostic. +// +// assertDiagnosticsMatch sorts the two sets of diagnostics in the usual way +// before comparing them, though diagnostics only have a partial order so that +// will not totally normalize the ordering of all diagnostics sets. +func assertDiagnosticsMatch(t *testing.T, got, want tfdiags.Diagnostics) { + got = got.ForRPC() + want = want.ForRPC() + got.Sort() + want.Sort() + if diff := cmp.Diff(want, got); diff != "" { + t.Fatalf("wrong diagnostics\n%s", diff) + } +} + // logDiagnostics is a test helper that logs the given diagnostics to to the // given testing.T using t.Log, in a way that is hopefully useful in debugging // a test. It does not generate any errors or fail the test. See diff --git a/internal/terraform/context_validate.go b/internal/terraform/context_validate.go index b079477ba..fb54be420 100644 --- a/internal/terraform/context_validate.go +++ b/internal/terraform/context_validate.go @@ -26,10 +26,10 @@ func (c *Context) Validate(config *configs.Config) tfdiags.Diagnostics { var diags tfdiags.Diagnostics - moreDiags := CheckCoreVersionRequirements(config) + moreDiags := c.checkConfigDependencies(config) diags = diags.Append(moreDiags) - // If version constraints are not met then we'll bail early since otherwise - // we're likely to just see a bunch of other errors related to + // If required dependencies are not available then we'll bail early since + // otherwise we're likely to just see a bunch of other errors related to // incompatibilities, which could be overwhelming for the user. if diags.HasErrors() { return diags