Skip to content

Commit fc240f1

Browse files
authored
ResourceIdentity: Validate that identities do not change after Terraform stores it (#1137)
* add immutability validation to plan and apply * add private key to import and validation to read * add `MutableIdentity` resource behavior * fix immutability
1 parent e43fb0b commit fc240f1

27 files changed

+1039
-162
lines changed

internal/fromproto5/applyresourcechange.go

Lines changed: 5 additions & 4 deletions
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, identitySchema fwschema.Schema) (*fwserver.ApplyResourceChangeRequest, diag.Diagnostics) {
20+
func ApplyResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.ApplyResourceChangeRequest, resource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, resourceBehavior resource.ResourceBehavior, identitySchema fwschema.Schema) (*fwserver.ApplyResourceChangeRequest, diag.Diagnostics) {
2121
if proto5 == nil {
2222
return nil, nil
2323
}
@@ -39,9 +39,10 @@ func ApplyResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.ApplyReso
3939
}
4040

4141
fw := &fwserver.ApplyResourceChangeRequest{
42-
ResourceSchema: resourceSchema,
43-
IdentitySchema: identitySchema,
44-
Resource: resource,
42+
ResourceSchema: resourceSchema,
43+
ResourceBehavior: resourceBehavior,
44+
IdentitySchema: identitySchema,
45+
Resource: resource,
4546
}
4647

4748
config, configDiags := Config(ctx, proto5.Config, resourceSchema)

internal/fromproto5/applyresourcechange_test.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ func TestApplyResourceChangeRequest(t *testing.T) {
8383

8484
testCases := map[string]struct {
8585
input *tfprotov5.ApplyResourceChangeRequest
86+
resourceBehavior resource.ResourceBehavior
8687
resourceSchema fwschema.Schema
8788
resource resource.Resource
8889
providerMetaSchema fwschema.Schema
@@ -309,13 +310,26 @@ func TestApplyResourceChangeRequest(t *testing.T) {
309310
ResourceSchema: testFwSchema,
310311
},
311312
},
313+
"resource-behavior": {
314+
input: &tfprotov5.ApplyResourceChangeRequest{},
315+
resourceSchema: testFwSchema,
316+
resourceBehavior: resource.ResourceBehavior{
317+
MutableIdentity: true,
318+
},
319+
expected: &fwserver.ApplyResourceChangeRequest{
320+
ResourceBehavior: resource.ResourceBehavior{
321+
MutableIdentity: true,
322+
},
323+
ResourceSchema: testFwSchema,
324+
},
325+
},
312326
}
313327

