Skip to content

Commit 8a90ecb

Browse files
committed
implement ApplyResourceChange fwserver + protov5
1 parent 774af73 commit 8a90ecb

20 files changed

+1347
-73
lines changed

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ toolchain go1.22.7
66

77
require (
88
github.com/google/go-cmp v0.7.0
9-
github.com/hashicorp/terraform-plugin-go v0.26.1-0.20250219133416-0561ec7f0caf
9+
github.com/hashicorp/terraform-plugin-go v0.26.1-0.20250310103402-e49288281ffd
1010
github.com/hashicorp/terraform-plugin-log v0.9.0
1111
)
1212

@@ -28,7 +28,7 @@ require (
2828
golang.org/x/net v0.34.0 // indirect
2929
golang.org/x/sys v0.29.0 // indirect
3030
golang.org/x/text v0.21.0 // indirect
31-
google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect
32-
google.golang.org/grpc v1.70.0 // indirect
31+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
32+
google.golang.org/grpc v1.71.0 // indirect
3333
google.golang.org/protobuf v1.36.5 // indirect
3434
)

go.sum

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0U
2121
github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0=
2222
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
2323
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
24-
github.com/hashicorp/terraform-plugin-go v0.26.1-0.20250219133416-0561ec7f0caf h1:qsHBfGoRp15P8vc95eAVMVO6erkMfpLKZ/6A5lLcR1w=
25-
github.com/hashicorp/terraform-plugin-go v0.26.1-0.20250219133416-0561ec7f0caf/go.mod h1:dUu1RU16sOSKn6w4g+xaAnV0S0wHsVBEY/XH8jv1kx4=
24+
github.com/hashicorp/terraform-plugin-go v0.26.1-0.20250310103402-e49288281ffd h1:g5iwwMyN3PXQ+eyyJGL1rOLPDcD4f0gjUKDsbPabTNU=
25+
github.com/hashicorp/terraform-plugin-go v0.26.1-0.20250310103402-e49288281ffd/go.mod h1:MfDwS/KnIy2QzCwdRtuqIjZ23gpYa9Vm+Z8cFpx8qtU=
2626
github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0=
2727
github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow=
2828
github.com/hashicorp/terraform-registry-address v0.2.4 h1:JXu/zHB2Ymg/TGVCRu10XqNa4Sh2bWcqCNyKWjnCPJA=
@@ -54,16 +54,18 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU
5454
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
5555
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
5656
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
57-
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
58-
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
59-
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
60-
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
61-
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
62-
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
63-
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
64-
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
65-
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
66-
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
57+
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
58+
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
59+
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
60+
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
61+
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
62+
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
63+
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
64+
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
65+
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
66+
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
67+
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
68+
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
6769
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
6870
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
6971
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -76,10 +78,10 @@ golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
7678
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
7779
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
7880
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
79-
google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a h1:hgh8P4EuoxpsuKMXX/To36nOFD7vixReXgn8lPGnt+o=
80-
google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
81-
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
82-
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
81+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
82+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
83+
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
84+
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
8385
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
8486
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
8587
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

internal/fromproto5/applyresourcechange.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717

1818
// ApplyResourceChangeRequest returns the *fwserver.ApplyResourceChangeRequest
1919
// equivalent of a *tfprotov5.ApplyResourceChangeRequest.
20-
func ApplyResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.ApplyResourceChangeRequest, resource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema) (*fwserver.ApplyResourceChangeRequest, diag.Diagnostics) {
20+
func ApplyResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.ApplyResourceChangeRequest, resource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, identitySchema fwschema.Schema) (*fwserver.ApplyResourceChangeRequest, diag.Diagnostics) {
2121
if proto5 == nil {
2222
return nil, nil
2323
}
@@ -40,6 +40,7 @@ func ApplyResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.ApplyReso
4040

4141
fw := &fwserver.ApplyResourceChangeRequest{
4242
ResourceSchema: resourceSchema,
43+
IdentitySchema: identitySchema,
4344
Resource: resource,
4445
}
4546

@@ -55,6 +56,12 @@ func ApplyResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.ApplyReso
5556

5657
fw.PlannedState = plannedState
5758

59+
plannedIdentity, plannedIdentityDiags := ResourceIdentity(ctx, proto5.PlannedIdentity, identitySchema)
60+
61+
diags.Append(plannedIdentityDiags...)
62+
63+
fw.PlannedIdentity = plannedIdentity
64+
5865
priorState, priorStateDiags := State(ctx, proto5.PriorState, resourceSchema)
5966

6067
diags.Append(priorStateDiags...)

internal/fromproto5/applyresourcechange_test.go

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
1818
"github.com/hashicorp/terraform-plugin-framework/internal/privatestate"
1919
"github.com/hashicorp/terraform-plugin-framework/resource"
20+
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
2021
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
2122
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
2223
)
@@ -48,6 +49,30 @@ func TestApplyResourceChangeRequest(t *testing.T) {
4849
},
4950
}
5051

