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.
This commit is contained in:
Martin Atkins 2021-09-29 15:51:09 -07:00
parent 0062e7112a
commit 8d193ad268
17 changed files with 364 additions and 570 deletions

View File

@ -252,11 +252,6 @@ func (b *Local) localRunForPlanFile(pf *planfile.Reader, run *backend.LocalRun,
// we need to apply the plan. // we need to apply the plan.
run.Plan = 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) tfCtx, moreDiags := terraform.NewContext(coreOpts)
diags = diags.Append(moreDiags) diags = diags.Append(moreDiags)
if moreDiags.HasErrors() { if moreDiags.HasErrors() {

View File

@ -478,31 +478,7 @@ func (m *Meta) contextOpts() (*terraform.ContextOpts, error) {
} }
opts.Providers = providerFactories opts.Providers = providerFactories
opts.Provisioners = m.provisionerFactories() 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{ opts.Meta = &terraform.ContextMeta{
Env: workspace, Env: workspace,

View File

@ -1051,7 +1051,7 @@ func TestPlan_init_required(t *testing.T) {
t.Fatalf("expected error, got success") t.Fatalf("expected error, got success")
} }
got := output.Stderr() 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) t.Fatal("wrong error message in output:", got)
} }
} }

View File

@ -5,15 +5,8 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"path/filepath"
) )
func (m *Meta) providerPluginsLock() *pluginSHA256LockFile {
return &pluginSHA256LockFile{
Filename: filepath.Join(m.pluginDir(), "lock.json"),
}
}
type pluginSHA256LockFile struct { type pluginSHA256LockFile struct {
Filename string Filename string
} }

View File