314328
for name, testCase := range testCases {
315329
t.Run(name, func(t *testing.T) {
316330
t.Parallel()
317331

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

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

internal/fromproto5/readresource.go

Lines changed: 2 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, identitySchema fwschema.Schema) (*fwserver.ReadResourceRequest, diag.Diagnostics) {
20+
func ReadResourceRequest(ctx context.Context, proto5 *tfprotov5.ReadResourceRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, resourceBehavior resource.ResourceBehavior, 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+
ResourceBehavior: resourceBehavior,
2930
IdentitySchema: identitySchema,
3031
ClientCapabilities: ReadResourceClientCapabilities(proto5.ClientCapabilities),
3132
}

internal/fromproto5/readresource_test.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ func TestReadResourceRequest(t *testing.T) {
8383

8484
testCases := map[string]struct {
8585
input *tfprotov5.ReadResourceRequest
86+
resourceBehavior resource.ResourceBehavior
8687
resourceSchema fwschema.Schema
8788
identitySchema fwschema.Schema
8889
resource resource.Resource
@@ -251,13 +252,25 @@ func TestReadResourceRequest(t *testing.T) {
251252
},
252253
},
253254
},
255+
"resource-behavior": {
256+
input: &tfprotov5.ReadResourceRequest{},
257+
resourceSchema: testFwSchema,
258+
resourceBehavior: resource.ResourceBehavior{
259+
MutableIdentity: true,
260+
},
261+
expected: &fwserver.ReadResourceRequest{
262+
ResourceBehavior: resource.ResourceBehavior{
263+
MutableIdentity: true,
264+
},
265+
},
266+
},
254267
}
255268

256269
for name, testCase := range testCases {
257270
t.Run(name, func(t *testing.T) {
258271
t.Parallel()
259272

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

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

internal/fromproto6/applyresourcechange.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717

1818
// ApplyResourceChangeRequest returns the *fwserver.ApplyResourceChangeRequest
1919
// equivalent of a *tfprotov6.ApplyResourceChangeRequest.
20-
func ApplyResourceChangeRequest(ctx context.Context, proto6 *tfprotov6.ApplyResourceChangeRequest, resource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, identitySchema fwschema.Schema) (*fwserver.ApplyResourceChangeRequest, diag.Diagnostics) {
20+
func ApplyResourceChangeRequest(ctx context.Context, proto6 *tfprotov6.ApplyResourceChangeRequest, resource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, resourceBehavior resource.ResourceBehavior, identitySchema fwschema.Schema) (*fwserver.ApplyResourceChangeRequest, diag.Diagnostics) {
2121
if proto6 == nil {
2222
return nil, nil
2323
}
@@ -39,9 +39,10 @@ func ApplyResourceChangeRequest(ctx context.Context, proto6 *tfprotov6.ApplyReso
3939
}
4040

4141
fw := &fwserver.ApplyResourceChangeRequest{
42-
ResourceSchema: resourceSchema,
43-
IdentitySchema: identitySchema,
44-
Resource: resource,
42+
ResourceSchema: resourceSchema,
43+
ResourceBehavior: resourceBehavior,
44+
IdentitySchema: identitySchema,
45+
Resource: resource,
4546
}
4647

4748
config, configDiags := Config(ctx, proto6.Config, resourceSchema)

internal/fromproto6/applyresourcechange_test.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ func TestApplyResourceChangeRequest(t *testing.T) {
8383

8484
testCases := map[string]struct {
8585
input *tfprotov6.ApplyResourceChangeRequest
86+
resourceBehavior resource.ResourceBehavior
8687
resourceSchema fwschema.Schema
8788
resource resource.Resource
8889
providerMetaSchema fwschema.Schema
@@ -309,13 +310,26 @@ func TestApplyResourceChangeRequest(t *testing.T) {
309310
ResourceSchema: testFwSchema,
310311
},
311312
},
313+
"resource-behavior": {
314+
input: &tfprotov6.ApplyResourceChangeRequest{},
315+
resourceSchema: testFwSchema,
316+
resourceBehavior: resource.ResourceBehavior{
317+
MutableIdentity: true,
318+
},
319+
expected: &fwserver.ApplyResourceChangeRequest{
320+
ResourceBehavior: resource.ResourceBehavior{
321+
MutableIdentity: true,
322+
},
323+
ResourceSchema: testFwSchema,
324+
},
325+
},
312326
}
313327

314328
for name, testCase := range testCases {
315329
t.Run(name, func(t *testing.T) {
316330
t.Parallel()
317331

318-
got, diags := fromproto6.ApplyResourceChangeRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.identitySchema)
332+
got, diags := fromproto6.ApplyResourceChangeRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.resourceBehavior, testCase.identitySchema)
319333

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

internal/fromproto6/readresource.go

Lines changed: 2 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 *tfprotov6.ReadResourceRequest.
20-
func ReadResourceRequest(ctx context.Context, proto6 *tfprotov6.ReadResourceRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, identitySchema fwschema.Schema) (*fwserver.ReadResourceRequest, diag.Diagnostics) {
20+
func ReadResourceRequest(ctx context.Context, proto6 *tfprotov6.ReadResourceRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, resourceBehavior resource.ResourceBehavior, identitySchema fwschema.Schema) (*fwserver.ReadResourceRequest, diag.Diagnostics) {
2121
if proto6 == nil {
2222
return nil, nil
2323
}
@@ -26,6 +26,7 @@ func ReadResourceRequest(ctx context.Context, proto6 *tfprotov6.ReadResourceRequ
2626

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

internal/fromproto6/readresource_test.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ func TestReadResourceRequest(t *testing.T) {
8383

8484
testCases := map[string]struct {
8585
input *tfprotov6.ReadResourceRequest
86+
resourceBehavior resource.ResourceBehavior
8687
resourceSchema fwschema.Schema
8788
identitySchema fwschema.Schema
8889
resource resource.Resource
@@ -251,13 +252,25 @@ func TestReadResourceRequest(t *testing.T) {
251252
},
252253
},
253254
},
255+
"resource-behavior": {
256+
input: &tfprotov6.ReadResourceRequest{},
257+
resourceSchema: testFwSchema,
258+
resourceBehavior: resource.ResourceBehavior{
259+
MutableIdentity: true,
260+
},
261+
expected: &fwserver.ReadResourceRequest{
262+
ResourceBehavior: resource.ResourceBehavior{
263+
MutableIdentity: true,
264+
},
265+
},
266+
},
254267
}
255268

256269
for name, testCase := range testCases {
257270
t.Run(name, func(t *testing.T) {
258271
t.Parallel()
259272

260-
got, diags := fromproto6.ReadResourceRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.identitySchema)
273+
got, diags := fromproto6.ReadResourceRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.resourceBehavior, testCase.identitySchema)
261274

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

internal/fwserver/server_applyresourcechange.go

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,16 @@ 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-
PlannedIdentity *tfsdk.ResourceIdentity
24-
PriorState *tfsdk.State
25-
ProviderMeta *tfsdk.Config
26-
ResourceSchema fwschema.Schema
27-
IdentitySchema fwschema.Schema
28-
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
29+
ResourceBehavior resource.ResourceBehavior
2930
}
3031

3132
// ApplyResourceChangeResponse is the framework server response for the
@@ -101,15 +102,16 @@ func (s *Server) ApplyResourceChange(ctx context.Context, req *ApplyResourceChan
101102
logging.FrameworkTrace(ctx, "ApplyResourceChange running UpdateResource")
102103

103104
updateReq := &UpdateResourceRequest{
104-
Config: req.Config,
105-
PlannedPrivate: req.PlannedPrivate,
106-
PlannedState: req.PlannedState,
107-
PlannedIdentity: req.PlannedIdentity,
108-
PriorState: req.PriorState,
109-
ProviderMeta: req.ProviderMeta,
110-
ResourceSchema: req.ResourceSchema,
111-
IdentitySchema: req.IdentitySchema,
112-
Resource: req.Resource,
105+
Config: req.Config,
106+
PlannedPrivate: req.PlannedPrivate,
107+
PlannedState: req.PlannedState,
108+
PlannedIdentity: req.PlannedIdentity,
109+
PriorState: req.PriorState,
110+
ProviderMeta: req.ProviderMeta,
111+
ResourceSchema: req.ResourceSchema,
112+
IdentitySchema: req.IdentitySchema,
113+
Resource: req.Resource,
114+
ResourceBehavior: req.ResourceBehavior,
113115
}
114116
updateResp := &UpdateResourceResponse{}
115117

0 commit comments

Comments
 (0)