52+
testIdentityProto5Type := tftypes.Object{
53+
AttributeTypes: map[string]tftypes.Type{
54+
"test_identity_attribute": tftypes.String,
55+
},
56+
}
57+
58+
testIdentityProto5Value := tftypes.NewValue(testIdentityProto5Type, map[string]tftypes.Value{
59+
"test_identity_attribute": tftypes.NewValue(tftypes.String, "id-123"),
60+
})
61+
62+
testIdentityProto5DynamicValue, err := tfprotov5.NewDynamicValue(testIdentityProto5Type, testIdentityProto5Value)
63+
64+
if err != nil {
65+
t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err)
66+
}
67+
68+
testIdentitySchema := identityschema.Schema{
69+
Attributes: map[string]identityschema.Attribute{
70+
"test_identity_attribute": identityschema.StringAttribute{
71+
RequiredForImport: true,
72+
},
73+
},
74+
}
75+
5176
testProviderKeyValue := privatestate.MustMarshalToJson(map[string][]byte{
5277
"providerKeyOne": []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`),
5378
})
@@ -61,6 +86,7 @@ func TestApplyResourceChangeRequest(t *testing.T) {
6186
resourceSchema fwschema.Schema
6287
resource resource.Resource
6388
providerMetaSchema fwschema.Schema
89+
identitySchema fwschema.Schema
6490
expected *fwserver.ApplyResourceChangeRequest
6591
expectedDiagnostics diag.Diagnostics
6692
}{
@@ -137,6 +163,42 @@ func TestApplyResourceChangeRequest(t *testing.T) {
137163
ResourceSchema: testFwSchema,
138164
},
139165
},
166+
"plannedidentity-missing-schema": {
167+
input: &tfprotov5.ApplyResourceChangeRequest{
168+
PlannedIdentity: &tfprotov5.ResourceIdentityData{
169+
IdentityData: &testIdentityProto5DynamicValue,
170+
},
171+
},
172+
resourceSchema: testFwSchema,
173+
expected: &fwserver.ApplyResourceChangeRequest{
174+
ResourceSchema: testFwSchema,
175+
},
176+
expectedDiagnostics: diag.Diagnostics{
177+
diag.NewErrorDiagnostic(
178+
"Unable to Convert Resource Identity",
179+
"An unexpected error was encountered when converting the resource identity from the protocol type. "+
180+
"Identity data was sent in the protocol to a resource that doesn't support identity.\n\n"+
181+
"This is always a problem with Terraform or terraform-plugin-framework. Please report this to the provider developer.",
182+
),
183+
},
184+
},
185+
"plannedidentity": {
186+
input: &tfprotov5.ApplyResourceChangeRequest{
187+
PlannedIdentity: &tfprotov5.ResourceIdentityData{
188+
IdentityData: &testIdentityProto5DynamicValue,
189+
},
190+
},
191+
identitySchema: testIdentitySchema,
192+
resourceSchema: testFwSchema,
193+
expected: &fwserver.ApplyResourceChangeRequest{
194+
IdentitySchema: testIdentitySchema,
195+
PlannedIdentity: &tfsdk.ResourceIdentity{
196+
Raw: testIdentityProto5Value,
197+
Schema: testIdentitySchema,
198+
},
199+
ResourceSchema: testFwSchema,
200+
},
201+
},
140202
"plannedprivate-malformed-json": {
141203
input: &tfprotov5.ApplyResourceChangeRequest{
142204
PlannedPrivate: []byte(`{`),
@@ -253,7 +315,7 @@ func TestApplyResourceChangeRequest(t *testing.T) {
253315
t.Run(name, func(t *testing.T) {
254316
t.Parallel()
255317

256-
got, diags := fromproto5.ApplyResourceChangeRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema)
318+
got, diags := fromproto5.ApplyResourceChangeRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.identitySchema)
257319

258320
if diff := cmp.Diff(got, testCase.expected, cmp.AllowUnexported(privatestate.ProviderData{})); diff != "" {
259321
t.Errorf("unexpected difference: %s", diff)

internal/fwserver/server_applyresourcechange.go

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,23 @@ import (
1717
// ApplyResourceChangeRequest is the framework server request for the
1818
// ApplyResourceChange RPC.
1919
type ApplyResourceChangeRequest struct {
20-
Config *tfsdk.Config
21-
PlannedPrivate *privatestate.Data
22-
PlannedState *tfsdk.Plan
23-
PriorState *tfsdk.State
24-
ProviderMeta *tfsdk.Config
25-
ResourceSchema fwschema.Schema
26-
Resource resource.Resource
20+
Config *tfsdk.Config
21+
PlannedPrivate *privatestate.Data
22+
PlannedState *tfsdk.Plan
23+
PlannedIdentity *tfsdk.ResourceIdentity
24+
PriorState *tfsdk.State
25+
ProviderMeta *tfsdk.Config
26+
ResourceSchema fwschema.Schema
27+
IdentitySchema fwschema.Schema
28+
Resource resource.Resource
2729
}
2830

2931
// ApplyResourceChangeResponse is the framework server response for the
3032
// ApplyResourceChange RPC.
3133
type ApplyResourceChangeResponse struct {
3234
Diagnostics diag.Diagnostics
3335
NewState *tfsdk.State
36+
NewIdentity *tfsdk.ResourceIdentity
3437
Private *privatestate.Data
3538
}
3639

@@ -45,19 +48,22 @@ func (s *Server) ApplyResourceChange(ctx context.Context, req *ApplyResourceChan
4548
logging.FrameworkTrace(ctx, "ApplyResourceChange received no PriorState, running CreateResource")
4649

4750
createReq := &CreateResourceRequest{
48-
Config: req.Config,
49-
PlannedPrivate: req.PlannedPrivate,
50-
PlannedState: req.PlannedState,
51-
ProviderMeta: req.ProviderMeta,
52-
ResourceSchema: req.ResourceSchema,
53-
Resource: req.Resource,
51+
Config: req.Config,
52+
PlannedPrivate: req.PlannedPrivate,
53+
PlannedState: req.PlannedState,
54+
PlannedIdentity: req.PlannedIdentity,
55+
ProviderMeta: req.ProviderMeta,
56+
ResourceSchema: req.ResourceSchema,
57+
IdentitySchema: req.IdentitySchema,
58+
Resource: req.Resource,
5459
}
5560
createResp := &CreateResourceResponse{}
5661

5762
s.CreateResource(ctx, createReq, createResp)
5863

5964
resp.Diagnostics = createResp.Diagnostics
6065
resp.NewState = createResp.NewState
66+
resp.NewIdentity = createResp.NewIdentity
6167
resp.Private = createResp.Private
6268

6369
return
@@ -72,6 +78,7 @@ func (s *Server) ApplyResourceChange(ctx context.Context, req *ApplyResourceChan
7278
PriorState: req.PriorState,
7379
ProviderMeta: req.ProviderMeta,
7480
ResourceSchema: req.ResourceSchema,
81+
IdentitySchema: req.IdentitySchema,
7582
Resource: req.Resource,
7683
}
7784
deleteResp := &DeleteResourceResponse{}
@@ -80,6 +87,7 @@ func (s *Server) ApplyResourceChange(ctx context.Context, req *ApplyResourceChan
8087

8188
resp.Diagnostics = deleteResp.Diagnostics
8289
resp.NewState = deleteResp.NewState
90+
resp.NewIdentity = deleteResp.NewIdentity
8391
resp.Private = deleteResp.Private
8492

8593
return
@@ -89,19 +97,22 @@ func (s *Server) ApplyResourceChange(ctx context.Context, req *ApplyResourceChan
8997
logging.FrameworkTrace(ctx, "ApplyResourceChange running UpdateResource")
9098

9199
updateReq := &UpdateResourceRequest{
92-
Config: req.Config,
93-
PlannedPrivate: req.PlannedPrivate,
94-
PlannedState: req.PlannedState,
95-
PriorState: req.PriorState,
96-
ProviderMeta: req.ProviderMeta,
97-
ResourceSchema: req.ResourceSchema,
98-
Resource: req.Resource,
100+
Config: req.Config,
101+
PlannedPrivate: req.PlannedPrivate,
102+
PlannedState: req.PlannedState,
103+
PlannedIdentity: req.PlannedIdentity,
104+
PriorState: req.PriorState,
105+
ProviderMeta: req.ProviderMeta,
106+
ResourceSchema: req.ResourceSchema,
107+
IdentitySchema: req.IdentitySchema,
108+
Resource: req.Resource,
99109
}
100110
updateResp := &UpdateResourceResponse{}
101111

102112
s.UpdateResource(ctx, updateReq, updateResp)
103113

104114
resp.Diagnostics = updateResp.Diagnostics
105115
resp.NewState = updateResp.NewState
116+
resp.NewIdentity = updateResp.NewIdentity
106117
resp.Private = updateResp.Private
107118
}

0 commit comments

Comments
 (0)