@ -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"` 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. // 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"` 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 // Backend is a description of the backend configuration and other related
// settings at the time the plan was created. // settings at the time the plan was created.
Backend *Backend `protobuf:"bytes,13,opt,name=backend,proto3" json:"backend,omitempty"` Backend *Backend `protobuf:"bytes,13,opt,name=backend,proto3" json:"backend,omitempty"`
@ -360,13 +357,6 @@ func (x *Plan) GetTerraformVersion() string {
return "" return ""
} }
func (x *Plan) GetProviderHashes() map[string]*Hash {
if x != nil {
return x.ProviderHashes
}
return nil
}
func (x *Plan) GetBackend() *Backend { func (x *Plan) GetBackend() *Backend {
if x != nil { if x != nil {
return x.Backend return x.Backend
@ -785,61 +775,6 @@ func (x *DynamicValue) GetMsgpack() []byte {
return nil 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 // 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 // used to refer to a sub-structure within a dynamic data structure presented
// separately. // separately.
@ -854,7 +789,7 @@ type Path struct {
func (x *Path) Reset() { func (x *Path) Reset() {
*x = Path{} *x = Path{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_planfile_proto_msgTypes[7] mi := &file_planfile_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -867,7 +802,7 @@ func (x *Path) String() string {
func (*Path) ProtoMessage() {} func (*Path) ProtoMessage() {}
func (x *Path) ProtoReflect() protoreflect.Message { func (x *Path) ProtoReflect() protoreflect.Message {
mi := &file_planfile_proto_msgTypes[7] mi := &file_planfile_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -880,7 +815,7 @@ func (x *Path) ProtoReflect() protoreflect.Message {
// Deprecated: Use Path.ProtoReflect.Descriptor instead. // Deprecated: Use Path.ProtoReflect.Descriptor instead.
func (*Path) Descriptor() ([]byte, []int) { 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 { func (x *Path) GetSteps() []*Path_Step {
@ -904,7 +839,7 @@ type Path_Step struct {
func (x *Path_Step) Reset() { func (x *Path_Step) Reset() {
*x = Path_Step{} *x = Path_Step{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_planfile_proto_msgTypes[10] mi := &file_planfile_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -917,7 +852,7 @@ func (x *Path_Step) String() string {
func (*Path_Step) ProtoMessage() {} func (*Path_Step) ProtoMessage() {}
func (x *Path_Step) ProtoReflect() protoreflect.Message { func (x *Path_Step) ProtoReflect() protoreflect.Message {
mi := &file_planfile_proto_msgTypes[10] mi := &file_planfile_proto_msgTypes[8]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -930,7 +865,7 @@ func (x *Path_Step) ProtoReflect() protoreflect.Message {
// Deprecated: Use Path_Step.ProtoReflect.Descriptor instead. // Deprecated: Use Path_Step.ProtoReflect.Descriptor instead.
func (*Path_Step) Descriptor() ([]byte, []int) { 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 { func (m *Path_Step) GetSelector() isPath_Step_Selector {
@ -978,7 +913,7 @@ var File_planfile_proto protoreflect.FileDescriptor
var file_planfile_proto_rawDesc = []byte{ var file_planfile_proto_rawDesc = []byte{
0x0a, 0x0e, 0x70, 0x6c, 0x61, 0x6e, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 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, 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, 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, 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, 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, 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, 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, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x07,
0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e,
0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x07,
0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x48, 0x61, 0x73, 0x68, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x1a, 0x52, 0x0a, 0x0e, 0x56, 0x61, 0x72, 0x69, 0x61,
0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
0x72, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76,
0x6e, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70,
0x6e, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6c, 0x61, 0x6e, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65,
0x6e, 0x64, 0x1a, 0x52, 0x0a, 0x0e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x69, 0x0a, 0x07, 0x42,
0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01,
0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x63, 0x6f,
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x44, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70,
0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x6c, 0x61, 0x6e, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65,
0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4f, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b,
0x65, 0x72, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x6f, 0x72,
0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0xe4, 0x01, 0x0a, 0x06, 0x43, 0x68, 0x61, 0x6e, 0x67,
0x22, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28,
0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x05, 0x76, 0x61, 0x0e, 0x32, 0x0e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f,
0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x69, 0x0a, 0x07, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x06, 0x76, 0x61, 0x6c,
0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70, 0x6c,
0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x61, 0x6e, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x16, 0x62, 0x65, 0x66, 0x6f, 0x72,
0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x65, 0x5f, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68,
0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e,
0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x14, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x6e,
0x63, 0x65, 0x22, 0xe4, 0x01, 0x0a, 0x06, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x40, 0x0a, 0x15, 0x61,
0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70,
0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x61, 0x74, 0x68, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x13, 0x61, 0x66, 0x74, 0x65, 0x72, 0x53,
0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x44, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x73, 0x22, 0xd3, 0x02,
0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e,
0x75, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x16, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72,
0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x03, 0x20, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x22, 0x0a, 0x0d,
0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x70, 0x72, 0x65, 0x76, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0e, 0x20,
0x68, 0x52, 0x14, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x65, 0x76, 0x52, 0x75, 0x6e, 0x41, 0x64, 0x64, 0x72,
0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x40, 0x0a, 0x15, 0x61, 0x66, 0x74, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18,
0x5f, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x4b, 0x65,
0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x08, 0x20,
0x50, 0x61, 0x74, 0x68, 0x52, 0x13, 0x61, 0x66, 0x74, 0x65, 0x72, 0x53, 0x65, 0x6e, 0x73, 0x69, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x26, 0x0a,
0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x73, 0x22, 0xd3, 0x02, 0x0a, 0x16, 0x52, 0x65, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e,
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x06, 0x63,
0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65,
0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x22, 0x0a, 0x0d, 0x70, 0x72, 0x65, 0x76, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12,
0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x37, 0x0a, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x70, 0x6c,
0x0b, 0x70, 0x72, 0x65, 0x76, 0x52, 0x75, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c,
0x64, 0x65, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65,
0x09, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x12, 0x49, 0x0a, 0x0d, 0x61, 0x63, 0x74, 0x69,
0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0e, 0x32,
0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x24, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
0x6e, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52,
0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x0c, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61,
0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x73, 0x6f, 0x6e, 0x22, 0x68, 0x0a, 0x0c, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x43, 0x68, 0x61,
0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x10, 0x72, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x18, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67,
0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e,
0x61, 0x74, 0x68, 0x52, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x52, 0x65, 0x70, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12,
0x6c, 0x61, 0x63, 0x65, 0x12, 0x49, 0x0a, 0x0d, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01,
0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x74, 0x66, 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x22, 0x28, 0x0a,
0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x73, 0x0c, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a,
0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x07, 0x6d, 0x73, 0x67, 0x70, 0x61, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07,
0x6e, 0x52, 0x0c, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x6d, 0x73, 0x67, 0x70, 0x61, 0x63, 0x6b, 0x22, 0xa5, 0x01, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68,
0x68, 0x0a, 0x0c, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x27, 0x0a, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x2e, 0x53, 0x74,
0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x65, 0x70, 0x52, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x1a, 0x74, 0x0a, 0x04, 0x53, 0x74, 0x65,
0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x70, 0x12, 0x27, 0x0a, 0x0e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6e,
0x6e, 0x67, 0x65, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0d, 0x61, 0x74, 0x74,
0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x65, 0x6c,
0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x22, 0x28, 0x0a, 0x0c, 0x44, 0x79, 0x6e, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x73, 0x67, 0x14, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63,
0x70, 0x61, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x73, 0x67, 0x70, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
0x61, 0x63, 0x6b, 0x22, 0x1e, 0x0a, 0x04, 0x48, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x4b, 0x65, 0x79, 0x42, 0x0a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2a,
0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x68, 0x61, 0x31, 0x0a, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x4f, 0x52, 0x4d, 0x41,
0x32, 0x35, 0x36, 0x22, 0xa5, 0x01, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, 0x27, 0x0a, 0x05, 0x4c, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4f, 0x59, 0x10, 0x01,
0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x12, 0x10, 0x0a, 0x0c, 0x52, 0x45, 0x46, 0x52, 0x45, 0x53, 0x48, 0x5f, 0x4f, 0x4e, 0x4c, 0x59,
0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x2e, 0x53, 0x74, 0x65, 0x70, 0x52, 0x05, 0x10, 0x02, 0x2a, 0x70, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x08, 0x0a, 0x04,
0x73, 0x74, 0x65, 0x70, 0x73, 0x1a, 0x74, 0x0a, 0x04, 0x53, 0x74, 0x65, 0x70, 0x12, 0x27, 0x0a, 0x4e, 0x4f, 0x4f, 0x50, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45,
0x0e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x52, 0x45, 0x41, 0x44, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06,
0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x4c, 0x45,
0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x54, 0x45, 0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x54,
0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x48, 0x45, 0x4e, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12,
0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4e, 0x5f, 0x44, 0x45, 0x4c, 0x45,
0x65, 0x48, 0x00, 0x52, 0x0a, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x42, 0x54, 0x45, 0x10, 0x07, 0x2a, 0xa7, 0x02, 0x0a, 0x1c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
0x0a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2a, 0x31, 0x0a, 0x04, 0x4d, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52,
0x6f, 0x64, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12,
0x0b, 0x0a, 0x07, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4f, 0x59, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55,
0x52, 0x45, 0x46, 0x52, 0x45, 0x53, 0x48, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x02, 0x2a, 0x70, 0x53, 0x45, 0x5f, 0x54, 0x41, 0x49, 0x4e, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12,
0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4f, 0x50, 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x5f, 0x42, 0x59, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45,
0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x01, 0x12, 0x08, 0x53, 0x54, 0x10, 0x02, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x5f,
0x0a, 0x04, 0x52, 0x45, 0x41, 0x44, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x50, 0x44, 0x41, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x43, 0x41, 0x4e, 0x4e, 0x4f, 0x54, 0x5f, 0x55,
0x54, 0x45, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x05, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x03, 0x12, 0x25, 0x0a, 0x21, 0x44, 0x45, 0x4c, 0x45, 0x54,
0x12, 0x16, 0x0a, 0x12, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4e, 0x5f, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x4e, 0x4f, 0x5f, 0x52, 0x45, 0x53,
0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x43, 0x52, 0x45, 0x41, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x04, 0x12, 0x23,
0x54, 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4e, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x07, 0x0a, 0x1f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45,
0x2a, 0xa7, 0x02, 0x0a, 0x1c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x73, 0x5f, 0x57, 0x52, 0x4f, 0x4e, 0x47, 0x5f, 0x52, 0x45, 0x50, 0x45, 0x54, 0x49, 0x54, 0x49, 0x4f,
0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x4e, 0x10, 0x05, 0x12, 0x1e, 0x0a, 0x1a, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45,
0x6e, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x49, 0x4e, 0x44, 0x45,
0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x54, 0x58, 0x10, 0x06, 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45,
0x41, 0x49, 0x4e, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x45, 0x50, 0x4c, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x45, 0x41, 0x43, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x07,
0x41, 0x43, 0x45, 0x5f, 0x42, 0x59, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55,
0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x53, 0x45, 0x5f, 0x4e, 0x4f, 0x5f, 0x4d, 0x4f, 0x44, 0x55, 0x4c, 0x45, 0x10, 0x08, 0x42, 0x42,
0x55, 0x53, 0x45, 0x5f, 0x43, 0x41, 0x4e, 0x4e, 0x4f, 0x54, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73,
0x45, 0x10, 0x03, 0x12, 0x25, 0x0a, 0x21, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d,
0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x4e, 0x4f, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x61, 0x6e, 0x73, 0x2f,
0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x04, 0x12, 0x23, 0x0a, 0x1f, 0x44, 0x45, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x61, 0x6e, 0x70, 0x72, 0x6f,
0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x57, 0x52, 0x4f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
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 ( var (
@ -1139,7 +1062,7 @@ func file_planfile_proto_rawDescGZIP() []byte {
} }
var file_planfile_proto_enumTypes = make([]protoimpl.EnumInfo, 3) 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{}{ var file_planfile_proto_goTypes = []interface{}{
(Mode)(0), // 0: tfplan.Mode (Mode)(0), // 0: tfplan.Mode
(Action)(0), // 1: tfplan.Action (Action)(0), // 1: tfplan.Action
@ -1150,38 +1073,34 @@ var file_planfile_proto_goTypes = []interface{}{
(*ResourceInstanceChange)(nil), // 6: tfplan.ResourceInstanceChange (*ResourceInstanceChange)(nil), // 6: tfplan.ResourceInstanceChange
(*OutputChange)(nil), // 7: tfplan.OutputChange (*OutputChange)(nil), // 7: tfplan.OutputChange
(*DynamicValue)(nil), // 8: tfplan.DynamicValue (*DynamicValue)(nil), // 8: tfplan.DynamicValue
(*Hash)(nil), // 9: tfplan.Hash (*Path)(nil), // 9: tfplan.Path
(*Path)(nil), // 10: tfplan.Path nil, // 10: tfplan.Plan.VariablesEntry
nil, // 11: tfplan.Plan.VariablesEntry (*Path_Step)(nil), // 11: tfplan.Path.Step
nil, // 12: tfplan.Plan.ProviderHashesEntry
(*Path_Step)(nil), // 13: tfplan.Path.Step
} }
var file_planfile_proto_depIdxs = []int32{ var file_planfile_proto_depIdxs = []int32{
0, // 0: tfplan.Plan.ui_mode:type_name -> tfplan.Mode 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, // 2: tfplan.Plan.resource_changes:type_name -> tfplan.ResourceInstanceChange
6, // 3: tfplan.Plan.resource_drift:type_name -> tfplan.ResourceInstanceChange 6, // 3: tfplan.Plan.resource_drift:type_name -> tfplan.ResourceInstanceChange
7, // 4: tfplan.Plan.output_changes:type_name -> tfplan.OutputChange 7, // 4: tfplan.Plan.output_changes:type_name -> tfplan.OutputChange
12, // 5: tfplan.Plan.provider_hashes:type_name -> tfplan.Plan.ProviderHashesEntry 4, // 5: tfplan.Plan.backend:type_name -> tfplan.Backend
4, // 6: tfplan.Plan.backend:type_name -> tfplan.Backend 8, // 6: tfplan.Backend.config:type_name -> tfplan.DynamicValue
8, // 7: tfplan.Backend.config:type_name -> tfplan.DynamicValue 1, // 7: tfplan.Change.action:type_name -> tfplan.Action
1, // 8: tfplan.Change.action:type_name -> tfplan.Action 8, // 8: tfplan.Change.values:type_name -> tfplan.DynamicValue
8, // 9: tfplan.Change.values:type_name -> tfplan.DynamicValue 9, // 9: tfplan.Change.before_sensitive_paths:type_name -> tfplan.Path
10, // 10: tfplan.Change.before_sensitive_paths:type_name -> tfplan.Path 9, // 10: tfplan.Change.after_sensitive_paths:type_name -> tfplan.Path
10, // 11: tfplan.Change.after_sensitive_paths:type_name -> tfplan.Path 5, // 11: tfplan.ResourceInstanceChange.change:type_name -> tfplan.Change
5, // 12: tfplan.ResourceInstanceChange.change:type_name -> tfplan.Change 9, // 12: tfplan.ResourceInstanceChange.required_replace:type_name -> tfplan.Path
10, // 13: tfplan.ResourceInstanceChange.required_replace:type_name -> tfplan.Path 2, // 13: tfplan.ResourceInstanceChange.action_reason:type_name -> tfplan.ResourceInstanceActionReason
2, // 14: tfplan.ResourceInstanceChange.action_reason:type_name -> tfplan.ResourceInstanceActionReason 5, // 14: tfplan.OutputChange.change:type_name -> tfplan.Change
5, // 15: tfplan.OutputChange.change:type_name -> tfplan.Change 11, // 15: tfplan.Path.steps:type_name -> tfplan.Path.Step
13, // 16: tfplan.Path.steps:type_name -> tfplan.Path.Step 8, // 16: tfplan.Plan.VariablesEntry.value:type_name -> tfplan.DynamicValue
8, // 17: tfplan.Plan.VariablesEntry.value:type_name -> tfplan.DynamicValue 8, // 17: tfplan.Path.Step.element_key:type_name -> tfplan.DynamicValue
9, // 18: tfplan.Plan.ProviderHashesEntry.value:type_name -> tfplan.Hash 18, // [18:18] is the sub-list for method output_type
8, // 19: tfplan.Path.Step.element_key:type_name -> tfplan.DynamicValue 18, // [18:18] is the sub-list for method input_type
20, // [20:20] is the sub-list for method output_type 18, // [18:18] is the sub-list for extension type_name
20, // [20:20] is the sub-list for method input_type 18, // [18:18] is the sub-list for extension extendee
20, // [20:20] is the sub-list for extension type_name 0, // [0:18] is the sub-list for field type_name
20, // [20:20] is the sub-list for extension extendee
0, // [0:20] is the sub-list for field type_name
} }
func init() { file_planfile_proto_init() } 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{} { 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 { switch v := v.(*Path); i {
case 0: case 0:
return &v.state return &v.state
@ -1286,7 +1193,7 @@ func file_planfile_proto_init() {
return nil 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 { switch v := v.(*Path_Step); i {
case 0: case 0:
return &v.state 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_AttributeName)(nil),
(*Path_Step_ElementKey)(nil), (*Path_Step_ElementKey)(nil),
} }
@ -1309,7 +1216,7 @@ func file_planfile_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_planfile_proto_rawDesc, RawDescriptor: file_planfile_proto_rawDesc,
NumEnums: 3, NumEnums: 3,
NumMessages: 11, NumMessages: 9,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,
}, },

View File

@ -58,10 +58,6 @@ message Plan {
// The version string for the Terraform binary that created this plan. // The version string for the Terraform binary that created this plan.
string terraform_version = 14; string terraform_version = 14;
// SHA256 digests of all of the provider plugin binaries that were used
// in the creation of this plan.
map<string, Hash> provider_hashes = 15;
// Backend is a description of the backend configuration and other related // Backend is a description of the backend configuration and other related
// settings at the time the plan was created. // settings at the time the plan was created.
Backend backend = 13; Backend backend = 13;
@ -218,18 +214,6 @@ message DynamicValue {
bytes msgpack = 1; 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 // 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 // used to refer to a sub-structure within a dynamic data structure presented
// separately. // separately.

View File

@ -34,7 +34,6 @@ type Plan struct {
DriftedResources []*ResourceInstanceChangeSrc DriftedResources []*ResourceInstanceChangeSrc
TargetAddrs []addrs.Targetable TargetAddrs []addrs.Targetable
ForceReplaceAddrs []addrs.AbsResourceInstance ForceReplaceAddrs []addrs.AbsResourceInstance
ProviderSHA256s map[string][]byte
Backend Backend Backend Backend
// PrevRunState and PriorState both describe the situation that the plan // PrevRunState and PriorState both describe the situation that the plan

View File

@ -52,7 +52,6 @@ func TestRoundtrip(t *testing.T) {
Outputs: []*plans.OutputChangeSrc{}, Outputs: []*plans.OutputChangeSrc{},
}, },
DriftedResources: []*plans.ResourceInstanceChangeSrc{}, DriftedResources: []*plans.ResourceInstanceChangeSrc{},
ProviderSHA256s: map[string][]byte{},
VariableValues: map[string]plans.DynamicValue{ VariableValues: map[string]plans.DynamicValue{
"foo": plans.DynamicValue([]byte("foo placeholder")), "foo": plans.DynamicValue([]byte("foo placeholder")),
}, },

View File

@ -57,8 +57,6 @@ func readTfplan(r io.Reader) (*plans.Plan, error) {
Resources: []*plans.ResourceInstanceChangeSrc{}, Resources: []*plans.ResourceInstanceChangeSrc{},
}, },
DriftedResources: []*plans.ResourceInstanceChangeSrc{}, DriftedResources: []*plans.ResourceInstanceChangeSrc{},
ProviderSHA256s: map[string][]byte{},
} }
switch rawPlan.UiMode { switch rawPlan.UiMode {
@ -125,14 +123,6 @@ func readTfplan(r io.Reader) (*plans.Plan, error) {
plan.ForceReplaceAddrs = append(plan.ForceReplaceAddrs, addr) 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 { for name, rawVal := range rawPlan.Variables {
val, err := valueFromTfplan(rawVal) val, err := valueFromTfplan(rawVal)
if err != nil { if err != nil {
@ -358,7 +348,6 @@ func writeTfplan(plan *plans.Plan, w io.Writer) error {
rawPlan := &planproto.Plan{ rawPlan := &planproto.Plan{
Version: tfplanFormatVersion, Version: tfplanFormatVersion,
TerraformVersion: version.String(), TerraformVersion: version.String(),
ProviderHashes: map[string]*planproto.Hash{},
Variables: map[string]*planproto.DynamicValue{}, Variables: map[string]*planproto.DynamicValue{},
OutputChanges: []*planproto.OutputChange{}, OutputChanges: []*planproto.OutputChange{},
@ -426,12 +415,6 @@ func writeTfplan(plan *plans.Plan, w io.Writer) error {
rawPlan.ForceReplaceAddrs = append(rawPlan.ForceReplaceAddrs, replaceAddr.String()) 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 { for name, val := range plan.VariableValues {
rawPlan.Variables[name] = valueToTfplan(val) rawPlan.Variables[name] = valueToTfplan(val)
} }

View File

@ -165,14 +165,6 @@ func TestTFPlanRoundTrip(t *testing.T) {
Name: "woot", Name: "woot",
}.Absolute(addrs.RootModuleInstance), }.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{ Backend: plans.Backend{
Type: "local", Type: "local",
Config: mustNewDynamicValue( Config: mustNewDynamicValue(

View File

@ -4,10 +4,9 @@ import (
"context" "context"
"fmt" "fmt"
"log" "log"
"strings" "sort"
"sync" "sync"
"github.com/apparentlymart/go-versions/versions"
"github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/providers" "github.com/hashicorp/terraform/internal/providers"
@ -16,8 +15,6 @@ import (
"github.com/hashicorp/terraform/internal/tfdiags" "github.com/hashicorp/terraform/internal/tfdiags"
"github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/internal/depsfile"
"github.com/hashicorp/terraform/internal/getproviders"
_ "github.com/hashicorp/terraform/internal/logging" _ "github.com/hashicorp/terraform/internal/logging"
) )
@ -43,18 +40,6 @@ type ContextOpts struct {
Providers map[addrs.Provider]providers.Factory Providers map[addrs.Provider]providers.Factory
Provisioners map[string]provisioners.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 UIInput UIInput
} }
@ -89,8 +74,6 @@ type Context struct {
meta *ContextMeta meta *ContextMeta
plugins *contextPlugins plugins *contextPlugins
dependencyLocks *depsfile.Locks
providersInDevelopment map[addrs.Provider]struct{}
hooks []Hook hooks []Hook
sh *stopHook sh *stopHook
@ -99,7 +82,6 @@ type Context struct {
l sync.Mutex // Lock acquired during any task l sync.Mutex // Lock acquired during any task
parallelSem Semaphore parallelSem Semaphore
providerInputConfig map[string]map[string]cty.Value providerInputConfig map[string]map[string]cty.Value
providerSHA256s map[string][]byte
runCond *sync.Cond runCond *sync.Cond
runContext context.Context runContext context.Context
runContextCancel context.CancelFunc runContextCancel context.CancelFunc
@ -154,12 +136,9 @@ func NewContext(opts *ContextOpts) (*Context, tfdiags.Diagnostics) {
uiInput: opts.UIInput, uiInput: opts.UIInput,
plugins: plugins, plugins: plugins,
dependencyLocks: opts.LockedDependencies,
providersInDevelopment: opts.ProvidersInDevelopment,
parallelSem: NewSemaphore(par), parallelSem: NewSemaphore(par),
providerInputConfig: make(map[string]map[string]cty.Value), providerInputConfig: make(map[string]map[string]cty.Value),
providerSHA256s: opts.ProviderSHA256s,
sh: sh, sh: sh,
}, diags }, diags
} }
@ -174,50 +153,6 @@ func (c *Context) Schemas(config *configs.Config, state *states.State) (*Schemas
var diags tfdiags.Diagnostics 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) ret, err := loadSchemas(config, state, c.plugins)
if err != nil { if err != nil {
diags = diags.Append(tfdiags.Sourceless( diags = diags.Append(tfdiags.Sourceless(
@ -381,3 +316,115 @@ func (c *Context) watchStop(walker *ContextGraphWalker) (chan struct{}, <-chan s
return stop, wait 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
}

View File

@ -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) diags = diags.Append(moreDiags)
// If version constraints are not met then we'll bail early since otherwise // If required dependencies are not available then we'll bail early since
// we're likely to just see a bunch of other errors related to // otherwise we're likely to just see a bunch of other errors related to
// incompatibilities, which could be overwhelming for the user. // incompatibilities, which could be overwhelming for the user.
if diags.HasErrors() { if diags.HasErrors() {
return nil, diags return nil, diags
@ -161,7 +161,6 @@ The -target option is not for routine use, and is provided only for exceptional
if plan != nil { if plan != nil {
plan.VariableValues = varVals plan.VariableValues = varVals
plan.TargetAddrs = opts.Targets plan.TargetAddrs = opts.Targets
plan.ProviderSHA256s = c.providerSHA256s
} else if !diags.HasErrors() { } else if !diags.HasErrors() {
panic("nil plan but no errors") panic("nil plan but no errors")
} }

View File

@ -33,9 +33,6 @@ func TestContext2Plan_basic(t *testing.T) {
Providers: map[addrs.Provider]providers.Factory{ Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
}, },
ProviderSHA256s: map[string][]byte{
"aws": []byte("placeholder"),
},
}) })
plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) 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)) 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 schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
ty := schema.ImpliedType() ty := schema.ImpliedType()
for _, r := range plan.Changes.Resources { for _, r := range plan.Changes.Resources {

View File

@ -43,6 +43,11 @@ func (cp *contextPlugins) init() {
cp.provisionerSchemas = make(map[string]*configschema.Block, len(cp.provisionerFactories)) 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) { func (cp *contextPlugins) NewProviderInstance(addr addrs.Provider) (providers.Interface, error) {
f, ok := cp.providerFactories[addr] f, ok := cp.providerFactories[addr]
if !ok { 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) { func (cp *contextPlugins) NewProvisionerInstance(typ string) (provisioners.Interface, error) {
f, ok := cp.provisionerFactories[typ] f, ok := cp.provisionerFactories[typ]
if !ok { if !ok {

View File

@ -2,7 +2,6 @@ package terraform
import ( import (
"reflect" "reflect"
"regexp"
"sort" "sort"
"strings" "strings"
"sync" "sync"
@ -1051,8 +1050,8 @@ func TestContext2Refresh_unknownProvider(t *testing.T) {
t.Fatal("successfully refreshed; want error") t.Fatal("successfully refreshed; want error")
} }
if !regexp.MustCompile(`failed to instantiate provider ".+"`).MatchString(diags.Err().Error()) { if got, want := diags.Err().Error(), "Missing required provider"; !strings.Contains(got, want) {
t.Fatalf("wrong error: %s", diags.Err()) t.Errorf("missing expected error\nwant substring: %s\ngot:\n%s", want, got)
} }
} }

View File

@ -15,12 +15,10 @@ import (
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts" "github.com/google/go-cmp/cmp/cmpopts"
"github.com/hashicorp/go-version" "github.com/hashicorp/go-version"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/configs/configload" "github.com/hashicorp/terraform/internal/configs/configload"
"github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/configs/hcl2shim" "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"
"github.com/hashicorp/terraform/internal/plans/planfile" "github.com/hashicorp/terraform/internal/plans/planfile"
"github.com/hashicorp/terraform/internal/providers" "github.com/hashicorp/terraform/internal/providers"
@ -122,184 +120,82 @@ func TestNewContextRequiredVersion(t *testing.T) {
} }
} }
func TestNewContext_lockedDependencies(t *testing.T) { func TestContext_missingPlugins(t *testing.T) {
// TODO: Remove this test altogether once we've factored out the version ctx, diags := NewContext(&ContextOpts{})
// and checksum verification to be exclusively the caller's responsibility. assertNoDiagnostics(t, diags)
t.Skip("only one step away from locked dependencies being the caller's responsibility")
configBeepGreaterThanOne := ` configSrc := `
terraform { terraform {
required_providers { required_providers {
beep = { explicit = {
source = "example.com/foo/beep" source = "example.com/foo/beep"
version = ">= 1.0.0" }
builtin = {
source = "terraform.io/builtin/nonexist"
} }
} }
} }
resource "implicit_thing" "a" {
provisioner "nonexist" {
}
}
resource "implicit_thing" "b" {
provider = implicit2
}
` `
configBeepLessThanOne := `
terraform {
required_providers {
beep = {
source = "example.com/foo/beep"
version = "< 1.0.0"
}
}
}
`
configBuiltin := `
terraform {
required_providers {
terraform = {
source = "terraform.io/builtin/terraform"
}
}
}
`
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 { cfg := testModuleInline(t, map[string]string{
Config string "main.tf": configSrc,
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".`,
},
}
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,
}) })
c, diags := NewContext(opts) // Validate and Plan are the two entry points where we explicitly verify
if diags.HasErrors() { // the available plugins match what the configuration needs. For other
t.Fatalf("unexpected NewContext error: %s", diags.Err()) // 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,
} }
diags = c.Validate(m) for testName, gotDiags := range tests {
if tc.WantErr != "" { t.Run(testName, func(t *testing.T) {
if len(diags) == 0 { var wantDiags tfdiags.Diagnostics
t.Fatal("expected diags but none returned") wantDiags = wantDiags.Append(
} tfdiags.Sourceless(
if got, want := diags.Err().Error(), tc.WantErr; got != want { tfdiags.Error,
t.Errorf("wrong diags\n got: %s\nwant: %s", got, want) "Missing required provider",
} "This configuration requires built-in provider terraform.io/builtin/nonexist, but that provider isn't available in this Terraform version.",
} else { ),
if len(diags) > 0 { tfdiags.Sourceless(
t.Errorf("unexpected diags: %s", diags.Err()) 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 nil, nil, nil, err
} }
return &ContextOpts{ // Note: This has grown rather silly over the course of ongoing refactoring,
ProviderSHA256s: plan.ProviderSHA256s, // because ContextOpts is no longer actually responsible for carrying
}, config, plan, nil // 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 // legacyPlanComparisonString produces a string representation of the changes
@ -994,6 +894,24 @@ func assertNoErrors(t *testing.T, diags tfdiags.Diagnostics) {
t.FailNow() 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 // 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 // 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 // a test. It does not generate any errors or fail the test. See

View File

@ -26,10 +26,10 @@ func (c *Context) Validate(config *configs.Config) tfdiags.Diagnostics {
var diags tfdiags.Diagnostics var diags tfdiags.Diagnostics
moreDiags := CheckCoreVersionRequirements(config) moreDiags := c.checkConfigDependencies(config)
diags = diags.Append(moreDiags) diags = diags.Append(moreDiags)
// If version constraints are not met then we'll bail early since otherwise // If required dependencies are not available then we'll bail early since
// we're likely to just see a bunch of other errors related to // otherwise we're likely to just see a bunch of other errors related to
// incompatibilities, which could be overwhelming for the user. // incompatibilities, which could be overwhelming for the user.
if diags.HasErrors() { if diags.HasErrors() {
return diags return diags