Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ go 1.22.7
require (
github.com/hashicorp/go-memdb v1.3.4
github.com/hashicorp/terraform-json v0.24.0
github.com/hashicorp/terraform-plugin-framework v1.13.0
github.com/hashicorp/terraform-plugin-framework v1.13.1-0.20250117124025-7e7910eadc9c
github.com/hashicorp/terraform-plugin-framework-nettypes v0.2.0
github.com/hashicorp/terraform-plugin-framework-timeouts v0.5.0
github.com/hashicorp/terraform-plugin-framework-timetypes v0.5.0
github.com/hashicorp/terraform-plugin-framework-validators v0.16.0
github.com/hashicorp/terraform-plugin-go v0.25.0
github.com/hashicorp/terraform-plugin-mux v0.17.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.35.0
Expand Down
8 changes: 6 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,16 @@ github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVW
github.com/hashicorp/terraform-exec v0.21.0/go.mod h1:1PPeMYou+KDUSSeRE9szMZ/oHf4fYUmB923Wzbq1ICg=
github.com/hashicorp/terraform-json v0.24.0 h1:rUiyF+x1kYawXeRth6fKFm/MdfBS6+lW4NbeATsYz8Q=
github.com/hashicorp/terraform-json v0.24.0/go.mod h1:Nfj5ubo9xbu9uiAoZVBsNOjvNKB66Oyrvtit74kC7ow=
github.com/hashicorp/terraform-plugin-framework v1.13.0 h1:8OTG4+oZUfKgnfTdPTJwZ532Bh2BobF4H+yBiYJ/scw=
github.com/hashicorp/terraform-plugin-framework v1.13.0/go.mod h1:j64rwMGpgM3NYXTKuxrCnyubQb/4VKldEKlcG8cvmjU=
github.com/hashicorp/terraform-plugin-framework v1.13.1-0.20250117124025-7e7910eadc9c h1:eliGGc+yGbXegGQc0RuHTXZ0ywyLXZuzZRFNSavfxZU=
github.com/hashicorp/terraform-plugin-framework v1.13.1-0.20250117124025-7e7910eadc9c/go.mod h1:j64rwMGpgM3NYXTKuxrCnyubQb/4VKldEKlcG8cvmjU=
github.com/hashicorp/terraform-plugin-framework-nettypes v0.2.0 h1:Zap24rkky7SvNGGNYHMKFhAriP6+6riI21BMYOYgLRE=
github.com/hashicorp/terraform-plugin-framework-nettypes v0.2.0/go.mod h1:CYPq+I5bWsmI8021VJY85hAyOeiEEQpdGW+NapdQn7A=
github.com/hashicorp/terraform-plugin-framework-timeouts v0.5.0 h1:I/N0g/eLZ1ZkLZXUQ0oRSXa8YG/EF0CEuQP1wXdrzKw=
github.com/hashicorp/terraform-plugin-framework-timeouts v0.5.0/go.mod h1:t339KhmxnaF4SzdpxmqW8HnQBHVGYazwtfxU0qCs4eE=
github.com/hashicorp/terraform-plugin-framework-timetypes v0.5.0 h1:v3DapR8gsp3EM8fKMh6up9cJUFQ2iRaFsYLP8UJnCco=
github.com/hashicorp/terraform-plugin-framework-timetypes v0.5.0/go.mod h1:c3PnGE9pHBDfdEVG9t1S1C9ia5LW+gkFR0CygXlM8ak=
github.com/hashicorp/terraform-plugin-framework-validators v0.16.0 h1:O9QqGoYDzQT7lwTXUsZEtgabeWW96zUBh47Smn2lkFA=
github.com/hashicorp/terraform-plugin-framework-validators v0.16.0/go.mod h1:Bh89/hNmqsEWug4/XWKYBwtnw3tbz5BAy1L1OgvbIaY=
github.com/hashicorp/terraform-plugin-go v0.25.0 h1:oi13cx7xXA6QciMcpcFi/rwA974rdTxjqEhXJjbAyks=
github.com/hashicorp/terraform-plugin-go v0.25.0/go.mod h1:+SYagMYadJP86Kvn+TGeV+ofr/R3g4/If0O5sO96MVw=
github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0=
Expand Down
1 change: 1 addition & 0 deletions internal/framework5provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ func (p *testProvider) Resources(_ context.Context) []func() resource.Resource {
NewTFSDKReflectionResource,
NewMoveStateResource,
NewSetNestedBlockWithDefaultsResource,
NewSetSemanticEqualityResource,
}
}

