diff --git a/CHANGELOG.md b/CHANGELOG.md index ec83c24a09..52f178f4f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,7 @@ S3 BACKEND: * S3 backend endpoints without proto:// default to https:// instead of failing ([#821](https://github.com/opentofu/opentofu/issues/821)) * Most S3 compatible remote state backends should now work without checksum errors / 400s ([#821](https://github.com/opentofu/opentofu/issues/821)) * Logging has been re-added to S3 remote state calls ([#821](https://github.com/opentofu/opentofu/issues/821)) +* Fixed validation for certain optional fields ([875](https://github.com/opentofu/opentofu/issues/875)) ## Previous Releases diff --git a/internal/backend/remote-state/s3/backend.go b/internal/backend/remote-state/s3/backend.go index 9c6658944a..45418178c3 100644 --- a/internal/backend/remote-state/s3/backend.go +++ b/internal/backend/remote-state/s3/backend.go @@ -71,6 +71,7 @@ func (b *Backend) ConfigSchema() *configschema.Block { Description: "AWS region of the S3 Bucket and DynamoDB Table (if used).", }, "endpoints": { + Optional: true, NestedType: &configschema.Object{ Nesting: configschema.NestingSingle, Attributes: map[string]*configschema.Attribute{ @@ -302,6 +303,7 @@ func (b *Backend) ConfigSchema() *configschema.Block { Description: "The endpoint mode of IMDS. Valid values: IPv4, IPv6.", }, "assume_role": { + Optional: true, NestedType: &configschema.Object{ Nesting: configschema.NestingSingle, Attributes: map[string]*configschema.Attribute{ @@ -361,6 +363,7 @@ func (b *Backend) ConfigSchema() *configschema.Block { }, }, "assume_role_with_web_identity": { + Optional: true, NestedType: &configschema.Object{ Nesting: configschema.NestingSingle, Attributes: map[string]*configschema.Attribute{ diff --git a/internal/backend/remote-state/s3/backend_test.go b/internal/backend/remote-state/s3/backend_test.go index 5cfb9243ac..66e019f069 100644 --- a/internal/backend/remote-state/s3/backend_test.go +++ b/internal/backend/remote-state/s3/backend_test.go @@ -1420,6 +1420,18 @@ func TestBackend_includeProtoIfNessesary(t *testing.T) { } } +func TestBackend_schemaCoercionMinimal(t *testing.T) { + example := cty.ObjectVal(map[string]cty.Value{ + "bucket": cty.StringVal("my-bucket"), + "key": cty.StringVal("state.tf"), + }) + schema := New().ConfigSchema() + _, err := schema.CoerceValue(example) + if err != nil { + t.Errorf("Unexpected error: %s", err.Error()) + } +} + func testGetWorkspaceForKey(b *Backend, key string, expected string) error { if actual := b.keyEnv(key); actual != expected { return fmt.Errorf("incorrect workspace for key[%q]. Expected[%q]: Actual[%q]", key, expected, actual) diff --git a/internal/configs/configschema/coerce_value.go b/internal/configs/configschema/coerce_value.go index d7205892f5..607781a0fb 100644 --- a/internal/configs/configschema/coerce_value.go +++ b/internal/configs/configschema/coerce_value.go @@ -5,6 +5,7 @@ package configschema import ( "fmt" + "sort" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/convert" @@ -57,7 +58,15 @@ func (b *Block) coerceValue(in cty.Value, path cty.Path) (cty.Value, error) { attrs := make(map[string]cty.Value) - for name, attrS := range b.Attributes { + // Stable sort keys for consistent error reports + attrKeys := make([]string, 0, len(b.Attributes)) + for key := range b.Attributes { + attrKeys = append(attrKeys, key) + } + sort.Strings(attrKeys) + + for _, name := range attrKeys { + attrS := b.Attributes[name] attrType := impliedType.AttributeType(name) attrConvType := convType.AttributeType(name) @@ -78,7 +87,15 @@ func (b *Block) coerceValue(in cty.Value, path cty.Path) (cty.Value, error) { attrs[name] = val } - for typeName, blockS := range b.BlockTypes { + // Stable sort keys for consistent error reports + typeKeys := make([]string, 0, len(b.BlockTypes)) + for key := range b.BlockTypes { + typeKeys = append(typeKeys, key) + } + sort.Strings(typeKeys) + + for _, typeName := range typeKeys { + blockS := b.BlockTypes[typeName] switch blockS.Nesting { case NestingSingle, NestingGroup: