Skip to content

Commit a31ec4a

Browse files
authored
Resource Identity: Add identity data to RPCs needed to store/read from state (#1112)
* new attribute additions (breaks build for existing internal schema attributes) * update internal testing/testschema * implement in datasource/schema * implement in ephemeral/schema * implement in provider/metaschema * implement in provider/schema * implement in resource/schema * add to final internal/testing/testschema * get resource identity schema implementation * protov6 + tests * fix up package docs + build specific TODO comments * spellcheck! * add tfsdk object * ReadResource RPC implementation for v5 * ReadResource RPC implementation for v6 * protov5 + fwserver implementation for PlanResourceChange * protov6 implementation of PlanResourceChange * implement ApplyResourceChange fwserver + protov5 * protov6 implementation of apply resource * update go dep
1 parent 8852480 commit a31ec4a

File tree

66 files changed

+4746
-54
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+4746
-54
lines changed

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/fromproto5/planresourcechange.go

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

1818
// PlanResourceChangeRequest returns the *fwserver.PlanResourceChangeRequest
1919
// equivalent of a *tfprotov5.PlanResourceChangeRequest.
20-
func PlanResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.PlanResourceChangeRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, resourceBehavior resource.ResourceBehavior) (*fwserver.PlanResourceChangeRequest, diag.Diagnostics) {
20+
func PlanResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.PlanResourceChangeRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, resourceBehavior resource.ResourceBehavior, identitySchema fwschema.Schema) (*fwserver.PlanResourceChangeRequest, diag.Diagnostics) {
2121
if proto5 == nil {
2222
return nil, nil
2323
}
@@ -41,6 +41,7 @@ func PlanResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.PlanResour
4141
fw := &fwserver.PlanResourceChangeRequest{
4242
ResourceBehavior: resourceBehavior,
4343
ResourceSchema: resourceSchema,
44+
IdentitySchema: identitySchema,
4445
Resource: reqResource,
4546
ClientCapabilities: ModifyPlanClientCapabilities(proto5.ClientCapabilities),
4647
}
@@ -57,6 +58,12 @@ func PlanResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.PlanResour
5758

5859
fw.PriorState = priorState
5960

61+
priorIdentity, priorIdentityDiags := ResourceIdentity(ctx, proto5.PriorIdentity, identitySchema)
62+
63+
diags.Append(priorIdentityDiags...)
64+
65+
fw.PriorIdentity = priorIdentity
66+
6067
proposedNewState, proposedNewStateDiags := Plan(ctx, proto5.ProposedNewState, resourceSchema)
6168

6269
diags.Append(proposedNewStateDiags...)

internal/fromproto5/planresourcechange_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 TestPlanResourceChangeRequest(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
})
@@ -58,6 +83,7 @@ func TestPlanResourceChangeRequest(t *testing.T) {
5883
input *tfprotov5.PlanResourceChangeRequest
5984
resourceBehavior resource.ResourceBehavior
6085
resourceSchema fwschema.Schema
86+
identitySchema fwschema.Schema
6187
resource resource.Resource
6288
providerMetaSchema fwschema.Schema
6389
expected *fwserver.PlanResourceChangeRequest
@@ -182,6 +208,42 @@ func TestPlanResourceChangeRequest(t *testing.T) {
182208
ResourceSchema: testFwSchema,
183209
},
184210
},
211+
"prioridentity-missing-schema": {
212+
input: &tfprotov5.PlanResourceChangeRequest{
213+
PriorIdentity: &tfprotov5.ResourceIdentityData{
214+
IdentityData: &testIdentityProto5DynamicValue,
215+
},
216+
},
217+
resourceSchema: testFwSchema,
218+
expected: &fwserver.PlanResourceChangeRequest{
219+
ResourceSchema: testFwSchema,
220+
},
221+
expectedDiagnostics: diag.Diagnostics{
222+
diag.NewErrorDiagnostic(
223+
"Unable to Convert Resource Identity",
224+
"An unexpected error was encountered when converting the resource identity from the protocol type. "+
225+
"Identity data was sent in the protocol to a resource that doesn't support identity.\n\n"+
226+
"This is always a problem with Terraform or terraform-plugin-framework. Please report this to the provider developer.",
227+
),
228+
},
229+
},
230+
"prioridentity": {
231+
input: &tfprotov5.PlanResourceChangeRequest{
232+
PriorIdentity: &tfprotov5.ResourceIdentityData{
233+
IdentityData: &testIdentityProto5DynamicValue,
234+
},
235+
},
236+
identitySchema: testIdentitySchema,
237+
resourceSchema: testFwSchema,
238+
expected: &fwserver.PlanResourceChangeRequest{
239+
IdentitySchema: testIdentitySchema,
240+
PriorIdentity: &tfsdk.ResourceIdentity{
241+
Raw: testIdentityProto5Value,
242+
Schema: testIdentitySchema,
243+
},
244+
ResourceSchema: testFwSchema,
245+
},
246+
},
185247
"providermeta-missing-data": {
186248
input: &tfprotov5.PlanResourceChangeRequest{},
187249
resourceSchema: testFwSchema,
@@ -265,7 +327,7 @@ func TestPlanResourceChangeRequest(t *testing.T) {
265327
t.Run(name, func(t *testing.T) {
266328
t.Parallel()
267329

268-
got, diags := fromproto5.PlanResourceChangeRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.resourceBehavior)
330+
got, diags := fromproto5.PlanResourceChangeRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.resourceBehavior, testCase.identitySchema)
269331

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

internal/fromproto5/readresource.go

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

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

2727
fw := &fwserver.ReadResourceRequest{
2828
Resource: reqResource,
29+
IdentitySchema: identitySchema,
2930
ClientCapabilities: ReadResourceClientCapabilities(proto5.ClientCapabilities),
3031
}
3132

@@ -35,6 +36,12 @@ func ReadResourceRequest(ctx context.Context, proto5 *tfprotov5.ReadResourceRequ
3536

3637
fw.CurrentState = currentState
3738

39+
currentIdentity, currentIdentityDiags := ResourceIdentity(ctx, proto5.CurrentIdentity, identitySchema)
40+
41+
diags.Append(currentIdentityDiags...)
42+
43+
fw.CurrentIdentity = currentIdentity
44+
3845
providerMeta, providerMetaDiags := ProviderMeta(ctx, proto5.ProviderMeta, providerMetaSchema)
3946

4047
diags.Append(providerMetaDiags...)

internal/fromproto5/readresource_test.go

Lines changed: 58 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 TestReadResourceRequest(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
})
@@ -59,6 +84,7 @@ func TestReadResourceRequest(t *testing.T) {
5984
testCases := map[string]struct {
6085
input *tfprotov5.ReadResourceRequest
6186
resourceSchema fwschema.Schema
87+
identitySchema fwschema.Schema
6288
resource resource.Resource
6389
providerMetaSchema fwschema.Schema
6490
expected *fwserver.ReadResourceRequest
@@ -99,6 +125,37 @@ func TestReadResourceRequest(t *testing.T) {
99125
},
100126
},
101127
},
128+
"currentidentity-missing-schema": {
129+
input: &tfprotov5.ReadResourceRequest{
130+
CurrentIdentity: &tfprotov5.ResourceIdentityData{
131+
IdentityData: &testIdentityProto5DynamicValue,
132+
},
133+
},
134+
expected: &fwserver.ReadResourceRequest{},
135+
expectedDiagnostics: diag.Diagnostics{
136+
diag.NewErrorDiagnostic(
137+
"Unable to Convert Resource Identity",
138+
"An unexpected error was encountered when converting the resource identity from the protocol type. "+
139+
"Identity data was sent in the protocol to a resource that doesn't support identity.\n\n"+
140+
"This is always a problem with Terraform or terraform-plugin-framework. Please report this to the provider developer.",
141+
),
142+
},
143+
},
144+
"currentidentity": {
145+
input: &tfprotov5.ReadResourceRequest{
146+
CurrentIdentity: &tfprotov5.ResourceIdentityData{
147+
IdentityData: &testIdentityProto5DynamicValue,
148+
},
149+
},
150+
identitySchema: testIdentitySchema,
151+
expected: &fwserver.ReadResourceRequest{
152+
IdentitySchema: testIdentitySchema,
153+
CurrentIdentity: &tfsdk.ResourceIdentity{
154+
Raw: testIdentityProto5Value,
155+
Schema: testIdentitySchema,
156+
},
157+
},
158+
},
102159
"private-malformed-json": {
103160
input: &tfprotov5.ReadResourceRequest{
104161
Private: []byte(`{`),
@@ -200,7 +257,7 @@ func TestReadResourceRequest(t *testing.T) {
200257
t.Run(name, func(t *testing.T) {
201258
t.Parallel()
202259

203-
got, diags := fromproto5.ReadResourceRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema)
260+
got, diags := fromproto5.ReadResourceRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.identitySchema)
204261

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

0 commit comments

Comments
 (0)