Expand Down
137 changes: 137 additions & 0 deletions internal/framework5provider/set_semantic_equality_resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package framework

import (
"context"

"github.com/hashicorp/terraform-plugin-framework-nettypes/iptypes"
"github.com/hashicorp/terraform-plugin-framework-validators/setvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
)

var _ resource.Resource = SetSemanticEqualityResource{}

func NewSetSemanticEqualityResource() resource.Resource {
return &SetSemanticEqualityResource{}
}

// This resource tests that semantic equality for elements inside of a set are correctly executed
// Original bug: https://github.com/hashicorp/terraform-plugin-framework/issues/1061
type SetSemanticEqualityResource struct{}

func (r SetSemanticEqualityResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_set_semantic_equality"
}

func (r SetSemanticEqualityResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"set_of_ipv6": schema.SetAttribute{
ElementType: iptypes.IPv6AddressType{},
Required: true,
},
},
Blocks: map[string]schema.Block{
"set_nested_block": schema.SetNestedBlock{
Validators: []validator.Set{
setvalidator.IsRequired(),
},
NestedObject: schema.NestedBlockObject{
Attributes: map[string]schema.Attribute{
"ipv6": schema.StringAttribute{
CustomType: iptypes.IPv6AddressType{},
Required: true,
},
},
},
},
},
}
}

func (r SetSemanticEqualityResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data SetSemanticEqualityResourceModel

resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)

if resp.Diagnostics.HasError() {
return
}

// Simulate remote API returning semantically equivalent IPv6 addresses
data.shiftAndShorten()

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r SetSemanticEqualityResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var data SetSemanticEqualityResourceModel

resp.Diagnostics.Append(req.State.Get(ctx, &data)...)

if resp.Diagnostics.HasError() {
return
}

// Simulate remote API returning semantically equivalent IPv6 addresses
data.shiftAndShorten()

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r SetSemanticEqualityResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var data SetSemanticEqualityResourceModel

resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)

if resp.Diagnostics.HasError() {
return
}

// Simulate remote API returning semantically equivalent IPv6 addresses
data.shiftAndShorten()

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r SetSemanticEqualityResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
}

type SetSemanticEqualityResourceModel struct {
SetOfIPv6 types.Set `tfsdk:"set_of_ipv6"`
SetNestedBlock types.Set `tfsdk:"set_nested_block"`
}

var setObjectWithIPv6 = types.ObjectType{
AttrTypes: map[string]attr.Type{
"ipv6": iptypes.IPv6AddressType{},
},
}

