From 7e6d5d7b0802500e68e52dfe9541f2e1a17fe45e Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Wed, 12 Feb 2025 14:43:13 +0100 Subject: [PATCH] validate resource identity schema --- internal/plugin/convert/schema.go | 18 +++++++++++++++--- internal/plugin/grpc_provider.go | 10 +++++++++- internal/plugin6/convert/schema.go | 18 +++++++++++++++--- internal/plugin6/grpc_provider.go | 14 +++++++++++++- 4 files changed, 52 insertions(+), 8 deletions(-) diff --git a/internal/plugin/convert/schema.go b/internal/plugin/convert/schema.go index 9513b5f32e6c..138838ce731e 100644 --- a/internal/plugin/convert/schema.go +++ b/internal/plugin/convert/schema.go @@ -5,11 +5,13 @@ package convert import ( "encoding/json" + "fmt" "reflect" "sort" "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/providers" + "github.com/hashicorp/terraform/internal/tfdiags" proto "github.com/hashicorp/terraform/internal/tfplugin5" ) @@ -189,7 +191,8 @@ func sortedKeys(m interface{}) []string { return keys } -func ProtoToResourceIdentitySchema(s *proto.ResourceIdentitySchema) providers.IdentitySchema { +func ProtoToResourceIdentitySchema(s *proto.ResourceIdentitySchema) (providers.IdentitySchema, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics schema := providers.IdentitySchema{ Version: s.Version, Attributes: make(configschema.IdentityAttributes), @@ -204,12 +207,21 @@ func ProtoToResourceIdentitySchema(s *proto.ResourceIdentitySchema) providers.Id if a.Type != nil { if err := json.Unmarshal(a.Type, &attr.Type); err != nil { - panic(err) + diags = diags.Append(fmt.Errorf("Could not unmarshal type for attribute %q: %w", a.Name, err)) } + } else { + diags = diags.Append(fmt.Errorf("Attribute %q is missing a type definition", a.Name)) + } + + if attr.RequiredForImport && attr.OptionalForImport { + diags = diags.Append(fmt.Errorf("Attribute %q cannot be both required and optional for import", a.Name)) + } + if !attr.RequiredForImport && !attr.OptionalForImport { + diags = diags.Append(fmt.Errorf("Attribute %q must be either required or optional for import", a.Name)) } schema.Attributes[a.Name] = attr } - return schema + return schema, diags } diff --git a/internal/plugin/grpc_provider.go b/internal/plugin/grpc_provider.go index bb7af23474dd..4488a5f13131 100644 --- a/internal/plugin/grpc_provider.go +++ b/internal/plugin/grpc_provider.go @@ -21,6 +21,7 @@ import ( "github.com/hashicorp/terraform/internal/logging" "github.com/hashicorp/terraform/internal/plugin/convert" "github.com/hashicorp/terraform/internal/providers" + "github.com/hashicorp/terraform/internal/tfdiags" proto "github.com/hashicorp/terraform/internal/tfplugin5" ) @@ -218,7 +219,14 @@ func (p *GRPCProvider) GetResourceIdentitySchemas() providers.GetResourceIdentit } for name, res := range protoResp.IdentitySchemas { - resp.IdentityTypes[name] = convert.ProtoToResourceIdentitySchema(res) + var protoDiags tfdiags.Diagnostics + resp.IdentityTypes[name], protoDiags = convert.ProtoToResourceIdentitySchema(res) + + var wrappedDiags tfdiags.Diagnostics + for _, diag := range protoDiags { + wrappedDiags = wrappedDiags.Append(fmt.Errorf("error in resource identity schema for resource %q: %s", name, diag)) + } + resp.Diagnostics = resp.Diagnostics.Append(wrappedDiags) } // set the global cache if we can diff --git a/internal/plugin6/convert/schema.go b/internal/plugin6/convert/schema.go index 5c242fd7db0a..bc3ad67c69cb 100644 --- a/internal/plugin6/convert/schema.go +++ b/internal/plugin6/convert/schema.go @@ -5,11 +5,13 @@ package convert import ( "encoding/json" + "fmt" "reflect" "sort" "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/providers" + "github.com/hashicorp/terraform/internal/tfdiags" proto "github.com/hashicorp/terraform/internal/tfplugin6" "github.com/zclconf/go-cty/cty" ) @@ -103,7 +105,8 @@ func ProtoToProviderSchema(s *proto.Schema) providers.Schema { } } -func ProtoToResourceIdentitySchema(s *proto.ResourceIdentitySchema) providers.IdentitySchema { +func ProtoToResourceIdentitySchema(s *proto.ResourceIdentitySchema) (providers.IdentitySchema, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics schema := providers.IdentitySchema{ Version: s.Version, Attributes: make(configschema.IdentityAttributes), @@ -118,14 +121,23 @@ func ProtoToResourceIdentitySchema(s *proto.ResourceIdentitySchema) providers.Id if a.Type != nil { if err := json.Unmarshal(a.Type, &attr.Type); err != nil { - panic(err) + diags = diags.Append(fmt.Errorf("Could not unmarshal type for attribute %q: %w", a.Name, err)) } + } else { + diags = diags.Append(fmt.Errorf("Attribute %q is missing a type definition", a.Name)) + } + + if attr.RequiredForImport && attr.OptionalForImport { + diags = diags.Append(fmt.Errorf("Attribute %q cannot be both required and optional for import", a.Name)) + } + if !attr.RequiredForImport && !attr.OptionalForImport { + diags = diags.Append(fmt.Errorf("Attribute %q must be either required or optional for import", a.Name)) } schema.Attributes[a.Name] = attr } - return schema + return schema, diags } // ProtoToConfigSchema takes the GetSchcema_Block from a grpc response and converts it diff --git a/internal/plugin6/grpc_provider.go b/internal/plugin6/grpc_provider.go index 8bddd2bd9900..5f838ef85b7e 100644 --- a/internal/plugin6/grpc_provider.go +++ b/internal/plugin6/grpc_provider.go @@ -21,6 +21,7 @@ import ( "github.com/hashicorp/terraform/internal/logging" "github.com/hashicorp/terraform/internal/plugin6/convert" "github.com/hashicorp/terraform/internal/providers" + "github.com/hashicorp/terraform/internal/tfdiags" proto6 "github.com/hashicorp/terraform/internal/tfplugin6" ) @@ -216,7 +217,18 @@ func (p *GRPCProvider) GetResourceIdentitySchemas() providers.GetResourceIdentit } for name, res := range protoResp.IdentitySchemas { - resp.IdentityTypes[name] = convert.ProtoToResourceIdentitySchema(res) + var protoDiags tfdiags.Diagnostics + resp.IdentityTypes[name], protoDiags = convert.ProtoToResourceIdentitySchema(res) + + var wrappedDiags tfdiags.Diagnostics + for _, diag := range protoDiags { + wrappedDiags = wrappedDiags.Append(fmt.Errorf("error in resource identity schema for resource %q: %s", name, diag)) + } + resp.Diagnostics = resp.Diagnostics.Append(wrappedDiags) + } + + if resp.Diagnostics.HasErrors() { + return resp } // set the global cache if we can