// Shifts + switches data to shortened IPv6 addresses, but is semantically equal to test config
func (m *SetSemanticEqualityResourceModel) shiftAndShorten() {
m.SetOfIPv6 = types.SetValueMust(iptypes.IPv6AddressType{}, []attr.Value{
iptypes.NewIPv6AddressValue("2001:DB8::8:800:200C:417A"),
iptypes.NewIPv6AddressValue("::FFFF:192.168.255.255"),
iptypes.NewIPv6AddressValue("::"),
iptypes.NewIPv6AddressValue("::101"),
})

m.SetNestedBlock = types.SetValueMust(setObjectWithIPv6, []attr.Value{
types.ObjectValueMust(setObjectWithIPv6.AttributeTypes(), map[string]attr.Value{
"ipv6": iptypes.NewIPv6AddressValue("::FFFF:192.168.255.255"),
}),
types.ObjectValueMust(setObjectWithIPv6.AttributeTypes(), map[string]attr.Value{
"ipv6": iptypes.NewIPv6AddressValue("FF01::"),
}),
types.ObjectValueMust(setObjectWithIPv6.AttributeTypes(), map[string]attr.Value{
"ipv6": iptypes.NewIPv6AddressValue("2001:DB8::8:800:200C:417A"),
}),
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package framework

import (
"testing"

"github.com/hashicorp/terraform-plugin-framework/providerserver"
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/plancheck"
)

// This resource tests that semantic equality for elements inside of a set are correctly executed
// Original bug: https://github.com/hashicorp/terraform-plugin-framework/issues/1061
func TestSetSemanticEqualityResource(t *testing.T) {
resource.UnitTest(t, resource.TestCase{
ProtoV5ProviderFactories: map[string]func() (tfprotov5.ProviderServer, error){
"framework": providerserver.NewProtocol5WithError(New()),
},
Steps: []resource.TestStep{
{
// The resource Create/Read will return semantically equal data that will cause a diff if returned to Terraform.
// The semantic equality logic in iptypes.IPv6Address allows this configuration to successfully apply.
Config: `resource "framework_set_semantic_equality" "test" {
set_of_ipv6 = [
"0:0:0:0:0:0:0:0",
"2001:0DB8:0000:0000:0008:0800:200C:417A",
"0:0:0:0:0:0:0:101",
"0:0:0:0:0:FFFF:192.168.255.255",
]

set_nested_block {
ipv6 = "FF01:0:0:0:0:0:0:0"
}
set_nested_block {
ipv6 = "2001:db8::8:800:200c:417a"
}
set_nested_block {
ipv6 = "0:0:0:0:0:FFFF:192.168.255.255"
}
}`,
},
{
// Re-ordering the set doesn't produce a diff with semantically equal hardcoded data
Config: `resource "framework_set_semantic_equality" "test" {
set_of_ipv6 = [
"0:0:0:0:0:FFFF:192.168.255.255",
"0:0:0:0:0:0:0:0",
"2001:0DB8:0000:0000:0008:0800:200C:417A",
"0:0:0:0:0:0:0:101",
]

set_nested_block {
ipv6 = "0:0:0:0:0:FFFF:192.168.255.255"
}
set_nested_block {
ipv6 = "FF01:0:0:0:0:0:0:0"
}
set_nested_block {
ipv6 = "2001:db8::8:800:200c:417a"
}
}`,
ConfigPlanChecks: resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectResourceAction("framework_set_semantic_equality.test", plancheck.ResourceActionNoop),
},
},
},
{
// User config changes will still produce a diff, but the apply will be successful with semantically equal data
Config: `resource "framework_set_semantic_equality" "test" {
set_of_ipv6 = [
"0:0:0:0:0:FFFF:192.168.255.255",
"::", # <----------- This update will cause a diff
"2001:0DB8:0000:0000:0008:0800:200C:417A",
"0:0:0:0:0:0:0:101",
]

set_nested_block {
ipv6 = "0:0:0:0:0:FFFF:192.168.255.255"
}
set_nested_block {
ipv6 = "FF01::" # <----------- This update will cause a diff
}
set_nested_block {
ipv6 = "2001:db8::8:800:200c:417a"
}
}`,
ConfigPlanChecks: resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectResourceAction("framework_set_semantic_equality.test", plancheck.ResourceActionUpdate),
},
},
},
},
})
}
1 change: 1 addition & 0 deletions internal/framework6provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ func (p *testProvider) Resources(_ context.Context) []func() resource.Resource {
NewMoveStateResource,
NewSetNestedBlockWithDefaultsResource,
NewSetNestedAttributeWithDefaultsResource,
NewSetSemanticEqualityResource,
}
}

Expand Down
Loading
Loading