From df3f0cf2b664ac40b94515bfdf8c05d0fc19967c Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Wed, 1 May 2024 15:35:27 -0400 Subject: [PATCH 01/25] Implement manual deferred action support for `resource.importResourceState` --- go.mod | 2 +- go.sum | 6 +- internal/fromproto5/importresourcestate.go | 14 ++-- .../fromproto5/importresourcestate_test.go | 21 +++++- internal/fromproto6/importresourcestate.go | 14 ++-- .../fromproto6/importresourcestate_test.go | 21 +++++- .../fwserver/server_importresourcestate.go | 21 +++++- .../server_importresourcestate_test.go | 75 +++++++++++++++++++ internal/toproto5/importresourcestate.go | 7 +- internal/toproto6/importresourcestate.go | 7 +- resource/deferral.go | 28 +++++++ resource/import_state.go | 17 +++++ 12 files changed, 213 insertions(+), 20 deletions(-) create mode 100644 resource/deferral.go diff --git a/go.mod b/go.mod index aa5120093..377c56177 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.21.6 require ( github.com/google/go-cmp v0.6.0 - github.com/hashicorp/terraform-plugin-go v0.22.2 + github.com/hashicorp/terraform-plugin-go v0.22.3-0.20240424182422-dba624001d4f github.com/hashicorp/terraform-plugin-log v0.9.0 ) diff --git a/go.sum b/go.sum index 3503fab49..7380e78b6 100644 --- a/go.sum +++ b/go.sum @@ -15,8 +15,10 @@ github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDm github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/terraform-plugin-go v0.22.2 h1:5o8uveu6eZUf5J7xGPV0eY0TPXg3qpmwX9sce03Bxnc= -github.com/hashicorp/terraform-plugin-go v0.22.2/go.mod h1:drq8Snexp9HsbFZddvyLHN6LuWHHndSQg+gV+FPkcIM= +github.com/hashicorp/terraform-plugin-go v0.22.3-0.20240419152848-9a1607db1cab h1:Q86RQOyr+03Z+MbEi1hhlDADMwc0k24XYqASQAYPE0A= +github.com/hashicorp/terraform-plugin-go v0.22.3-0.20240419152848-9a1607db1cab/go.mod h1:drq8Snexp9HsbFZddvyLHN6LuWHHndSQg+gV+FPkcIM= +github.com/hashicorp/terraform-plugin-go v0.22.3-0.20240424182422-dba624001d4f h1:r6EtFgZbSf+VeNrWqFwTFl5mKBdlFl+bpufwgaNBeQA= +github.com/hashicorp/terraform-plugin-go v0.22.3-0.20240424182422-dba624001d4f/go.mod h1:DbW1zoh21fsPD35whjnDA5aR7bXQV+k+lnkZrn8ZrFM= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= diff --git a/internal/fromproto5/importresourcestate.go b/internal/fromproto5/importresourcestate.go index 4a10174b1..7cb5871c4 100644 --- a/internal/fromproto5/importresourcestate.go +++ b/internal/fromproto5/importresourcestate.go @@ -6,18 +6,19 @@ package fromproto5 import ( "context" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - "github.com/hashicorp/terraform-plugin-go/tfprotov5" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // ImportResourceStateRequest returns the *fwserver.ImportResourceStateRequest // equivalent of a *tfprotov5.ImportResourceStateRequest. -func ImportResourceStateRequest(ctx context.Context, proto5 *tfprotov5.ImportResourceStateRequest, resource resource.Resource, resourceSchema fwschema.Schema) (*fwserver.ImportResourceStateRequest, diag.Diagnostics) { +func ImportResourceStateRequest(ctx context.Context, proto5 *tfprotov5.ImportResourceStateRequest, reqResource resource.Resource, resourceSchema fwschema.Schema) (*fwserver.ImportResourceStateRequest, diag.Diagnostics) { if proto5 == nil { return nil, nil } @@ -43,8 +44,11 @@ func ImportResourceStateRequest(ctx context.Context, proto5 *tfprotov5.ImportRes Raw: tftypes.NewValue(resourceSchema.Type().TerraformType(ctx), nil), Schema: resourceSchema, }, - ID: proto5.ID, - Resource: resource, + ID: proto5.ID, + ClientCapabilities: &resource.ImportStateClientCapabilities{ + DeferralAllowed: proto5.ClientCapabilities.DeferralAllowed, + }, + Resource: reqResource, TypeName: proto5.TypeName, } diff --git a/internal/fromproto5/importresourcestate_test.go b/internal/fromproto5/importresourcestate_test.go index 23a50989b..00013ced3 100644 --- a/internal/fromproto5/importresourcestate_test.go +++ b/internal/fromproto5/importresourcestate_test.go @@ -8,6 +8,9 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fromproto5" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -15,8 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - "github.com/hashicorp/terraform-plugin-go/tfprotov5" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestImportResourceStateRequest(t *testing.T) { @@ -86,6 +87,22 @@ func TestImportResourceStateRequest(t *testing.T) { TypeName: "test_resource", }, }, + "client-capabilities": { + input: &tfprotov5.ImportResourceStateRequest{ + ID: "test-id", + ClientCapabilities: &tfprotov5.ClientCapabilities{ + DeferralAllowed: true, + }, + }, + resourceSchema: testFwSchema, + expected: &fwserver.ImportResourceStateRequest{ + EmptyState: testFwEmptyState, + ID: "test-id", + ClientCapabilities: &resource.ImportStateClientCapabilities{ + DeferralAllowed: true, + }, + }, + }, } for name, testCase := range testCases { diff --git a/internal/fromproto6/importresourcestate.go b/internal/fromproto6/importresourcestate.go index 11018c41b..30158d0c1 100644 --- a/internal/fromproto6/importresourcestate.go +++ b/internal/fromproto6/importresourcestate.go @@ -6,18 +6,19 @@ package fromproto6 import ( "context" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - "github.com/hashicorp/terraform-plugin-go/tfprotov6" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // ImportResourceStateRequest returns the *fwserver.ImportResourceStateRequest // equivalent of a *tfprotov6.ImportResourceStateRequest. -func ImportResourceStateRequest(ctx context.Context, proto6 *tfprotov6.ImportResourceStateRequest, resource resource.Resource, resourceSchema fwschema.Schema) (*fwserver.ImportResourceStateRequest, diag.Diagnostics) { +func ImportResourceStateRequest(ctx context.Context, proto6 *tfprotov6.ImportResourceStateRequest, reqResource resource.Resource, resourceSchema fwschema.Schema) (*fwserver.ImportResourceStateRequest, diag.Diagnostics) { if proto6 == nil { return nil, nil } @@ -43,8 +44,11 @@ func ImportResourceStateRequest(ctx context.Context, proto6 *tfprotov6.ImportRes Raw: tftypes.NewValue(resourceSchema.Type().TerraformType(ctx), nil), Schema: resourceSchema, }, - ID: proto6.ID, - Resource: resource, + ID: proto6.ID, + ClientCapabilities: &resource.ImportStateClientCapabilities{ + DeferralAllowed: proto6.ClientCapabilities.DeferralAllowed, + }, + Resource: reqResource, TypeName: proto6.TypeName, } diff --git a/internal/fromproto6/importresourcestate_test.go b/internal/fromproto6/importresourcestate_test.go index e582263f8..6ef29c77e 100644 --- a/internal/fromproto6/importresourcestate_test.go +++ b/internal/fromproto6/importresourcestate_test.go @@ -8,6 +8,9 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fromproto6" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -15,8 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - "github.com/hashicorp/terraform-plugin-go/tfprotov6" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestImportResourceStateRequest(t *testing.T) { @@ -86,6 +87,22 @@ func TestImportResourceStateRequest(t *testing.T) { TypeName: "test_resource", }, }, + "client-capabilities": { + input: &tfprotov6.ImportResourceStateRequest{ + ID: "test-id", + ClientCapabilities: &tfprotov6.ClientCapabilities{ + DeferralAllowed: true, + }, + }, + resourceSchema: testFwSchema, + expected: &fwserver.ImportResourceStateRequest{ + EmptyState: testFwEmptyState, + ID: "test-id", + ClientCapabilities: &resource.ImportStateClientCapabilities{ + DeferralAllowed: true, + }, + }, + }, } for name, testCase := range testCases { diff --git a/internal/fwserver/server_importresourcestate.go b/internal/fwserver/server_importresourcestate.go index d89cee357..65b1a0761 100644 --- a/internal/fwserver/server_importresourcestate.go +++ b/internal/fwserver/server_importresourcestate.go @@ -35,6 +35,9 @@ type ImportResourceStateRequest struct { // TypeName is the resource type name, which is necessary for populating // the ImportedResource TypeName of the ImportResourceStateResponse. TypeName string + + //TODO: doc + ClientCapabilities *resource.ImportStateClientCapabilities } // ImportResourceStateResponse is the framework server response for the @@ -42,6 +45,7 @@ type ImportResourceStateRequest struct { type ImportResourceStateResponse struct { Diagnostics diag.Diagnostics ImportedResources []ImportedResource + Deferral *resource.DeferralResponse } // ImportResourceState implements the framework server ImportResourceState RPC. @@ -90,7 +94,8 @@ func (s *Server) ImportResourceState(ctx context.Context, req *ImportResourceSta } importReq := resource.ImportStateRequest{ - ID: req.ID, + ID: req.ID, + ClientCapabilities: req.ClientCapabilities, } privateProviderData := privatestate.EmptyProviderData(ctx) @@ -113,6 +118,16 @@ func (s *Server) ImportResourceState(ctx context.Context, req *ImportResourceSta return } + if (importReq.ClientCapabilities == nil || !importReq.ClientCapabilities.DeferralAllowed) && importResp.DeferralResponse != nil { + resp.Diagnostics.AddError( + "Resource Import Deferral Not Allowed", + "An unexpected error was encountered when importing the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ + "The resource requested a deferral but the Terraform client does not support deferrals, "+ + "resource.DeferralResponse can only be set if resource.ImportStateRequest.ImportStateClientCapabilities.DeferralAllowed is true.", + ) + return + } + if importResp.State.Raw.Equal(req.EmptyState.Raw) { resp.Diagnostics.AddError( "Missing Resource Import State", @@ -128,6 +143,10 @@ func (s *Server) ImportResourceState(ctx context.Context, req *ImportResourceSta private.Provider = importResp.Private } + if importResp.DeferralResponse != nil { + resp.Deferral = importResp.DeferralResponse + } + resp.ImportedResources = []ImportedResource{ { State: importResp.State, diff --git a/internal/fwserver/server_importresourcestate_test.go b/internal/fwserver/server_importresourcestate_test.go index c9f00e579..bffba4e59 100644 --- a/internal/fwserver/server_importresourcestate_test.go +++ b/internal/fwserver/server_importresourcestate_test.go @@ -84,6 +84,10 @@ func TestServerImportResourceState(t *testing.T) { Provider: testEmptyProviderData, } + testDeferral := &resource.ImportStateClientCapabilities{ + DeferralAllowed: true, + } + testCases := map[string]struct { server *fwserver.Server request *fwserver.ImportResourceStateRequest @@ -299,6 +303,77 @@ func TestServerImportResourceState(t *testing.T) { }, }, }, + "request-deferral-allowed-response-deferral": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ImportResourceStateRequest{ + EmptyState: *testEmptyState, + ID: "test-id", + Resource: &testprovider.ResourceWithImportState{ + Resource: &testprovider.Resource{}, + ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + if req.ID != "test-id" { + resp.Diagnostics.AddError("unexpected req.ID value: %s", req.ID) + } + + resp.DeferralResponse = &resource.DeferralResponse{ + Reason: resource.DeferralReasonAbsentPrereq, + } + + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + + }, + }, + TypeName: "test_resource", + ClientCapabilities: testDeferral, + }, + expectedResponse: &fwserver.ImportResourceStateResponse{ + ImportedResources: []fwserver.ImportedResource{ + { + State: *testState, + TypeName: "test_resource", + Private: testEmptyPrivate, + }, + }, + Deferral: &resource.DeferralResponse{Reason: resource.DeferralReasonAbsentPrereq}, + }, + }, + "request-deferral-not-allowed-response-deferral": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ImportResourceStateRequest{ + EmptyState: *testEmptyState, + ID: "test-id", + Resource: &testprovider.ResourceWithImportState{ + Resource: &testprovider.Resource{}, + ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + if req.ID != "test-id" { + resp.Diagnostics.AddError("unexpected req.ID value: %s", req.ID) + } + + resp.DeferralResponse = &resource.DeferralResponse{ + Reason: resource.DeferralReasonAbsentPrereq, + } + + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + + }, + }, + TypeName: "test_resource", + }, + expectedResponse: &fwserver.ImportResourceStateResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Resource Import Deferral Not Allowed", + "An unexpected error was encountered when importing the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ + "The resource requested a deferral but the Terraform client does not support deferrals, "+ + "resource.DeferralResponse can only be set if resource.ImportStateRequest.ImportStateClientCapabilities.DeferralAllowed is true.", + ), + }, + }, + }, } for name, testCase := range testCases { diff --git a/internal/toproto5/importresourcestate.go b/internal/toproto5/importresourcestate.go index 654b84b63..606b5de55 100644 --- a/internal/toproto5/importresourcestate.go +++ b/internal/toproto5/importresourcestate.go @@ -6,8 +6,9 @@ package toproto5 import ( "context" - "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-go/tfprotov5" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" ) // ImportResourceStateResponse returns the *tfprotov5.ImportResourceStateResponse @@ -33,5 +34,9 @@ func ImportResourceStateResponse(ctx context.Context, fw *fwserver.ImportResourc proto5.ImportedResources = append(proto5.ImportedResources, proto5ImportedResource) } + if fw.Deferral != nil { + proto5.Deferred.Reason = tfprotov5.DeferredReason(fw.Deferral.Reason) + } + return proto5 } diff --git a/internal/toproto6/importresourcestate.go b/internal/toproto6/importresourcestate.go index a5a29a699..f5ecd4862 100644 --- a/internal/toproto6/importresourcestate.go +++ b/internal/toproto6/importresourcestate.go @@ -6,8 +6,9 @@ package toproto6 import ( "context" - "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" ) // ImportResourceStateResponse returns the *tfprotov6.ImportResourceStateResponse @@ -33,5 +34,9 @@ func ImportResourceStateResponse(ctx context.Context, fw *fwserver.ImportResourc proto6.ImportedResources = append(proto6.ImportedResources, proto6ImportedResource) } + if fw.Deferral != nil { + proto6.Deferred.Reason = tfprotov6.DeferredReason(fw.Deferral.Reason) + } + return proto6 } diff --git a/resource/deferral.go b/resource/deferral.go new file mode 100644 index 000000000..1ce045222 --- /dev/null +++ b/resource/deferral.go @@ -0,0 +1,28 @@ +package resource + +const ( + DeferralReasonUnknown DeferralReason = 0 + DeferralReasonResourceConfigUnknown DeferralReason = 1 + DeferralReasonProviderConfigUnknown DeferralReason = 2 + DeferralReasonAbsentPrereq DeferralReason = 3 +) + +type DeferralResponse struct { + Reason DeferralReason +} + +type DeferralReason int32 + +func (d DeferralReason) String() string { + switch d { + case 0: + return "Unknown" + case 1: + return "Resource Config Unknown" + case 2: + return "Provider Config Unknown" + case 3: + return "Absent Prerequisite" + } + return "Unknown" +} diff --git a/resource/import_state.go b/resource/import_state.go index c08255741..7e9c0669a 100644 --- a/resource/import_state.go +++ b/resource/import_state.go @@ -12,6 +12,13 @@ import ( "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) +// TODO: doc +type ImportStateClientCapabilities struct { + // DeferralAllowed indicates whether the Terraform client initiating + // the request allows a deferral response. + DeferralAllowed bool +} + // ImportStateRequest represents a request for the provider to import a // resource. An instance of this request struct is supplied as an argument to // the Resource's ImportState method. @@ -23,6 +30,9 @@ type ImportStateRequest struct { // its own type of value and parsed during import. This value // is not stored in the state unless the provider explicitly stores it. ID string + + //TODO: doc + ClientCapabilities *ImportStateClientCapabilities } // ImportStateResponse represents a response to a ImportStateRequest. @@ -44,6 +54,13 @@ type ImportStateResponse struct { // This field is not pre-populated as there is no pre-existing private state // data during the resource's Import operation. Private *privatestate.ProviderData + + // DeferralResponse indicates that Terraform should defer + // importing this resource. + // + // This field can only be set if + // `(resource.ImportStateRequest.ImportStateClientCapabilities).DeferralAllowed` is true. + DeferralResponse *DeferralResponse } // ImportStatePassthroughID is a helper function to set the import From 0a270766a78331d0a47ec75da5498d2ed91b52a4 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Fri, 3 May 2024 17:04:26 -0400 Subject: [PATCH 02/25] Implement manual deferred action support for `resource.readResource` --- internal/fromproto5/importresourcestate.go | 11 +-- internal/fromproto5/readresource.go | 10 ++- internal/fromproto5/readresource_test.go | 13 ++++ internal/fromproto6/importresourcestate.go | 11 +-- internal/fromproto6/readresource.go | 10 ++- internal/fromproto6/readresource_test.go | 13 ++++ internal/fwserver/server_readresource.go | 23 ++++-- internal/fwserver/server_readresource_test.go | 71 +++++++++++++++++++ internal/toproto5/importresourcestate.go | 4 +- internal/toproto5/readresource.go | 6 ++ internal/toproto5/readresource_test.go | 17 +++++ internal/toproto6/importresourcestate.go | 4 +- internal/toproto6/readresource.go | 6 ++ internal/toproto6/readresource_test.go | 17 +++++ resource/read.go | 17 +++++ 15 files changed, 215 insertions(+), 18 deletions(-) diff --git a/internal/fromproto5/importresourcestate.go b/internal/fromproto5/importresourcestate.go index 7cb5871c4..d074f0c9e 100644 --- a/internal/fromproto5/importresourcestate.go +++ b/internal/fromproto5/importresourcestate.go @@ -44,13 +44,16 @@ func ImportResourceStateRequest(ctx context.Context, proto5 *tfprotov5.ImportRes Raw: tftypes.NewValue(resourceSchema.Type().TerraformType(ctx), nil), Schema: resourceSchema, }, - ID: proto5.ID, - ClientCapabilities: &resource.ImportStateClientCapabilities{ - DeferralAllowed: proto5.ClientCapabilities.DeferralAllowed, - }, + ID: proto5.ID, Resource: reqResource, TypeName: proto5.TypeName, } + if proto5.ClientCapabilities != nil { + fw.ClientCapabilities = &resource.ImportStateClientCapabilities{ + DeferralAllowed: proto5.ClientCapabilities.DeferralAllowed, + } + } + return fw, diags } diff --git a/internal/fromproto5/readresource.go b/internal/fromproto5/readresource.go index 94164a2c0..a3860f6df 100644 --- a/internal/fromproto5/readresource.go +++ b/internal/fromproto5/readresource.go @@ -17,7 +17,7 @@ import ( // ReadResourceRequest returns the *fwserver.ReadResourceRequest // equivalent of a *tfprotov5.ReadResourceRequest. -func ReadResourceRequest(ctx context.Context, proto5 *tfprotov5.ReadResourceRequest, resource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema) (*fwserver.ReadResourceRequest, diag.Diagnostics) { +func ReadResourceRequest(ctx context.Context, proto5 *tfprotov5.ReadResourceRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema) (*fwserver.ReadResourceRequest, diag.Diagnostics) { if proto5 == nil { return nil, nil } @@ -25,7 +25,7 @@ func ReadResourceRequest(ctx context.Context, proto5 *tfprotov5.ReadResourceRequ var diags diag.Diagnostics fw := &fwserver.ReadResourceRequest{ - Resource: resource, + Resource: reqResource, } currentState, currentStateDiags := State(ctx, proto5.CurrentState, resourceSchema) @@ -46,5 +46,11 @@ func ReadResourceRequest(ctx context.Context, proto5 *tfprotov5.ReadResourceRequ fw.Private = privateData + if proto5.ClientCapabilities != nil { + fw.ClientCapabilities = &resource.ReadClientCapabilities{ + DeferralAllowed: proto5.ClientCapabilities.DeferralAllowed, + } + } + return fw, diags } diff --git a/internal/fromproto5/readresource_test.go b/internal/fromproto5/readresource_test.go index 549417515..7a0c9534b 100644 --- a/internal/fromproto5/readresource_test.go +++ b/internal/fromproto5/readresource_test.go @@ -56,6 +56,8 @@ func TestReadResourceRequest(t *testing.T) { testEmptyProviderData := privatestate.EmptyProviderData(context.Background()) + testClientCapabilities := tfprotov5.ClientCapabilities{DeferralAllowed: true} + testCases := map[string]struct { input *tfprotov5.ReadResourceRequest resourceSchema fwschema.Schema @@ -172,6 +174,17 @@ func TestReadResourceRequest(t *testing.T) { }, }, }, + "client-capabilities": { + input: &tfprotov5.ReadResourceRequest{ + ClientCapabilities: &testClientCapabilities, + }, + resourceSchema: testFwSchema, + expected: &fwserver.ReadResourceRequest{ + ClientCapabilities: &resource.ReadClientCapabilities{ + DeferralAllowed: true, + }, + }, + }, } for name, testCase := range testCases { diff --git a/internal/fromproto6/importresourcestate.go b/internal/fromproto6/importresourcestate.go index 30158d0c1..431fadd4c 100644 --- a/internal/fromproto6/importresourcestate.go +++ b/internal/fromproto6/importresourcestate.go @@ -44,13 +44,16 @@ func ImportResourceStateRequest(ctx context.Context, proto6 *tfprotov6.ImportRes Raw: tftypes.NewValue(resourceSchema.Type().TerraformType(ctx), nil), Schema: resourceSchema, }, - ID: proto6.ID, - ClientCapabilities: &resource.ImportStateClientCapabilities{ - DeferralAllowed: proto6.ClientCapabilities.DeferralAllowed, - }, + ID: proto6.ID, Resource: reqResource, TypeName: proto6.TypeName, } + if proto6.ClientCapabilities != nil { + fw.ClientCapabilities = &resource.ImportStateClientCapabilities{ + DeferralAllowed: proto6.ClientCapabilities.DeferralAllowed, + } + } + return fw, diags } diff --git a/internal/fromproto6/readresource.go b/internal/fromproto6/readresource.go index 4e48e565b..2679b6612 100644 --- a/internal/fromproto6/readresource.go +++ b/internal/fromproto6/readresource.go @@ -17,7 +17,7 @@ import ( // ReadResourceRequest returns the *fwserver.ReadResourceRequest // equivalent of a *tfprotov6.ReadResourceRequest. -func ReadResourceRequest(ctx context.Context, proto6 *tfprotov6.ReadResourceRequest, resource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema) (*fwserver.ReadResourceRequest, diag.Diagnostics) { +func ReadResourceRequest(ctx context.Context, proto6 *tfprotov6.ReadResourceRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema) (*fwserver.ReadResourceRequest, diag.Diagnostics) { if proto6 == nil { return nil, nil } @@ -25,7 +25,7 @@ func ReadResourceRequest(ctx context.Context, proto6 *tfprotov6.ReadResourceRequ var diags diag.Diagnostics fw := &fwserver.ReadResourceRequest{ - Resource: resource, + Resource: reqResource, } currentState, currentStateDiags := State(ctx, proto6.CurrentState, resourceSchema) @@ -46,5 +46,11 @@ func ReadResourceRequest(ctx context.Context, proto6 *tfprotov6.ReadResourceRequ fw.Private = privateData + if proto6.ClientCapabilities != nil { + fw.ClientCapabilities = &resource.ReadClientCapabilities{ + DeferralAllowed: proto6.ClientCapabilities.DeferralAllowed, + } + } + return fw, diags } diff --git a/internal/fromproto6/readresource_test.go b/internal/fromproto6/readresource_test.go index 3c75a7b45..f3f61a017 100644 --- a/internal/fromproto6/readresource_test.go +++ b/internal/fromproto6/readresource_test.go @@ -56,6 +56,8 @@ func TestReadResourceRequest(t *testing.T) { testEmptyProviderData := privatestate.EmptyProviderData(context.Background()) + testClientCapabilities := tfprotov6.ClientCapabilities{DeferralAllowed: true} + testCases := map[string]struct { input *tfprotov6.ReadResourceRequest resourceSchema fwschema.Schema @@ -172,6 +174,17 @@ func TestReadResourceRequest(t *testing.T) { }, }, }, + "client-capabilities": { + input: &tfprotov6.ReadResourceRequest{ + ClientCapabilities: &testClientCapabilities, + }, + resourceSchema: testFwSchema, + expected: &fwserver.ReadResourceRequest{ + ClientCapabilities: &resource.ReadClientCapabilities{ + DeferralAllowed: true, + }, + }, + }, } for name, testCase := range testCases { diff --git a/internal/fwserver/server_readresource.go b/internal/fwserver/server_readresource.go index 172a48009..47e5554e4 100644 --- a/internal/fwserver/server_readresource.go +++ b/internal/fwserver/server_readresource.go @@ -17,10 +17,11 @@ import ( // ReadResourceRequest is the framework server request for the // ReadResource RPC. type ReadResourceRequest struct { - CurrentState *tfsdk.State - Resource resource.Resource - Private *privatestate.Data - ProviderMeta *tfsdk.Config + CurrentState *tfsdk.State + Resource resource.Resource + Private *privatestate.Data + ProviderMeta *tfsdk.Config + ClientCapabilities *resource.ReadClientCapabilities } // ReadResourceResponse is the framework server response for the @@ -29,6 +30,7 @@ type ReadResourceResponse struct { Diagnostics diag.Diagnostics NewState *tfsdk.State Private *privatestate.Data + Deferral *resource.DeferralResponse } // ReadResource implements the framework server ReadResource RPC. @@ -97,12 +99,15 @@ func (s *Server) ReadResource(ctx context.Context, req *ReadResourceRequest, res resp.Private = req.Private } + readReq.ClientCapabilities = req.ClientCapabilities + logging.FrameworkTrace(ctx, "Calling provider defined Resource Read") req.Resource.Read(ctx, readReq, &readResp) logging.FrameworkTrace(ctx, "Called provider defined Resource Read") resp.Diagnostics = readResp.Diagnostics resp.NewState = &readResp.State + resp.Deferral = readResp.DeferralResponse if readResp.Private != nil { if resp.Private == nil { @@ -116,6 +121,16 @@ func (s *Server) ReadResource(ctx context.Context, req *ReadResourceRequest, res return } + if (req.ClientCapabilities == nil || !req.ClientCapabilities.DeferralAllowed) && resp.Deferral != nil { + resp.Diagnostics.AddError( + "Resource Deferral Not Allowed", + "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ + "The resource requested a deferral but the Terraform client does not support deferrals, "+ + "resource.DeferralResponse can only be set if resource.ReadRequest.ReadClientCapabilities.DeferralAllowed is true.", + ) + return + } + semanticEqualityReq := SchemaSemanticEqualityRequest{ PriorData: fwschemadata.Data{ Description: fwschemadata.DataDescriptionState, diff --git a/internal/fwserver/server_readresource_test.go b/internal/fwserver/server_readresource_test.go index e248d2baf..f95f16511 100644 --- a/internal/fwserver/server_readresource_test.go +++ b/internal/fwserver/server_readresource_test.go @@ -135,6 +135,10 @@ func TestServerReadResource(t *testing.T) { Provider: testEmptyProviderData, } + testDeferralAllowed := &resource.ReadClientCapabilities{ + DeferralAllowed: true, + } + testCases := map[string]struct { server *fwserver.Server request *fwserver.ReadResourceRequest @@ -510,6 +514,73 @@ func TestServerReadResource(t *testing.T) { Private: testPrivate, }, }, + "request-deferral-allowed-response-deferral": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ReadResourceRequest{ + CurrentState: testCurrentState, + Resource: &testprovider.Resource{ + ReadMethod: func(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data struct { + TestComputed types.String `tfsdk:"test_computed"` + TestRequired types.String `tfsdk:"test_required"` + } + + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + resp.DeferralResponse = &resource.DeferralResponse{Reason: resource.DeferralReasonAbsentPrereq} + + if data.TestRequired.ValueString() != "test-currentstate-value" { + resp.Diagnostics.AddError("unexpected req.State value: %s", data.TestRequired.ValueString()) + } + }, + }, + ClientCapabilities: testDeferralAllowed, + }, + expectedResponse: &fwserver.ReadResourceResponse{ + NewState: testCurrentState, + Private: testEmptyPrivate, + Deferral: &resource.DeferralResponse{Reason: resource.DeferralReasonAbsentPrereq}, + }, + }, + "request-deferral-not-allowed-response-deferral": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ReadResourceRequest{ + CurrentState: testCurrentState, + Resource: &testprovider.Resource{ + ReadMethod: func(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data struct { + TestComputed types.String `tfsdk:"test_computed"` + TestRequired types.String `tfsdk:"test_required"` + } + + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + resp.DeferralResponse = &resource.DeferralResponse{Reason: resource.DeferralReasonAbsentPrereq} + + if data.TestRequired.ValueString() != "test-currentstate-value" { + resp.Diagnostics.AddError("unexpected req.State value: %s", data.TestRequired.ValueString()) + } + }, + }, + }, + expectedResponse: &fwserver.ReadResourceResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Resource Deferral Not Allowed", + "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ + "The resource requested a deferral but the Terraform client does not support deferrals, "+ + "resource.DeferralResponse can only be set if resource.ReadRequest.ReadClientCapabilities.DeferralAllowed is true.", + ), + }, + NewState: testCurrentState, + Private: testEmptyPrivate, + Deferral: &resource.DeferralResponse{Reason: resource.DeferralReasonAbsentPrereq}, + }, + }, } for name, testCase := range testCases { diff --git a/internal/toproto5/importresourcestate.go b/internal/toproto5/importresourcestate.go index 606b5de55..49131f02f 100644 --- a/internal/toproto5/importresourcestate.go +++ b/internal/toproto5/importresourcestate.go @@ -35,7 +35,9 @@ func ImportResourceStateResponse(ctx context.Context, fw *fwserver.ImportResourc } if fw.Deferral != nil { - proto5.Deferred.Reason = tfprotov5.DeferredReason(fw.Deferral.Reason) + proto5.Deferred = &tfprotov5.Deferred{ + Reason: tfprotov5.DeferredReason(fw.Deferral.Reason), + } } return proto5 diff --git a/internal/toproto5/readresource.go b/internal/toproto5/readresource.go index 43401ab09..d60d2eab1 100644 --- a/internal/toproto5/readresource.go +++ b/internal/toproto5/readresource.go @@ -32,5 +32,11 @@ func ReadResourceResponse(ctx context.Context, fw *fwserver.ReadResourceResponse proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) proto5.Private = newPrivate + if fw.Deferral != nil { + proto5.Deferred = &tfprotov5.Deferred{ + Reason: tfprotov5.DeferredReason(fw.Deferral.Reason), + } + } + return proto5 } diff --git a/internal/toproto5/readresource_test.go b/internal/toproto5/readresource_test.go index 4967f9888..9e66c7065 100644 --- a/internal/toproto5/readresource_test.go +++ b/internal/toproto5/readresource_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/internal/toproto5" + "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -68,6 +69,14 @@ func TestReadResourceResponse(t *testing.T) { }, } + testDeferral := &resource.DeferralResponse{ + Reason: resource.DeferralReasonAbsentPrereq, + } + + testProto5Deferred := &tfprotov5.Deferred{ + Reason: tfprotov5.DeferredReasonAbsentPrereq, + } + testCases := map[string]struct { input *fwserver.ReadResourceResponse expected *tfprotov5.ReadResourceResponse @@ -167,6 +176,14 @@ func TestReadResourceResponse(t *testing.T) { }), }, }, + "deferral": { + input: &fwserver.ReadResourceResponse{ + Deferral: testDeferral, + }, + expected: &tfprotov5.ReadResourceResponse{ + Deferred: testProto5Deferred, + }, + }, } for name, testCase := range testCases { diff --git a/internal/toproto6/importresourcestate.go b/internal/toproto6/importresourcestate.go index f5ecd4862..ae003832f 100644 --- a/internal/toproto6/importresourcestate.go +++ b/internal/toproto6/importresourcestate.go @@ -35,7 +35,9 @@ func ImportResourceStateResponse(ctx context.Context, fw *fwserver.ImportResourc } if fw.Deferral != nil { - proto6.Deferred.Reason = tfprotov6.DeferredReason(fw.Deferral.Reason) + proto6.Deferred = &tfprotov6.Deferred{ + Reason: tfprotov6.DeferredReason(fw.Deferral.Reason), + } } return proto6 diff --git a/internal/toproto6/readresource.go b/internal/toproto6/readresource.go index d5e600e10..eb154d1d3 100644 --- a/internal/toproto6/readresource.go +++ b/internal/toproto6/readresource.go @@ -32,5 +32,11 @@ func ReadResourceResponse(ctx context.Context, fw *fwserver.ReadResourceResponse proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) proto6.Private = newPrivate + if fw.Deferral != nil { + proto6.Deferred = &tfprotov6.Deferred{ + Reason: tfprotov6.DeferredReason(fw.Deferral.Reason), + } + } + return proto6 } diff --git a/internal/toproto6/readresource_test.go b/internal/toproto6/readresource_test.go index c34141b4f..2fa0b46f6 100644 --- a/internal/toproto6/readresource_test.go +++ b/internal/toproto6/readresource_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/internal/toproto6" + "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -68,6 +69,14 @@ func TestReadResourceResponse(t *testing.T) { testEmptyProviderData := privatestate.EmptyProviderData(context.Background()) + testDeferral := &resource.DeferralResponse{ + Reason: resource.DeferralReasonAbsentPrereq, + } + + testProto6Deferred := &tfprotov6.Deferred{ + Reason: tfprotov6.DeferredReasonAbsentPrereq, + } + testCases := map[string]struct { input *fwserver.ReadResourceResponse expected *tfprotov6.ReadResourceResponse @@ -167,6 +176,14 @@ func TestReadResourceResponse(t *testing.T) { }), }, }, + "deferral": { + input: &fwserver.ReadResourceResponse{ + Deferral: testDeferral, + }, + expected: &tfprotov6.ReadResourceResponse{ + Deferred: testProto6Deferred, + }, + }, } for name, testCase := range testCases { diff --git a/resource/read.go b/resource/read.go index 0cc135434..0e714dfc1 100644 --- a/resource/read.go +++ b/resource/read.go @@ -9,6 +9,13 @@ import ( "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) +// TODO: doc +type ReadClientCapabilities struct { + // DeferralAllowed indicates whether the Terraform client initiating + // the request allows a deferral response. + DeferralAllowed bool +} + // ReadRequest represents a request for the provider to read a // resource, i.e., update values in state according to the real state of the // resource. An instance of this request struct is supplied as an argument to @@ -29,6 +36,9 @@ type ReadRequest struct { // ProviderMeta is metadata from the provider_meta block of the module. ProviderMeta tfsdk.Config + + //TODO: doc + ClientCapabilities *ReadClientCapabilities } // ReadResponse represents a response to a ReadRequest. An @@ -50,4 +60,11 @@ type ReadResponse struct { // resource. An empty slice indicates a successful operation with no // warnings or errors generated. Diagnostics diag.Diagnostics + + // DeferralResponse indicates that Terraform should defer + // importing this resource. + // + // This field can only be set if + // `(resource.ReadRequest.ReadStateClientCapabilities).DeferralAllowed` is true. + DeferralResponse *DeferralResponse } From bbf6c8dcd92b9e36e2ede61812b52028a56f7714 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Tue, 7 May 2024 14:58:07 -0400 Subject: [PATCH 03/25] Implement manual deferred action support for `resource.modifyPlan` --- internal/fromproto5/plan.go | 3 +- internal/fromproto5/planresourcechange.go | 10 +- .../fromproto5/planresourcechange_test.go | 14 +++ internal/fromproto6/planresourcechange.go | 10 +- .../fromproto6/planresourcechange_test.go | 14 +++ .../fwserver/server_planresourcechange.go | 36 ++++--- .../server_planresourcechange_test.go | 93 +++++++++++++++++++ internal/fwserver/server_readresource.go | 4 +- internal/toproto5/planresourcechange.go | 6 ++ internal/toproto5/planresourcechange_test.go | 17 ++++ internal/toproto6/planresourcechange.go | 6 ++ internal/toproto6/planresourcechange_test.go | 17 ++++ resource/modify_plan.go | 17 ++++ 13 files changed, 229 insertions(+), 18 deletions(-) diff --git a/internal/fromproto5/plan.go b/internal/fromproto5/plan.go index f20a1a509..3882811eb 100644 --- a/internal/fromproto5/plan.go +++ b/internal/fromproto5/plan.go @@ -6,11 +6,12 @@ package fromproto5 import ( "context" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - "github.com/hashicorp/terraform-plugin-go/tfprotov5" ) // Plan returns the *tfsdk.Plan for a *tfprotov5.DynamicValue and diff --git a/internal/fromproto5/planresourcechange.go b/internal/fromproto5/planresourcechange.go index 67cced273..0852ad5db 100644 --- a/internal/fromproto5/planresourcechange.go +++ b/internal/fromproto5/planresourcechange.go @@ -17,7 +17,7 @@ import ( // PlanResourceChangeRequest returns the *fwserver.PlanResourceChangeRequest // equivalent of a *tfprotov5.PlanResourceChangeRequest. -func PlanResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.PlanResourceChangeRequest, resource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema) (*fwserver.PlanResourceChangeRequest, diag.Diagnostics) { +func PlanResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.PlanResourceChangeRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema) (*fwserver.PlanResourceChangeRequest, diag.Diagnostics) { if proto5 == nil { return nil, nil } @@ -40,7 +40,7 @@ func PlanResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.PlanResour fw := &fwserver.PlanResourceChangeRequest{ ResourceSchema: resourceSchema, - Resource: resource, + Resource: reqResource, } config, configDiags := Config(ctx, proto5.Config, resourceSchema) @@ -73,5 +73,11 @@ func PlanResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.PlanResour fw.PriorPrivate = privateData + if proto5.ClientCapabilities != nil { + fw.ClientCapabilities = &resource.ModifyPlanClientCapabilities{ + DeferralAllowed: proto5.ClientCapabilities.DeferralAllowed, + } + } + return fw, diags } diff --git a/internal/fromproto5/planresourcechange_test.go b/internal/fromproto5/planresourcechange_test.go index 0d7638868..f134a807e 100644 --- a/internal/fromproto5/planresourcechange_test.go +++ b/internal/fromproto5/planresourcechange_test.go @@ -54,6 +54,8 @@ func TestPlanResourceChangeRequest(t *testing.T) { testProviderData := privatestate.MustProviderData(context.Background(), testProviderKeyValue) + testClientCapabilities := tfprotov5.ClientCapabilities{DeferralAllowed: true} + testCases := map[string]struct { input *tfprotov5.PlanResourceChangeRequest resourceSchema fwschema.Schema @@ -217,6 +219,18 @@ func TestPlanResourceChangeRequest(t *testing.T) { ResourceSchema: testFwSchema, }, }, + "client-capabilities": { + input: &tfprotov5.PlanResourceChangeRequest{ + ClientCapabilities: &testClientCapabilities, + }, + resourceSchema: testFwSchema, + expected: &fwserver.PlanResourceChangeRequest{ + ClientCapabilities: &resource.ModifyPlanClientCapabilities{ + DeferralAllowed: true, + }, + ResourceSchema: testFwSchema, + }, + }, } for name, testCase := range testCases { diff --git a/internal/fromproto6/planresourcechange.go b/internal/fromproto6/planresourcechange.go index 3c2bb3e9c..a957c9b9e 100644 --- a/internal/fromproto6/planresourcechange.go +++ b/internal/fromproto6/planresourcechange.go @@ -17,7 +17,7 @@ import ( // PlanResourceChangeRequest returns the *fwserver.PlanResourceChangeRequest // equivalent of a *tfprotov6.PlanResourceChangeRequest. -func PlanResourceChangeRequest(ctx context.Context, proto6 *tfprotov6.PlanResourceChangeRequest, resource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema) (*fwserver.PlanResourceChangeRequest, diag.Diagnostics) { +func PlanResourceChangeRequest(ctx context.Context, proto6 *tfprotov6.PlanResourceChangeRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema) (*fwserver.PlanResourceChangeRequest, diag.Diagnostics) { if proto6 == nil { return nil, nil } @@ -40,7 +40,7 @@ func PlanResourceChangeRequest(ctx context.Context, proto6 *tfprotov6.PlanResour fw := &fwserver.PlanResourceChangeRequest{ ResourceSchema: resourceSchema, - Resource: resource, + Resource: reqResource, } config, configDiags := Config(ctx, proto6.Config, resourceSchema) @@ -73,5 +73,11 @@ func PlanResourceChangeRequest(ctx context.Context, proto6 *tfprotov6.PlanResour fw.PriorPrivate = privateData + if proto6.ClientCapabilities != nil { + fw.ClientCapabilities = &resource.ModifyPlanClientCapabilities{ + DeferralAllowed: proto6.ClientCapabilities.DeferralAllowed, + } + } + return fw, diags } diff --git a/internal/fromproto6/planresourcechange_test.go b/internal/fromproto6/planresourcechange_test.go index ca8d11f59..332d7c473 100644 --- a/internal/fromproto6/planresourcechange_test.go +++ b/internal/fromproto6/planresourcechange_test.go @@ -54,6 +54,8 @@ func TestPlanResourceChangeRequest(t *testing.T) { testProviderData := privatestate.MustProviderData(context.Background(), testProviderKeyValue) + testClientCapabilities := tfprotov6.ClientCapabilities{DeferralAllowed: true} + testCases := map[string]struct { input *tfprotov6.PlanResourceChangeRequest resourceSchema fwschema.Schema @@ -217,6 +219,18 @@ func TestPlanResourceChangeRequest(t *testing.T) { ResourceSchema: testFwSchema, }, }, + "client-capabilities": { + input: &tfprotov6.PlanResourceChangeRequest{ + ClientCapabilities: &testClientCapabilities, + }, + resourceSchema: testFwSchema, + expected: &fwserver.PlanResourceChangeRequest{ + ClientCapabilities: &resource.ModifyPlanClientCapabilities{ + DeferralAllowed: true, + }, + ResourceSchema: testFwSchema, + }, + }, } for name, testCase := range testCases { diff --git a/internal/fwserver/server_planresourcechange.go b/internal/fwserver/server_planresourcechange.go index f769eea3f..e261a3fcf 100644 --- a/internal/fwserver/server_planresourcechange.go +++ b/internal/fwserver/server_planresourcechange.go @@ -26,18 +26,20 @@ import ( // PlanResourceChangeRequest is the framework server request for the // PlanResourceChange RPC. type PlanResourceChangeRequest struct { - Config *tfsdk.Config - PriorPrivate *privatestate.Data - PriorState *tfsdk.State - ProposedNewState *tfsdk.Plan - ProviderMeta *tfsdk.Config - ResourceSchema fwschema.Schema - Resource resource.Resource + ClientCapabilities *resource.ModifyPlanClientCapabilities + Config *tfsdk.Config + PriorPrivate *privatestate.Data + PriorState *tfsdk.State + ProposedNewState *tfsdk.Plan + ProviderMeta *tfsdk.Config + ResourceSchema fwschema.Schema + Resource resource.Resource } // PlanResourceChangeResponse is the framework server response for the // PlanResourceChange RPC. type PlanResourceChangeResponse struct { + Deferral *resource.DeferralResponse Diagnostics diag.Diagnostics PlannedPrivate *privatestate.Data PlannedState *tfsdk.State @@ -260,10 +262,11 @@ func (s *Server) PlanResourceChange(ctx context.Context, req *PlanResourceChange logging.FrameworkTrace(ctx, "Resource implements ResourceWithModifyPlan") modifyPlanReq := resource.ModifyPlanRequest{ - Config: *req.Config, - Plan: stateToPlan(*resp.PlannedState), - State: *req.PriorState, - Private: resp.PlannedPrivate.Provider, + Config: *req.Config, + Plan: stateToPlan(*resp.PlannedState), + State: *req.PriorState, + Private: resp.PlannedPrivate.Provider, + ClientCapabilities: req.ClientCapabilities, } if req.ProviderMeta != nil { @@ -281,10 +284,21 @@ func (s *Server) PlanResourceChange(ctx context.Context, req *PlanResourceChange resourceWithModifyPlan.ModifyPlan(ctx, modifyPlanReq, &modifyPlanResp) logging.FrameworkTrace(ctx, "Called provider defined Resource ModifyPlan") + if (modifyPlanReq.ClientCapabilities == nil || !modifyPlanReq.ClientCapabilities.DeferralAllowed) && modifyPlanResp.DeferralResponse != nil { + resp.Diagnostics.AddError( + "Resource Deferral Not Allowed", + "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ + "The resource requested a deferral but the Terraform client does not support deferrals, "+ + "(*resource.ModifyPlanResponse).DeferralResponse can only be set if (resource.ModifyPlanRequest).ClientCapabilities.DeferralAllowed is true.", + ) + return + } + resp.Diagnostics = modifyPlanResp.Diagnostics resp.PlannedState = planToState(modifyPlanResp.Plan) resp.RequiresReplace = append(resp.RequiresReplace, modifyPlanResp.RequiresReplace...) resp.PlannedPrivate.Provider = modifyPlanResp.Private + resp.Deferral = modifyPlanResp.DeferralResponse } // Ensure deterministic RequiresReplace by sorting and deduplicating diff --git a/internal/fwserver/server_planresourcechange_test.go b/internal/fwserver/server_planresourcechange_test.go index e8bd95c0c..aa62a2132 100644 --- a/internal/fwserver/server_planresourcechange_test.go +++ b/internal/fwserver/server_planresourcechange_test.go @@ -1230,6 +1230,10 @@ func TestServerPlanResourceChange(t *testing.T) { Provider: testEmptyProviderData, } + testDeferralAllowed := &resource.ModifyPlanClientCapabilities{ + DeferralAllowed: true, + } + testCases := map[string]struct { server *fwserver.Server request *fwserver.PlanResourceChangeRequest @@ -6015,6 +6019,95 @@ func TestServerPlanResourceChange(t *testing.T) { PlannedPrivate: testPrivateProvider, }, }, + "create-resourcewithmodifyplan-request-deferral-allowed-response-deferral": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.PlanResourceChangeRequest{ + ClientCapabilities: testDeferralAllowed, + Config: &tfsdk.Config{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + ProposedNewState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + PriorState: testEmptyState, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithModifyPlan{ + ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if req.ClientCapabilities.DeferralAllowed == true { + resp.DeferralResponse = &resource.DeferralResponse{Reason: resource.DeferralReasonAbsentPrereq} + } + + }, + }, + }, + expectedResponse: &fwserver.PlanResourceChangeResponse{ + Deferral: &resource.DeferralResponse{Reason: resource.DeferralReasonAbsentPrereq}, + PlannedState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + PlannedPrivate: testEmptyPrivate, + }, + }, + "create-resourcewithmodifyplan-request-deferral-not-allowed-response-deferral": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.PlanResourceChangeRequest{ + Config: &tfsdk.Config{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + ProposedNewState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + PriorState: testEmptyState, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithModifyPlan{ + ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + resp.DeferralResponse = &resource.DeferralResponse{Reason: resource.DeferralReasonAbsentPrereq} + }, + }, + }, + expectedResponse: &fwserver.PlanResourceChangeResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Resource Deferral Not Allowed", + "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ + "The resource requested a deferral but the Terraform client does not support deferrals, "+ + "(*resource.ModifyPlanResponse).DeferralResponse can only be set if (resource.ModifyPlanRequest).ClientCapabilities.DeferralAllowed is true.", + ), + }, + PlannedState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + PlannedPrivate: testEmptyPrivate, + }, + }, } for name, testCase := range testCases { diff --git a/internal/fwserver/server_readresource.go b/internal/fwserver/server_readresource.go index 47e5554e4..8ce320c72 100644 --- a/internal/fwserver/server_readresource.go +++ b/internal/fwserver/server_readresource.go @@ -17,20 +17,20 @@ import ( // ReadResourceRequest is the framework server request for the // ReadResource RPC. type ReadResourceRequest struct { + ClientCapabilities *resource.ReadClientCapabilities CurrentState *tfsdk.State Resource resource.Resource Private *privatestate.Data ProviderMeta *tfsdk.Config - ClientCapabilities *resource.ReadClientCapabilities } // ReadResourceResponse is the framework server response for the // ReadResource RPC. type ReadResourceResponse struct { + Deferral *resource.DeferralResponse Diagnostics diag.Diagnostics NewState *tfsdk.State Private *privatestate.Data - Deferral *resource.DeferralResponse } // ReadResource implements the framework server ReadResource RPC. diff --git a/internal/toproto5/planresourcechange.go b/internal/toproto5/planresourcechange.go index 1677b359a..b511c3870 100644 --- a/internal/toproto5/planresourcechange.go +++ b/internal/toproto5/planresourcechange.go @@ -38,5 +38,11 @@ func PlanResourceChangeResponse(ctx context.Context, fw *fwserver.PlanResourceCh proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) proto5.PlannedPrivate = plannedPrivate + if fw.Deferral != nil { + proto5.Deferred = &tfprotov5.Deferred{ + Reason: tfprotov5.DeferredReason(fw.Deferral.Reason), + } + } + return proto5 } diff --git a/internal/toproto5/planresourcechange_test.go b/internal/toproto5/planresourcechange_test.go index c0e76d83a..ca67cc258 100644 --- a/internal/toproto5/planresourcechange_test.go +++ b/internal/toproto5/planresourcechange_test.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/internal/toproto5" "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -69,6 +70,14 @@ func TestPlanResourceChangeResponse(t *testing.T) { testEmptyProviderData := privatestate.EmptyProviderData(context.Background()) + testDeferral := &resource.DeferralResponse{ + Reason: resource.DeferralReasonAbsentPrereq, + } + + testProto5Deferred := &tfprotov5.Deferred{ + Reason: tfprotov5.DeferredReasonAbsentPrereq, + } + testCases := map[string]struct { input *fwserver.PlanResourceChangeResponse expected *tfprotov5.PlanResourceChangeResponse @@ -180,6 +189,14 @@ func TestPlanResourceChangeResponse(t *testing.T) { }, }, }, + "deferral": { + input: &fwserver.PlanResourceChangeResponse{ + Deferral: testDeferral, + }, + expected: &tfprotov5.PlanResourceChangeResponse{ + Deferred: testProto5Deferred, + }, + }, } for name, testCase := range testCases { diff --git a/internal/toproto6/planresourcechange.go b/internal/toproto6/planresourcechange.go index 8cb2b66b1..cc538dfec 100644 --- a/internal/toproto6/planresourcechange.go +++ b/internal/toproto6/planresourcechange.go @@ -38,5 +38,11 @@ func PlanResourceChangeResponse(ctx context.Context, fw *fwserver.PlanResourceCh proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) proto6.PlannedPrivate = plannedPrivate + if fw.Deferral != nil { + proto6.Deferred = &tfprotov6.Deferred{ + Reason: tfprotov6.DeferredReason(fw.Deferral.Reason), + } + } + return proto6 } diff --git a/internal/toproto6/planresourcechange_test.go b/internal/toproto6/planresourcechange_test.go index ecae8783e..5bd5e0963 100644 --- a/internal/toproto6/planresourcechange_test.go +++ b/internal/toproto6/planresourcechange_test.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/internal/toproto6" "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -69,6 +70,14 @@ func TestPlanResourceChangeResponse(t *testing.T) { testEmptyProviderData := privatestate.EmptyProviderData(context.Background()) + testDeferral := &resource.DeferralResponse{ + Reason: resource.DeferralReasonAbsentPrereq, + } + + testProto6Deferred := &tfprotov6.Deferred{ + Reason: tfprotov6.DeferredReasonAbsentPrereq, + } + testCases := map[string]struct { input *fwserver.PlanResourceChangeResponse expected *tfprotov6.PlanResourceChangeResponse @@ -180,6 +189,14 @@ func TestPlanResourceChangeResponse(t *testing.T) { }, }, }, + "deferral": { + input: &fwserver.PlanResourceChangeResponse{ + Deferral: testDeferral, + }, + expected: &tfprotov6.PlanResourceChangeResponse{ + Deferred: testProto6Deferred, + }, + }, } for name, testCase := range testCases { diff --git a/resource/modify_plan.go b/resource/modify_plan.go index 76be450d8..655f84f5d 100644 --- a/resource/modify_plan.go +++ b/resource/modify_plan.go @@ -10,6 +10,13 @@ import ( "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) +// TODO: doc +type ModifyPlanClientCapabilities struct { + // DeferralAllowed indicates whether the Terraform client initiating + // the request allows a deferral response. + DeferralAllowed bool +} + // ModifyPlanRequest represents a request for the provider to modify the // planned new state that Terraform has generated for the resource. type ModifyPlanRequest struct { @@ -39,6 +46,9 @@ type ModifyPlanRequest struct { // Use the GetKey method to read data. Use the SetKey method on // ModifyPlanResponse.Private to update or remove a value. Private *privatestate.ProviderData + + //TODO: doc + ClientCapabilities *ModifyPlanClientCapabilities } // ModifyPlanResponse represents a response to a @@ -65,4 +75,11 @@ type ModifyPlanResponse struct { // indicates a successful plan modification with no warnings or errors // generated. Diagnostics diag.Diagnostics + + // DeferralResponse indicates that Terraform should defer + // importing this resource. + // + // This field can only be set if + // `(resource.ModifyPlanRequest.ModifyPlanClientCapabilities).DeferralAllowed` is true. + DeferralResponse *DeferralResponse } From 29cfc746fe7b7ed6095072b356bfcd15eb50e6b3 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Tue, 7 May 2024 15:18:51 -0400 Subject: [PATCH 04/25] Rename `DeferralReason` and `DeferralResponse` to `DeferredReason` and `DeferredResponse` respectively to match protobuf definition --- internal/fwserver/server_importresourcestate.go | 10 +++++----- .../fwserver/server_importresourcestate_test.go | 8 ++++---- internal/fwserver/server_planresourcechange.go | 8 ++++---- .../fwserver/server_planresourcechange_test.go | 8 ++++---- internal/fwserver/server_readresource.go | 8 ++++---- internal/fwserver/server_readresource_test.go | 10 +++++----- internal/toproto5/importresourcestate.go | 4 ++-- internal/toproto5/planresourcechange.go | 4 ++-- internal/toproto5/planresourcechange_test.go | 4 ++-- internal/toproto5/readresource.go | 4 ++-- internal/toproto5/readresource_test.go | 4 ++-- internal/toproto6/importresourcestate.go | 4 ++-- internal/toproto6/planresourcechange.go | 4 ++-- internal/toproto6/planresourcechange_test.go | 4 ++-- internal/toproto6/readresource.go | 4 ++-- internal/toproto6/readresource_test.go | 4 ++-- resource/deferral.go | 16 ++++++++-------- resource/import_state.go | 4 ++-- resource/modify_plan.go | 4 ++-- resource/read.go | 4 ++-- 20 files changed, 60 insertions(+), 60 deletions(-) diff --git a/internal/fwserver/server_importresourcestate.go b/internal/fwserver/server_importresourcestate.go index 65b1a0761..307ccd920 100644 --- a/internal/fwserver/server_importresourcestate.go +++ b/internal/fwserver/server_importresourcestate.go @@ -45,7 +45,7 @@ type ImportResourceStateRequest struct { type ImportResourceStateResponse struct { Diagnostics diag.Diagnostics ImportedResources []ImportedResource - Deferral *resource.DeferralResponse + Deferred *resource.DeferredResponse } // ImportResourceState implements the framework server ImportResourceState RPC. @@ -118,12 +118,12 @@ func (s *Server) ImportResourceState(ctx context.Context, req *ImportResourceSta return } - if (importReq.ClientCapabilities == nil || !importReq.ClientCapabilities.DeferralAllowed) && importResp.DeferralResponse != nil { + if (importReq.ClientCapabilities == nil || !importReq.ClientCapabilities.DeferralAllowed) && importResp.DeferredResponse != nil { resp.Diagnostics.AddError( "Resource Import Deferral Not Allowed", "An unexpected error was encountered when importing the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "resource.DeferralResponse can only be set if resource.ImportStateRequest.ImportStateClientCapabilities.DeferralAllowed is true.", + "resource.DeferredResponse can only be set if resource.ImportStateRequest.ImportStateClientCapabilities.DeferralAllowed is true.", ) return } @@ -143,8 +143,8 @@ func (s *Server) ImportResourceState(ctx context.Context, req *ImportResourceSta private.Provider = importResp.Private } - if importResp.DeferralResponse != nil { - resp.Deferral = importResp.DeferralResponse + if importResp.DeferredResponse != nil { + resp.Deferred = importResp.DeferredResponse } resp.ImportedResources = []ImportedResource{ diff --git a/internal/fwserver/server_importresourcestate_test.go b/internal/fwserver/server_importresourcestate_test.go index bffba4e59..e4bed808e 100644 --- a/internal/fwserver/server_importresourcestate_test.go +++ b/internal/fwserver/server_importresourcestate_test.go @@ -317,7 +317,7 @@ func TestServerImportResourceState(t *testing.T) { resp.Diagnostics.AddError("unexpected req.ID value: %s", req.ID) } - resp.DeferralResponse = &resource.DeferralResponse{ + resp.DeferredResponse = &resource.DeferredResponse{ Reason: resource.DeferralReasonAbsentPrereq, } @@ -336,7 +336,7 @@ func TestServerImportResourceState(t *testing.T) { Private: testEmptyPrivate, }, }, - Deferral: &resource.DeferralResponse{Reason: resource.DeferralReasonAbsentPrereq}, + Deferred: &resource.DeferredResponse{Reason: resource.DeferralReasonAbsentPrereq}, }, }, "request-deferral-not-allowed-response-deferral": { @@ -353,7 +353,7 @@ func TestServerImportResourceState(t *testing.T) { resp.Diagnostics.AddError("unexpected req.ID value: %s", req.ID) } - resp.DeferralResponse = &resource.DeferralResponse{ + resp.DeferredResponse = &resource.DeferredResponse{ Reason: resource.DeferralReasonAbsentPrereq, } @@ -369,7 +369,7 @@ func TestServerImportResourceState(t *testing.T) { "Resource Import Deferral Not Allowed", "An unexpected error was encountered when importing the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "resource.DeferralResponse can only be set if resource.ImportStateRequest.ImportStateClientCapabilities.DeferralAllowed is true.", + "resource.DeferredResponse can only be set if resource.ImportStateRequest.ImportStateClientCapabilities.DeferralAllowed is true.", ), }, }, diff --git a/internal/fwserver/server_planresourcechange.go b/internal/fwserver/server_planresourcechange.go index e261a3fcf..dc469d449 100644 --- a/internal/fwserver/server_planresourcechange.go +++ b/internal/fwserver/server_planresourcechange.go @@ -39,7 +39,7 @@ type PlanResourceChangeRequest struct { // PlanResourceChangeResponse is the framework server response for the // PlanResourceChange RPC. type PlanResourceChangeResponse struct { - Deferral *resource.DeferralResponse + Deferred *resource.DeferredResponse Diagnostics diag.Diagnostics PlannedPrivate *privatestate.Data PlannedState *tfsdk.State @@ -284,12 +284,12 @@ func (s *Server) PlanResourceChange(ctx context.Context, req *PlanResourceChange resourceWithModifyPlan.ModifyPlan(ctx, modifyPlanReq, &modifyPlanResp) logging.FrameworkTrace(ctx, "Called provider defined Resource ModifyPlan") - if (modifyPlanReq.ClientCapabilities == nil || !modifyPlanReq.ClientCapabilities.DeferralAllowed) && modifyPlanResp.DeferralResponse != nil { + if (modifyPlanReq.ClientCapabilities == nil || !modifyPlanReq.ClientCapabilities.DeferralAllowed) && modifyPlanResp.DeferredResponse != nil { resp.Diagnostics.AddError( "Resource Deferral Not Allowed", "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "(*resource.ModifyPlanResponse).DeferralResponse can only be set if (resource.ModifyPlanRequest).ClientCapabilities.DeferralAllowed is true.", + "(*resource.ModifyPlanResponse).DeferredResponse can only be set if (resource.ModifyPlanRequest).ClientCapabilities.DeferralAllowed is true.", ) return } @@ -298,7 +298,7 @@ func (s *Server) PlanResourceChange(ctx context.Context, req *PlanResourceChange resp.PlannedState = planToState(modifyPlanResp.Plan) resp.RequiresReplace = append(resp.RequiresReplace, modifyPlanResp.RequiresReplace...) resp.PlannedPrivate.Provider = modifyPlanResp.Private - resp.Deferral = modifyPlanResp.DeferralResponse + resp.Deferred = modifyPlanResp.DeferredResponse } // Ensure deterministic RequiresReplace by sorting and deduplicating diff --git a/internal/fwserver/server_planresourcechange_test.go b/internal/fwserver/server_planresourcechange_test.go index aa62a2132..40b2b1509 100644 --- a/internal/fwserver/server_planresourcechange_test.go +++ b/internal/fwserver/server_planresourcechange_test.go @@ -6044,14 +6044,14 @@ func TestServerPlanResourceChange(t *testing.T) { Resource: &testprovider.ResourceWithModifyPlan{ ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { if req.ClientCapabilities.DeferralAllowed == true { - resp.DeferralResponse = &resource.DeferralResponse{Reason: resource.DeferralReasonAbsentPrereq} + resp.DeferredResponse = &resource.DeferredResponse{Reason: resource.DeferralReasonAbsentPrereq} } }, }, }, expectedResponse: &fwserver.PlanResourceChangeResponse{ - Deferral: &resource.DeferralResponse{Reason: resource.DeferralReasonAbsentPrereq}, + Deferred: &resource.DeferredResponse{Reason: resource.DeferralReasonAbsentPrereq}, PlannedState: &tfsdk.State{ Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ "test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), @@ -6085,7 +6085,7 @@ func TestServerPlanResourceChange(t *testing.T) { ResourceSchema: testSchema, Resource: &testprovider.ResourceWithModifyPlan{ ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - resp.DeferralResponse = &resource.DeferralResponse{Reason: resource.DeferralReasonAbsentPrereq} + resp.DeferredResponse = &resource.DeferredResponse{Reason: resource.DeferralReasonAbsentPrereq} }, }, }, @@ -6095,7 +6095,7 @@ func TestServerPlanResourceChange(t *testing.T) { "Resource Deferral Not Allowed", "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "(*resource.ModifyPlanResponse).DeferralResponse can only be set if (resource.ModifyPlanRequest).ClientCapabilities.DeferralAllowed is true.", + "(*resource.ModifyPlanResponse).DeferredResponse can only be set if (resource.ModifyPlanRequest).ClientCapabilities.DeferralAllowed is true.", ), }, PlannedState: &tfsdk.State{ diff --git a/internal/fwserver/server_readresource.go b/internal/fwserver/server_readresource.go index 8ce320c72..7b7831a9f 100644 --- a/internal/fwserver/server_readresource.go +++ b/internal/fwserver/server_readresource.go @@ -27,7 +27,7 @@ type ReadResourceRequest struct { // ReadResourceResponse is the framework server response for the // ReadResource RPC. type ReadResourceResponse struct { - Deferral *resource.DeferralResponse + Deferred *resource.DeferredResponse Diagnostics diag.Diagnostics NewState *tfsdk.State Private *privatestate.Data @@ -107,7 +107,7 @@ func (s *Server) ReadResource(ctx context.Context, req *ReadResourceRequest, res resp.Diagnostics = readResp.Diagnostics resp.NewState = &readResp.State - resp.Deferral = readResp.DeferralResponse + resp.Deferred = readResp.DeferredResponse if readResp.Private != nil { if resp.Private == nil { @@ -121,12 +121,12 @@ func (s *Server) ReadResource(ctx context.Context, req *ReadResourceRequest, res return } - if (req.ClientCapabilities == nil || !req.ClientCapabilities.DeferralAllowed) && resp.Deferral != nil { + if (req.ClientCapabilities == nil || !req.ClientCapabilities.DeferralAllowed) && resp.Deferred != nil { resp.Diagnostics.AddError( "Resource Deferral Not Allowed", "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "resource.DeferralResponse can only be set if resource.ReadRequest.ReadClientCapabilities.DeferralAllowed is true.", + "resource.DeferredResponse can only be set if resource.ReadRequest.ReadClientCapabilities.DeferralAllowed is true.", ) return } diff --git a/internal/fwserver/server_readresource_test.go b/internal/fwserver/server_readresource_test.go index f95f16511..e6164d12b 100644 --- a/internal/fwserver/server_readresource_test.go +++ b/internal/fwserver/server_readresource_test.go @@ -529,7 +529,7 @@ func TestServerReadResource(t *testing.T) { resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - resp.DeferralResponse = &resource.DeferralResponse{Reason: resource.DeferralReasonAbsentPrereq} + resp.DeferredResponse = &resource.DeferredResponse{Reason: resource.DeferralReasonAbsentPrereq} if data.TestRequired.ValueString() != "test-currentstate-value" { resp.Diagnostics.AddError("unexpected req.State value: %s", data.TestRequired.ValueString()) @@ -541,7 +541,7 @@ func TestServerReadResource(t *testing.T) { expectedResponse: &fwserver.ReadResourceResponse{ NewState: testCurrentState, Private: testEmptyPrivate, - Deferral: &resource.DeferralResponse{Reason: resource.DeferralReasonAbsentPrereq}, + Deferred: &resource.DeferredResponse{Reason: resource.DeferralReasonAbsentPrereq}, }, }, "request-deferral-not-allowed-response-deferral": { @@ -559,7 +559,7 @@ func TestServerReadResource(t *testing.T) { resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - resp.DeferralResponse = &resource.DeferralResponse{Reason: resource.DeferralReasonAbsentPrereq} + resp.DeferredResponse = &resource.DeferredResponse{Reason: resource.DeferralReasonAbsentPrereq} if data.TestRequired.ValueString() != "test-currentstate-value" { resp.Diagnostics.AddError("unexpected req.State value: %s", data.TestRequired.ValueString()) @@ -573,12 +573,12 @@ func TestServerReadResource(t *testing.T) { "Resource Deferral Not Allowed", "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "resource.DeferralResponse can only be set if resource.ReadRequest.ReadClientCapabilities.DeferralAllowed is true.", + "resource.DeferredResponse can only be set if resource.ReadRequest.ReadClientCapabilities.DeferralAllowed is true.", ), }, NewState: testCurrentState, Private: testEmptyPrivate, - Deferral: &resource.DeferralResponse{Reason: resource.DeferralReasonAbsentPrereq}, + Deferred: &resource.DeferredResponse{Reason: resource.DeferralReasonAbsentPrereq}, }, }, } diff --git a/internal/toproto5/importresourcestate.go b/internal/toproto5/importresourcestate.go index 49131f02f..92e438c42 100644 --- a/internal/toproto5/importresourcestate.go +++ b/internal/toproto5/importresourcestate.go @@ -34,9 +34,9 @@ func ImportResourceStateResponse(ctx context.Context, fw *fwserver.ImportResourc proto5.ImportedResources = append(proto5.ImportedResources, proto5ImportedResource) } - if fw.Deferral != nil { + if fw.Deferred != nil { proto5.Deferred = &tfprotov5.Deferred{ - Reason: tfprotov5.DeferredReason(fw.Deferral.Reason), + Reason: tfprotov5.DeferredReason(fw.Deferred.Reason), } } diff --git a/internal/toproto5/planresourcechange.go b/internal/toproto5/planresourcechange.go index b511c3870..6b2abcc75 100644 --- a/internal/toproto5/planresourcechange.go +++ b/internal/toproto5/planresourcechange.go @@ -38,9 +38,9 @@ func PlanResourceChangeResponse(ctx context.Context, fw *fwserver.PlanResourceCh proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) proto5.PlannedPrivate = plannedPrivate - if fw.Deferral != nil { + if fw.Deferred != nil { proto5.Deferred = &tfprotov5.Deferred{ - Reason: tfprotov5.DeferredReason(fw.Deferral.Reason), + Reason: tfprotov5.DeferredReason(fw.Deferred.Reason), } } diff --git a/internal/toproto5/planresourcechange_test.go b/internal/toproto5/planresourcechange_test.go index ca67cc258..72b707a69 100644 --- a/internal/toproto5/planresourcechange_test.go +++ b/internal/toproto5/planresourcechange_test.go @@ -70,7 +70,7 @@ func TestPlanResourceChangeResponse(t *testing.T) { testEmptyProviderData := privatestate.EmptyProviderData(context.Background()) - testDeferral := &resource.DeferralResponse{ + testDeferred := &resource.DeferredResponse{ Reason: resource.DeferralReasonAbsentPrereq, } @@ -191,7 +191,7 @@ func TestPlanResourceChangeResponse(t *testing.T) { }, "deferral": { input: &fwserver.PlanResourceChangeResponse{ - Deferral: testDeferral, + Deferred: testDeferred, }, expected: &tfprotov5.PlanResourceChangeResponse{ Deferred: testProto5Deferred, diff --git a/internal/toproto5/readresource.go b/internal/toproto5/readresource.go index d60d2eab1..5f6ff7aef 100644 --- a/internal/toproto5/readresource.go +++ b/internal/toproto5/readresource.go @@ -32,9 +32,9 @@ func ReadResourceResponse(ctx context.Context, fw *fwserver.ReadResourceResponse proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) proto5.Private = newPrivate - if fw.Deferral != nil { + if fw.Deferred != nil { proto5.Deferred = &tfprotov5.Deferred{ - Reason: tfprotov5.DeferredReason(fw.Deferral.Reason), + Reason: tfprotov5.DeferredReason(fw.Deferred.Reason), } } diff --git a/internal/toproto5/readresource_test.go b/internal/toproto5/readresource_test.go index 9e66c7065..65865d76c 100644 --- a/internal/toproto5/readresource_test.go +++ b/internal/toproto5/readresource_test.go @@ -69,7 +69,7 @@ func TestReadResourceResponse(t *testing.T) { }, } - testDeferral := &resource.DeferralResponse{ + testDeferral := &resource.DeferredResponse{ Reason: resource.DeferralReasonAbsentPrereq, } @@ -178,7 +178,7 @@ func TestReadResourceResponse(t *testing.T) { }, "deferral": { input: &fwserver.ReadResourceResponse{ - Deferral: testDeferral, + Deferred: testDeferral, }, expected: &tfprotov5.ReadResourceResponse{ Deferred: testProto5Deferred, diff --git a/internal/toproto6/importresourcestate.go b/internal/toproto6/importresourcestate.go index ae003832f..447671c37 100644 --- a/internal/toproto6/importresourcestate.go +++ b/internal/toproto6/importresourcestate.go @@ -34,9 +34,9 @@ func ImportResourceStateResponse(ctx context.Context, fw *fwserver.ImportResourc proto6.ImportedResources = append(proto6.ImportedResources, proto6ImportedResource) } - if fw.Deferral != nil { + if fw.Deferred != nil { proto6.Deferred = &tfprotov6.Deferred{ - Reason: tfprotov6.DeferredReason(fw.Deferral.Reason), + Reason: tfprotov6.DeferredReason(fw.Deferred.Reason), } } diff --git a/internal/toproto6/planresourcechange.go b/internal/toproto6/planresourcechange.go index cc538dfec..d9499f979 100644 --- a/internal/toproto6/planresourcechange.go +++ b/internal/toproto6/planresourcechange.go @@ -38,9 +38,9 @@ func PlanResourceChangeResponse(ctx context.Context, fw *fwserver.PlanResourceCh proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) proto6.PlannedPrivate = plannedPrivate - if fw.Deferral != nil { + if fw.Deferred != nil { proto6.Deferred = &tfprotov6.Deferred{ - Reason: tfprotov6.DeferredReason(fw.Deferral.Reason), + Reason: tfprotov6.DeferredReason(fw.Deferred.Reason), } } diff --git a/internal/toproto6/planresourcechange_test.go b/internal/toproto6/planresourcechange_test.go index 5bd5e0963..50f17071a 100644 --- a/internal/toproto6/planresourcechange_test.go +++ b/internal/toproto6/planresourcechange_test.go @@ -70,7 +70,7 @@ func TestPlanResourceChangeResponse(t *testing.T) { testEmptyProviderData := privatestate.EmptyProviderData(context.Background()) - testDeferral := &resource.DeferralResponse{ + testDeferred := &resource.DeferredResponse{ Reason: resource.DeferralReasonAbsentPrereq, } @@ -191,7 +191,7 @@ func TestPlanResourceChangeResponse(t *testing.T) { }, "deferral": { input: &fwserver.PlanResourceChangeResponse{ - Deferral: testDeferral, + Deferred: testDeferred, }, expected: &tfprotov6.PlanResourceChangeResponse{ Deferred: testProto6Deferred, diff --git a/internal/toproto6/readresource.go b/internal/toproto6/readresource.go index eb154d1d3..6f515fce9 100644 --- a/internal/toproto6/readresource.go +++ b/internal/toproto6/readresource.go @@ -32,9 +32,9 @@ func ReadResourceResponse(ctx context.Context, fw *fwserver.ReadResourceResponse proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) proto6.Private = newPrivate - if fw.Deferral != nil { + if fw.Deferred != nil { proto6.Deferred = &tfprotov6.Deferred{ - Reason: tfprotov6.DeferredReason(fw.Deferral.Reason), + Reason: tfprotov6.DeferredReason(fw.Deferred.Reason), } } diff --git a/internal/toproto6/readresource_test.go b/internal/toproto6/readresource_test.go index 2fa0b46f6..49f734269 100644 --- a/internal/toproto6/readresource_test.go +++ b/internal/toproto6/readresource_test.go @@ -69,7 +69,7 @@ func TestReadResourceResponse(t *testing.T) { testEmptyProviderData := privatestate.EmptyProviderData(context.Background()) - testDeferral := &resource.DeferralResponse{ + testDeferral := &resource.DeferredResponse{ Reason: resource.DeferralReasonAbsentPrereq, } @@ -178,7 +178,7 @@ func TestReadResourceResponse(t *testing.T) { }, "deferral": { input: &fwserver.ReadResourceResponse{ - Deferral: testDeferral, + Deferred: testDeferral, }, expected: &tfprotov6.ReadResourceResponse{ Deferred: testProto6Deferred, diff --git a/resource/deferral.go b/resource/deferral.go index 1ce045222..640f31bee 100644 --- a/resource/deferral.go +++ b/resource/deferral.go @@ -1,19 +1,19 @@ package resource const ( - DeferralReasonUnknown DeferralReason = 0 - DeferralReasonResourceConfigUnknown DeferralReason = 1 - DeferralReasonProviderConfigUnknown DeferralReason = 2 - DeferralReasonAbsentPrereq DeferralReason = 3 + DeferralReasonUnknown DeferredReason = 0 + DeferralReasonResourceConfigUnknown DeferredReason = 1 + DeferralReasonProviderConfigUnknown DeferredReason = 2 + DeferralReasonAbsentPrereq DeferredReason = 3 ) -type DeferralResponse struct { - Reason DeferralReason +type DeferredResponse struct { + Reason DeferredReason } -type DeferralReason int32 +type DeferredReason int32 -func (d DeferralReason) String() string { +func (d DeferredReason) String() string { switch d { case 0: return "Unknown" diff --git a/resource/import_state.go b/resource/import_state.go index 7e9c0669a..658cc2bb7 100644 --- a/resource/import_state.go +++ b/resource/import_state.go @@ -55,12 +55,12 @@ type ImportStateResponse struct { // data during the resource's Import operation. Private *privatestate.ProviderData - // DeferralResponse indicates that Terraform should defer + // DeferredResponse indicates that Terraform should defer // importing this resource. // // This field can only be set if // `(resource.ImportStateRequest.ImportStateClientCapabilities).DeferralAllowed` is true. - DeferralResponse *DeferralResponse + DeferredResponse *DeferredResponse } // ImportStatePassthroughID is a helper function to set the import diff --git a/resource/modify_plan.go b/resource/modify_plan.go index 655f84f5d..6f3f563e4 100644 --- a/resource/modify_plan.go +++ b/resource/modify_plan.go @@ -76,10 +76,10 @@ type ModifyPlanResponse struct { // generated. Diagnostics diag.Diagnostics - // DeferralResponse indicates that Terraform should defer + // DeferredResponse indicates that Terraform should defer // importing this resource. // // This field can only be set if // `(resource.ModifyPlanRequest.ModifyPlanClientCapabilities).DeferralAllowed` is true. - DeferralResponse *DeferralResponse + DeferredResponse *DeferredResponse } diff --git a/resource/read.go b/resource/read.go index 0e714dfc1..df26d98a4 100644 --- a/resource/read.go +++ b/resource/read.go @@ -61,10 +61,10 @@ type ReadResponse struct { // warnings or errors generated. Diagnostics diag.Diagnostics - // DeferralResponse indicates that Terraform should defer + // DeferredResponse indicates that Terraform should defer // importing this resource. // // This field can only be set if // `(resource.ReadRequest.ReadStateClientCapabilities).DeferralAllowed` is true. - DeferralResponse *DeferralResponse + DeferredResponse *DeferredResponse } From 67ff59047dcafc0da6a2ba7d22231ae9fc5f85bc Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Tue, 7 May 2024 15:24:52 -0400 Subject: [PATCH 05/25] Update `terraform-plugin-go` dependency to `v0.23.0` --- go.mod | 4 ++-- go.sum | 4 ++++ internal/fromproto5/importresourcestate_test.go | 2 +- internal/fromproto5/planresourcechange_test.go | 2 +- internal/fromproto5/readresource_test.go | 2 +- internal/fromproto6/importresourcestate_test.go | 2 +- internal/fromproto6/planresourcechange_test.go | 2 +- internal/fromproto6/readresource_test.go | 2 +- 8 files changed, 12 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 377c56177..bbe8dfada 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.21.6 require ( github.com/google/go-cmp v0.6.0 - github.com/hashicorp/terraform-plugin-go v0.22.3-0.20240424182422-dba624001d4f + github.com/hashicorp/terraform-plugin-go v0.23.0 github.com/hashicorp/terraform-plugin-log v0.9.0 ) @@ -30,5 +30,5 @@ require ( golang.org/x/text v0.14.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/grpc v1.63.2 // indirect - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/protobuf v1.34.0 // indirect ) diff --git a/go.sum b/go.sum index 7380e78b6..8ae0ec93b 100644 --- a/go.sum +++ b/go.sum @@ -19,6 +19,8 @@ github.com/hashicorp/terraform-plugin-go v0.22.3-0.20240419152848-9a1607db1cab h github.com/hashicorp/terraform-plugin-go v0.22.3-0.20240419152848-9a1607db1cab/go.mod h1:drq8Snexp9HsbFZddvyLHN6LuWHHndSQg+gV+FPkcIM= github.com/hashicorp/terraform-plugin-go v0.22.3-0.20240424182422-dba624001d4f h1:r6EtFgZbSf+VeNrWqFwTFl5mKBdlFl+bpufwgaNBeQA= github.com/hashicorp/terraform-plugin-go v0.22.3-0.20240424182422-dba624001d4f/go.mod h1:DbW1zoh21fsPD35whjnDA5aR7bXQV+k+lnkZrn8ZrFM= +github.com/hashicorp/terraform-plugin-go v0.23.0 h1:AALVuU1gD1kPb48aPQUjug9Ir/125t+AAurhqphJ2Co= +github.com/hashicorp/terraform-plugin-go v0.23.0/go.mod h1:1E3Cr9h2vMlahWMbsSEcNrOCxovCZhOOIXjFHbjc/lQ= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= @@ -65,6 +67,8 @@ google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4= +google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/fromproto5/importresourcestate_test.go b/internal/fromproto5/importresourcestate_test.go index 00013ced3..f3de43bcf 100644 --- a/internal/fromproto5/importresourcestate_test.go +++ b/internal/fromproto5/importresourcestate_test.go @@ -90,7 +90,7 @@ func TestImportResourceStateRequest(t *testing.T) { "client-capabilities": { input: &tfprotov5.ImportResourceStateRequest{ ID: "test-id", - ClientCapabilities: &tfprotov5.ClientCapabilities{ + ClientCapabilities: &tfprotov5.ImportResourceStateClientCapabilities{ DeferralAllowed: true, }, }, diff --git a/internal/fromproto5/planresourcechange_test.go b/internal/fromproto5/planresourcechange_test.go index f134a807e..e629b5d38 100644 --- a/internal/fromproto5/planresourcechange_test.go +++ b/internal/fromproto5/planresourcechange_test.go @@ -54,7 +54,7 @@ func TestPlanResourceChangeRequest(t *testing.T) { testProviderData := privatestate.MustProviderData(context.Background(), testProviderKeyValue) - testClientCapabilities := tfprotov5.ClientCapabilities{DeferralAllowed: true} + testClientCapabilities := tfprotov5.PlanResourceChangeClientCapabilities{DeferralAllowed: true} testCases := map[string]struct { input *tfprotov5.PlanResourceChangeRequest diff --git a/internal/fromproto5/readresource_test.go b/internal/fromproto5/readresource_test.go index 7a0c9534b..28aadec15 100644 --- a/internal/fromproto5/readresource_test.go +++ b/internal/fromproto5/readresource_test.go @@ -56,7 +56,7 @@ func TestReadResourceRequest(t *testing.T) { testEmptyProviderData := privatestate.EmptyProviderData(context.Background()) - testClientCapabilities := tfprotov5.ClientCapabilities{DeferralAllowed: true} + testClientCapabilities := tfprotov5.ReadResourceClientCapabilities{DeferralAllowed: true} testCases := map[string]struct { input *tfprotov5.ReadResourceRequest diff --git a/internal/fromproto6/importresourcestate_test.go b/internal/fromproto6/importresourcestate_test.go index 6ef29c77e..fb11a340d 100644 --- a/internal/fromproto6/importresourcestate_test.go +++ b/internal/fromproto6/importresourcestate_test.go @@ -90,7 +90,7 @@ func TestImportResourceStateRequest(t *testing.T) { "client-capabilities": { input: &tfprotov6.ImportResourceStateRequest{ ID: "test-id", - ClientCapabilities: &tfprotov6.ClientCapabilities{ + ClientCapabilities: &tfprotov6.ImportResourceStateClientCapabilities{ DeferralAllowed: true, }, }, diff --git a/internal/fromproto6/planresourcechange_test.go b/internal/fromproto6/planresourcechange_test.go index 332d7c473..5b96bbe79 100644 --- a/internal/fromproto6/planresourcechange_test.go +++ b/internal/fromproto6/planresourcechange_test.go @@ -54,7 +54,7 @@ func TestPlanResourceChangeRequest(t *testing.T) { testProviderData := privatestate.MustProviderData(context.Background(), testProviderKeyValue) - testClientCapabilities := tfprotov6.ClientCapabilities{DeferralAllowed: true} + testClientCapabilities := tfprotov6.PlanResourceChangeClientCapabilities{DeferralAllowed: true} testCases := map[string]struct { input *tfprotov6.PlanResourceChangeRequest diff --git a/internal/fromproto6/readresource_test.go b/internal/fromproto6/readresource_test.go index f3f61a017..a6a238188 100644 --- a/internal/fromproto6/readresource_test.go +++ b/internal/fromproto6/readresource_test.go @@ -56,7 +56,7 @@ func TestReadResourceRequest(t *testing.T) { testEmptyProviderData := privatestate.EmptyProviderData(context.Background()) - testClientCapabilities := tfprotov6.ClientCapabilities{DeferralAllowed: true} + testClientCapabilities := tfprotov6.ReadResourceClientCapabilities{DeferralAllowed: true} testCases := map[string]struct { input *tfprotov6.ReadResourceRequest From 4a1140873d5eafbec4395a2f10a8f513db969bac Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Tue, 7 May 2024 15:56:44 -0400 Subject: [PATCH 06/25] Rename `deferral.go` to `deferred.go` --- .../server_importresourcestate_test.go | 6 +-- .../server_planresourcechange_test.go | 6 +-- internal/fwserver/server_readresource_test.go | 8 ++-- internal/toproto5/planresourcechange_test.go | 2 +- internal/toproto5/readresource_test.go | 2 +- internal/toproto6/planresourcechange_test.go | 2 +- internal/toproto6/readresource_test.go | 2 +- resource/deferral.go | 28 ------------- resource/deferred.go | 41 +++++++++++++++++++ 9 files changed, 55 insertions(+), 42 deletions(-) delete mode 100644 resource/deferral.go create mode 100644 resource/deferred.go diff --git a/internal/fwserver/server_importresourcestate_test.go b/internal/fwserver/server_importresourcestate_test.go index e4bed808e..492b0ba3c 100644 --- a/internal/fwserver/server_importresourcestate_test.go +++ b/internal/fwserver/server_importresourcestate_test.go @@ -318,7 +318,7 @@ func TestServerImportResourceState(t *testing.T) { } resp.DeferredResponse = &resource.DeferredResponse{ - Reason: resource.DeferralReasonAbsentPrereq, + Reason: resource.DeferredReasonAbsentPrereq, } resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) @@ -336,7 +336,7 @@ func TestServerImportResourceState(t *testing.T) { Private: testEmptyPrivate, }, }, - Deferred: &resource.DeferredResponse{Reason: resource.DeferralReasonAbsentPrereq}, + Deferred: &resource.DeferredResponse{Reason: resource.DeferredReasonAbsentPrereq}, }, }, "request-deferral-not-allowed-response-deferral": { @@ -354,7 +354,7 @@ func TestServerImportResourceState(t *testing.T) { } resp.DeferredResponse = &resource.DeferredResponse{ - Reason: resource.DeferralReasonAbsentPrereq, + Reason: resource.DeferredReasonAbsentPrereq, } resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) diff --git a/internal/fwserver/server_planresourcechange_test.go b/internal/fwserver/server_planresourcechange_test.go index 40b2b1509..78d6b050e 100644 --- a/internal/fwserver/server_planresourcechange_test.go +++ b/internal/fwserver/server_planresourcechange_test.go @@ -6044,14 +6044,14 @@ func TestServerPlanResourceChange(t *testing.T) { Resource: &testprovider.ResourceWithModifyPlan{ ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { if req.ClientCapabilities.DeferralAllowed == true { - resp.DeferredResponse = &resource.DeferredResponse{Reason: resource.DeferralReasonAbsentPrereq} + resp.DeferredResponse = &resource.DeferredResponse{Reason: resource.DeferredReasonAbsentPrereq} } }, }, }, expectedResponse: &fwserver.PlanResourceChangeResponse{ - Deferred: &resource.DeferredResponse{Reason: resource.DeferralReasonAbsentPrereq}, + Deferred: &resource.DeferredResponse{Reason: resource.DeferredReasonAbsentPrereq}, PlannedState: &tfsdk.State{ Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ "test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), @@ -6085,7 +6085,7 @@ func TestServerPlanResourceChange(t *testing.T) { ResourceSchema: testSchema, Resource: &testprovider.ResourceWithModifyPlan{ ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - resp.DeferredResponse = &resource.DeferredResponse{Reason: resource.DeferralReasonAbsentPrereq} + resp.DeferredResponse = &resource.DeferredResponse{Reason: resource.DeferredReasonAbsentPrereq} }, }, }, diff --git a/internal/fwserver/server_readresource_test.go b/internal/fwserver/server_readresource_test.go index e6164d12b..d7f4e9e9d 100644 --- a/internal/fwserver/server_readresource_test.go +++ b/internal/fwserver/server_readresource_test.go @@ -529,7 +529,7 @@ func TestServerReadResource(t *testing.T) { resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - resp.DeferredResponse = &resource.DeferredResponse{Reason: resource.DeferralReasonAbsentPrereq} + resp.DeferredResponse = &resource.DeferredResponse{Reason: resource.DeferredReasonAbsentPrereq} if data.TestRequired.ValueString() != "test-currentstate-value" { resp.Diagnostics.AddError("unexpected req.State value: %s", data.TestRequired.ValueString()) @@ -541,7 +541,7 @@ func TestServerReadResource(t *testing.T) { expectedResponse: &fwserver.ReadResourceResponse{ NewState: testCurrentState, Private: testEmptyPrivate, - Deferred: &resource.DeferredResponse{Reason: resource.DeferralReasonAbsentPrereq}, + Deferred: &resource.DeferredResponse{Reason: resource.DeferredReasonAbsentPrereq}, }, }, "request-deferral-not-allowed-response-deferral": { @@ -559,7 +559,7 @@ func TestServerReadResource(t *testing.T) { resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - resp.DeferredResponse = &resource.DeferredResponse{Reason: resource.DeferralReasonAbsentPrereq} + resp.DeferredResponse = &resource.DeferredResponse{Reason: resource.DeferredReasonAbsentPrereq} if data.TestRequired.ValueString() != "test-currentstate-value" { resp.Diagnostics.AddError("unexpected req.State value: %s", data.TestRequired.ValueString()) @@ -578,7 +578,7 @@ func TestServerReadResource(t *testing.T) { }, NewState: testCurrentState, Private: testEmptyPrivate, - Deferred: &resource.DeferredResponse{Reason: resource.DeferralReasonAbsentPrereq}, + Deferred: &resource.DeferredResponse{Reason: resource.DeferredReasonAbsentPrereq}, }, }, } diff --git a/internal/toproto5/planresourcechange_test.go b/internal/toproto5/planresourcechange_test.go index 72b707a69..bf0ddfff4 100644 --- a/internal/toproto5/planresourcechange_test.go +++ b/internal/toproto5/planresourcechange_test.go @@ -71,7 +71,7 @@ func TestPlanResourceChangeResponse(t *testing.T) { testEmptyProviderData := privatestate.EmptyProviderData(context.Background()) testDeferred := &resource.DeferredResponse{ - Reason: resource.DeferralReasonAbsentPrereq, + Reason: resource.DeferredReasonAbsentPrereq, } testProto5Deferred := &tfprotov5.Deferred{ diff --git a/internal/toproto5/readresource_test.go b/internal/toproto5/readresource_test.go index 65865d76c..f9b3dce77 100644 --- a/internal/toproto5/readresource_test.go +++ b/internal/toproto5/readresource_test.go @@ -70,7 +70,7 @@ func TestReadResourceResponse(t *testing.T) { } testDeferral := &resource.DeferredResponse{ - Reason: resource.DeferralReasonAbsentPrereq, + Reason: resource.DeferredReasonAbsentPrereq, } testProto5Deferred := &tfprotov5.Deferred{ diff --git a/internal/toproto6/planresourcechange_test.go b/internal/toproto6/planresourcechange_test.go index 50f17071a..d94f9c148 100644 --- a/internal/toproto6/planresourcechange_test.go +++ b/internal/toproto6/planresourcechange_test.go @@ -71,7 +71,7 @@ func TestPlanResourceChangeResponse(t *testing.T) { testEmptyProviderData := privatestate.EmptyProviderData(context.Background()) testDeferred := &resource.DeferredResponse{ - Reason: resource.DeferralReasonAbsentPrereq, + Reason: resource.DeferredReasonAbsentPrereq, } testProto6Deferred := &tfprotov6.Deferred{ diff --git a/internal/toproto6/readresource_test.go b/internal/toproto6/readresource_test.go index 49f734269..cfd856c7b 100644 --- a/internal/toproto6/readresource_test.go +++ b/internal/toproto6/readresource_test.go @@ -70,7 +70,7 @@ func TestReadResourceResponse(t *testing.T) { testEmptyProviderData := privatestate.EmptyProviderData(context.Background()) testDeferral := &resource.DeferredResponse{ - Reason: resource.DeferralReasonAbsentPrereq, + Reason: resource.DeferredReasonAbsentPrereq, } testProto6Deferred := &tfprotov6.Deferred{ diff --git a/resource/deferral.go b/resource/deferral.go deleted file mode 100644 index 640f31bee..000000000 --- a/resource/deferral.go +++ /dev/null @@ -1,28 +0,0 @@ -package resource - -const ( - DeferralReasonUnknown DeferredReason = 0 - DeferralReasonResourceConfigUnknown DeferredReason = 1 - DeferralReasonProviderConfigUnknown DeferredReason = 2 - DeferralReasonAbsentPrereq DeferredReason = 3 -) - -type DeferredResponse struct { - Reason DeferredReason -} - -type DeferredReason int32 - -func (d DeferredReason) String() string { - switch d { - case 0: - return "Unknown" - case 1: - return "Resource Config Unknown" - case 2: - return "Provider Config Unknown" - case 3: - return "Absent Prerequisite" - } - return "Unknown" -} diff --git a/resource/deferred.go b/resource/deferred.go new file mode 100644 index 000000000..f677e7b86 --- /dev/null +++ b/resource/deferred.go @@ -0,0 +1,41 @@ +package resource + +const ( + // DeferredReasonUnknown is used to indicate an invalid `DeferredReason`. + // Provider developers should not use it. + DeferredReasonUnknown DeferredReason = 0 + + // DeferredReasonResourceConfigUnknown is used to indicate that the resource configuration + // is partially unknown and the real values need to be known before the change can be planned. + DeferredReasonResourceConfigUnknown DeferredReason = 1 + + // DeferredReasonProviderConfigUnknown is used to indicate that the provider configuration + // is partially unknown and the real values need to be known before the change can be planned. + DeferredReasonProviderConfigUnknown DeferredReason = 2 + + // DeferredReasonAbsentPrereq is used to indicate that a hard dependency has not been satisfied. + DeferredReasonAbsentPrereq DeferredReason = 3 +) + +// DeferredResponse is used to indicate to Terraform that a change needs to be deferred for a reason. +type DeferredResponse struct { + // Reason is the reason for deferring the change. + Reason DeferredReason +} + +// DeferredReason represents different reasons for deferring a change. +type DeferredReason int32 + +func (d DeferredReason) String() string { + switch d { + case 0: + return "Unknown" + case 1: + return "Resource Config Unknown" + case 2: + return "Provider Config Unknown" + case 3: + return "Absent Prerequisite" + } + return "Unknown" +} From ce1d14b63c9f3d2c1ad12a8e2da86550965192c6 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Tue, 7 May 2024 16:40:19 -0400 Subject: [PATCH 07/25] Implement manual deferred action support for data sources --- datasource/deferred.go | 41 ++++++++++ datasource/read.go | 20 +++++ internal/fromproto5/readdatasource.go | 9 ++- internal/fromproto5/readdatasource_test.go | 20 ++++- internal/fromproto6/readdatasource.go | 9 ++- internal/fromproto6/readdatasource_test.go | 19 ++++- internal/fwserver/server_readdatasource.go | 23 +++++- .../fwserver/server_readdatasource_test.go | 74 ++++++++++++++++++- internal/toproto5/readdatasource.go | 9 ++- internal/toproto5/readdatasource_test.go | 22 +++++- internal/toproto6/readdatasource.go | 9 ++- internal/toproto6/readdatasource_test.go | 22 +++++- resource/read.go | 2 +- 13 files changed, 261 insertions(+), 18 deletions(-) create mode 100644 datasource/deferred.go diff --git a/datasource/deferred.go b/datasource/deferred.go new file mode 100644 index 000000000..8e5f275ba --- /dev/null +++ b/datasource/deferred.go @@ -0,0 +1,41 @@ +package datasource + +const ( + // DeferredReasonUnknown is used to indicate an invalid `DeferredReason`. + // Provider developers should not use it. + DeferredReasonUnknown DeferredReason = 0 + + // DeferredReasonResourceConfigUnknown is used to indicate that the resource configuration + // is partially unknown and the real values need to be known before the change can be planned. + DeferredReasonResourceConfigUnknown DeferredReason = 1 + + // DeferredReasonProviderConfigUnknown is used to indicate that the provider configuration + // is partially unknown and the real values need to be known before the change can be planned. + DeferredReasonProviderConfigUnknown DeferredReason = 2 + + // DeferredReasonAbsentPrereq is used to indicate that a hard dependency has not been satisfied. + DeferredReasonAbsentPrereq DeferredReason = 3 +) + +// DeferredResponse is used to indicate to Terraform that a change needs to be deferred for a reason. +type DeferredResponse struct { + // Reason is the reason for deferring the change. + Reason DeferredReason +} + +// DeferredReason represents different reasons for deferring a change. +type DeferredReason int32 + +func (d DeferredReason) String() string { + switch d { + case 0: + return "Unknown" + case 1: + return "Resource Config Unknown" + case 2: + return "Provider Config Unknown" + case 3: + return "Absent Prerequisite" + } + return "Unknown" +} diff --git a/datasource/read.go b/datasource/read.go index 07e28cab1..6a84cae4b 100644 --- a/datasource/read.go +++ b/datasource/read.go @@ -8,6 +8,15 @@ import ( "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) +// ReadClientCapabilities allows Terraform to publish information +// regarding optionally supported protocol features for the ReadDataSource RPC, +// such as forward-compatible Terraform behavior changes. +type ReadClientCapabilities struct { + // DeferralAllowed signals that the request from Terraform is able to + // handle deferred responses from the provider. + DeferralAllowed bool +} + // ReadRequest represents a request for the provider to read a data // source, i.e., update values in state according to the real state of the // data source. An instance of this request struct is supplied as an argument @@ -22,6 +31,10 @@ type ReadRequest struct { // ProviderMeta is metadata from the provider_meta block of the module. ProviderMeta tfsdk.Config + + // ClientCapabilities defines optionally supported protocol features for the + // ReadDataSource RPC, such as forward-compatible Terraform behavior changes. + ClientCapabilities *ReadClientCapabilities } // ReadResponse represents a response to a ReadRequest. An @@ -37,4 +50,11 @@ type ReadResponse struct { // source. An empty slice indicates a successful operation with no // warnings or errors generated. Diagnostics diag.Diagnostics + + // DeferredResponse indicates that Terraform should defer + // importing this resource. + // + // This field can only be set if + // `(datasource.ReadRequest.ReadClientCapabilities).DeferralAllowed` is true. + DeferredResponse *DeferredResponse } diff --git a/internal/fromproto5/readdatasource.go b/internal/fromproto5/readdatasource.go index 53ac543a9..105ffad67 100644 --- a/internal/fromproto5/readdatasource.go +++ b/internal/fromproto5/readdatasource.go @@ -6,11 +6,12 @@ package fromproto5 import ( "context" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" - "github.com/hashicorp/terraform-plugin-go/tfprotov5" ) // ReadDataSourceRequest returns the *fwserver.ReadDataSourceRequest @@ -53,5 +54,11 @@ func ReadDataSourceRequest(ctx context.Context, proto5 *tfprotov5.ReadDataSource fw.ProviderMeta = providerMeta + if proto5.ClientCapabilities != nil { + fw.ClientCapabilities = &datasource.ReadClientCapabilities{ + DeferralAllowed: proto5.ClientCapabilities.DeferralAllowed, + } + } + return fw, diags } diff --git a/internal/fromproto5/readdatasource_test.go b/internal/fromproto5/readdatasource_test.go index 79df02ea8..b5bdfca53 100644 --- a/internal/fromproto5/readdatasource_test.go +++ b/internal/fromproto5/readdatasource_test.go @@ -1,4 +1,5 @@ // Copyright (c) HashiCorp, Inc. + // SPDX-License-Identifier: MPL-2.0 package fromproto5_test @@ -8,6 +9,9 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/diag" @@ -15,8 +19,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - "github.com/hashicorp/terraform-plugin-go/tfprotov5" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestReadDataSourceRequest(t *testing.T) { @@ -46,6 +48,8 @@ func TestReadDataSourceRequest(t *testing.T) { }, } + testClientCapabilities := tfprotov5.ReadDataSourceClientCapabilities{DeferralAllowed: true} + testCases := map[string]struct { input *tfprotov5.ReadDataSourceRequest dataSourceSchema fwschema.Schema @@ -135,6 +139,18 @@ func TestReadDataSourceRequest(t *testing.T) { }, }, }, + "client-capabilities": { + input: &tfprotov5.ReadDataSourceRequest{ + ClientCapabilities: &testClientCapabilities, + }, + dataSourceSchema: testFwSchema, + expected: &fwserver.ReadDataSourceRequest{ + DataSourceSchema: testFwSchema, + ClientCapabilities: &datasource.ReadClientCapabilities{ + DeferralAllowed: true, + }, + }, + }, } for name, testCase := range testCases { diff --git a/internal/fromproto6/readdatasource.go b/internal/fromproto6/readdatasource.go index 83c264cd7..edd540688 100644 --- a/internal/fromproto6/readdatasource.go +++ b/internal/fromproto6/readdatasource.go @@ -6,11 +6,12 @@ package fromproto6 import ( "context" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" - "github.com/hashicorp/terraform-plugin-go/tfprotov6" ) // ReadDataSourceRequest returns the *fwserver.ReadDataSourceRequest @@ -53,5 +54,11 @@ func ReadDataSourceRequest(ctx context.Context, proto6 *tfprotov6.ReadDataSource fw.ProviderMeta = providerMeta + if proto6.ClientCapabilities != nil { + fw.ClientCapabilities = &datasource.ReadClientCapabilities{ + DeferralAllowed: proto6.ClientCapabilities.DeferralAllowed, + } + } + return fw, diags } diff --git a/internal/fromproto6/readdatasource_test.go b/internal/fromproto6/readdatasource_test.go index b3f996717..73332b45a 100644 --- a/internal/fromproto6/readdatasource_test.go +++ b/internal/fromproto6/readdatasource_test.go @@ -8,6 +8,9 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/diag" @@ -15,8 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - "github.com/hashicorp/terraform-plugin-go/tfprotov6" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestReadDataSourceRequest(t *testing.T) { @@ -46,6 +47,8 @@ func TestReadDataSourceRequest(t *testing.T) { }, } + testClientCapabilities := tfprotov6.ReadDataSourceClientCapabilities{DeferralAllowed: true} + testCases := map[string]struct { input *tfprotov6.ReadDataSourceRequest dataSourceSchema fwschema.Schema @@ -135,6 +138,18 @@ func TestReadDataSourceRequest(t *testing.T) { }, }, }, + "client-capabilities": { + input: &tfprotov6.ReadDataSourceRequest{ + ClientCapabilities: &testClientCapabilities, + }, + dataSourceSchema: testFwSchema, + expected: &fwserver.ReadDataSourceRequest{ + DataSourceSchema: testFwSchema, + ClientCapabilities: &datasource.ReadClientCapabilities{ + DeferralAllowed: true, + }, + }, + }, } for name, testCase := range testCases { diff --git a/internal/fwserver/server_readdatasource.go b/internal/fwserver/server_readdatasource.go index a95cd35dc..b08d3caba 100644 --- a/internal/fwserver/server_readdatasource.go +++ b/internal/fwserver/server_readdatasource.go @@ -17,15 +17,17 @@ import ( // ReadDataSourceRequest is the framework server request for the // ReadDataSource RPC. type ReadDataSourceRequest struct { - Config *tfsdk.Config - DataSourceSchema fwschema.Schema - DataSource datasource.DataSource - ProviderMeta *tfsdk.Config + ClientCapabilities *datasource.ReadClientCapabilities + Config *tfsdk.Config + DataSourceSchema fwschema.Schema + DataSource datasource.DataSource + ProviderMeta *tfsdk.Config } // ReadDataSourceResponse is the framework server response for the // ReadDataSource RPC. type ReadDataSourceResponse struct { + Deferred *datasource.DeferredResponse Diagnostics diag.Diagnostics State *tfsdk.State } @@ -75,17 +77,30 @@ func (s *Server) ReadDataSource(ctx context.Context, req *ReadDataSourceRequest, readReq.ProviderMeta = *req.ProviderMeta } + readReq.ClientCapabilities = req.ClientCapabilities + logging.FrameworkTrace(ctx, "Calling provider defined DataSource Read") req.DataSource.Read(ctx, readReq, &readResp) logging.FrameworkTrace(ctx, "Called provider defined DataSource Read") resp.Diagnostics = readResp.Diagnostics resp.State = &readResp.State + resp.Deferred = readResp.DeferredResponse if resp.Diagnostics.HasError() { return } + if (req.ClientCapabilities == nil || !req.ClientCapabilities.DeferralAllowed) && resp.Deferred != nil { + resp.Diagnostics.AddError( + "Data Source Deferral Not Allowed", + "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ + "The resource requested a deferral but the Terraform client does not support deferrals, "+ + "datasource.DeferredResponse can only be set if datasource.ReadRequest.ReadClientCapabilities.DeferralAllowed is true.", + ) + return + } + semanticEqualityReq := SchemaSemanticEqualityRequest{ PriorData: fwschemadata.Data{ Description: fwschemadata.DataDescriptionConfiguration, diff --git a/internal/fwserver/server_readdatasource_test.go b/internal/fwserver/server_readdatasource_test.go index d831ef212..663f5ea77 100644 --- a/internal/fwserver/server_readdatasource_test.go +++ b/internal/fwserver/server_readdatasource_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/diag" @@ -17,7 +19,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestServerReadDataSource(t *testing.T) { @@ -98,6 +99,10 @@ func TestServerReadDataSource(t *testing.T) { Schema: testSchema, } + testDeferralAllowed := &datasource.ReadClientCapabilities{ + DeferralAllowed: true, + } + testCases := map[string]struct { server *fwserver.Server request *fwserver.ReadDataSourceRequest @@ -349,6 +354,73 @@ func TestServerReadDataSource(t *testing.T) { }, }, }, + "request-deferral-allowed-response-deferral": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ReadDataSourceRequest{ + Config: testConfig, + DataSourceSchema: testSchema, + DataSource: &testprovider.DataSource{ + ReadMethod: func(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var config struct { + TestComputed types.String `tfsdk:"test_computed"` + TestRequired types.String `tfsdk:"test_required"` + } + + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + + resp.DeferredResponse = &datasource.DeferredResponse{Reason: datasource.DeferredReasonAbsentPrereq} + + if config.TestRequired.ValueString() != "test-config-value" { + resp.Diagnostics.AddError("unexpected req.Config value: %s", config.TestRequired.ValueString()) + } + }, + }, + ClientCapabilities: testDeferralAllowed, + }, + expectedResponse: &fwserver.ReadDataSourceResponse{ + State: testStateUnchanged, + Deferred: &datasource.DeferredResponse{Reason: datasource.DeferredReasonAbsentPrereq}, + }, + }, + "request-deferral-not-allowed-response-deferral": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ReadDataSourceRequest{ + Config: testConfig, + DataSourceSchema: testSchema, + DataSource: &testprovider.DataSource{ + ReadMethod: func(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var config struct { + TestComputed types.String `tfsdk:"test_computed"` + TestRequired types.String `tfsdk:"test_required"` + } + + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + + resp.DeferredResponse = &datasource.DeferredResponse{Reason: datasource.DeferredReasonAbsentPrereq} + + if config.TestRequired.ValueString() != "test-config-value" { + resp.Diagnostics.AddError("unexpected req.Config value: %s", config.TestRequired.ValueString()) + } + }, + }, + }, + expectedResponse: &fwserver.ReadDataSourceResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Data Source Deferral Not Allowed", + "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ + "The resource requested a deferral but the Terraform client does not support deferrals, "+ + "datasource.DeferredResponse can only be set if datasource.ReadRequest.ReadClientCapabilities.DeferralAllowed is true.", + ), + }, + State: testStateUnchanged, + Deferred: &datasource.DeferredResponse{Reason: datasource.DeferredReasonAbsentPrereq}, + }, + }, } for name, testCase := range testCases { diff --git a/internal/toproto5/readdatasource.go b/internal/toproto5/readdatasource.go index 4e977d1dd..f70e6e186 100644 --- a/internal/toproto5/readdatasource.go +++ b/internal/toproto5/readdatasource.go @@ -6,8 +6,9 @@ package toproto5 import ( "context" - "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-go/tfprotov5" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" ) // ReadDataSourceResponse returns the *tfprotov5.ReadDataSourceResponse @@ -26,5 +27,11 @@ func ReadDataSourceResponse(ctx context.Context, fw *fwserver.ReadDataSourceResp proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) proto5.State = state + if fw.Deferred != nil { + proto5.Deferred = &tfprotov5.Deferred{ + Reason: tfprotov5.DeferredReason(fw.Deferred.Reason), + } + } + return proto5 } diff --git a/internal/toproto5/readdatasource_test.go b/internal/toproto5/readdatasource_test.go index 691e2e896..40e42d131 100644 --- a/internal/toproto5/readdatasource_test.go +++ b/internal/toproto5/readdatasource_test.go @@ -8,13 +8,15 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/internal/toproto5" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - "github.com/hashicorp/terraform-plugin-go/tfprotov5" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestReadDataSourceResponse(t *testing.T) { @@ -32,6 +34,14 @@ func TestReadDataSourceResponse(t *testing.T) { testProto5DynamicValue, err := tfprotov5.NewDynamicValue(testProto5Type, testProto5Value) + testDeferral := &datasource.DeferredResponse{ + Reason: datasource.DeferredReasonAbsentPrereq, + } + + testProto5Deferred := &tfprotov5.Deferred{ + Reason: tfprotov5.DeferredReasonAbsentPrereq, + } + if err != nil { t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err) } @@ -131,6 +141,14 @@ func TestReadDataSourceResponse(t *testing.T) { State: &testProto5DynamicValue, }, }, + "deferral": { + input: &fwserver.ReadDataSourceResponse{ + Deferred: testDeferral, + }, + expected: &tfprotov5.ReadDataSourceResponse{ + Deferred: testProto5Deferred, + }, + }, } for name, testCase := range testCases { diff --git a/internal/toproto6/readdatasource.go b/internal/toproto6/readdatasource.go index f9a59f0e5..0f1738625 100644 --- a/internal/toproto6/readdatasource.go +++ b/internal/toproto6/readdatasource.go @@ -6,8 +6,9 @@ package toproto6 import ( "context" - "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" ) // ReadDataSourceResponse returns the *tfprotov6.ReadDataSourceResponse @@ -26,5 +27,11 @@ func ReadDataSourceResponse(ctx context.Context, fw *fwserver.ReadDataSourceResp proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) proto6.State = state + if fw.Deferred != nil { + proto6.Deferred = &tfprotov6.Deferred{ + Reason: tfprotov6.DeferredReason(fw.Deferred.Reason), + } + } + return proto6 } diff --git a/internal/toproto6/readdatasource_test.go b/internal/toproto6/readdatasource_test.go index f143ba56d..fb1ae4dd5 100644 --- a/internal/toproto6/readdatasource_test.go +++ b/internal/toproto6/readdatasource_test.go @@ -8,13 +8,15 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/internal/toproto6" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - "github.com/hashicorp/terraform-plugin-go/tfprotov6" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestReadDataSourceResponse(t *testing.T) { @@ -32,6 +34,14 @@ func TestReadDataSourceResponse(t *testing.T) { testProto6DynamicValue, err := tfprotov6.NewDynamicValue(testProto6Type, testProto6Value) + testDeferral := &datasource.DeferredResponse{ + Reason: datasource.DeferredReasonAbsentPrereq, + } + + testProto6Deferred := &tfprotov6.Deferred{ + Reason: tfprotov6.DeferredReasonAbsentPrereq, + } + if err != nil { t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err) } @@ -131,6 +141,14 @@ func TestReadDataSourceResponse(t *testing.T) { State: &testProto6DynamicValue, }, }, + "deferral": { + input: &fwserver.ReadDataSourceResponse{ + Deferred: testDeferral, + }, + expected: &tfprotov6.ReadDataSourceResponse{ + Deferred: testProto6Deferred, + }, + }, } for name, testCase := range testCases { diff --git a/resource/read.go b/resource/read.go index df26d98a4..2255e06aa 100644 --- a/resource/read.go +++ b/resource/read.go @@ -65,6 +65,6 @@ type ReadResponse struct { // importing this resource. // // This field can only be set if - // `(resource.ReadRequest.ReadStateClientCapabilities).DeferralAllowed` is true. + // `(resource.ReadRequest.ReadClientCapabilities).DeferralAllowed` is true. DeferredResponse *DeferredResponse } From 39c4bb5c58a176a2b2f54ea1423933ceb6d9ea37 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Tue, 7 May 2024 17:05:08 -0400 Subject: [PATCH 08/25] Update documentation and diagnostic messages --- datasource/read.go | 4 ++-- internal/fwserver/server_importresourcestate.go | 2 +- internal/fwserver/server_importresourcestate_test.go | 2 +- internal/fwserver/server_planresourcechange.go | 2 +- internal/fwserver/server_planresourcechange_test.go | 2 +- internal/fwserver/server_readdatasource.go | 2 +- internal/fwserver/server_readdatasource_test.go | 2 +- internal/fwserver/server_readresource.go | 2 +- internal/fwserver/server_readresource_test.go | 2 +- resource/import_state.go | 7 +++++-- resource/modify_plan.go | 7 +++++-- resource/read.go | 7 +++++-- 12 files changed, 25 insertions(+), 16 deletions(-) diff --git a/datasource/read.go b/datasource/read.go index 6a84cae4b..12ac76c1f 100644 --- a/datasource/read.go +++ b/datasource/read.go @@ -12,8 +12,8 @@ import ( // regarding optionally supported protocol features for the ReadDataSource RPC, // such as forward-compatible Terraform behavior changes. type ReadClientCapabilities struct { - // DeferralAllowed signals that the request from Terraform is able to - // handle deferred responses from the provider. + // DeferralAllowed indicates whether the Terraform client initiating + // the request allows a deferral response. DeferralAllowed bool } diff --git a/internal/fwserver/server_importresourcestate.go b/internal/fwserver/server_importresourcestate.go index 307ccd920..211060b01 100644 --- a/internal/fwserver/server_importresourcestate.go +++ b/internal/fwserver/server_importresourcestate.go @@ -123,7 +123,7 @@ func (s *Server) ImportResourceState(ctx context.Context, req *ImportResourceSta "Resource Import Deferral Not Allowed", "An unexpected error was encountered when importing the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "resource.DeferredResponse can only be set if resource.ImportStateRequest.ImportStateClientCapabilities.DeferralAllowed is true.", + "(resource.ImportStateResponse).DeferredResponse can only be set if (resource.ImportStateRequest.ClientCapabilities).DeferralAllowed is true.", ) return } diff --git a/internal/fwserver/server_importresourcestate_test.go b/internal/fwserver/server_importresourcestate_test.go index 492b0ba3c..7b0177837 100644 --- a/internal/fwserver/server_importresourcestate_test.go +++ b/internal/fwserver/server_importresourcestate_test.go @@ -369,7 +369,7 @@ func TestServerImportResourceState(t *testing.T) { "Resource Import Deferral Not Allowed", "An unexpected error was encountered when importing the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "resource.DeferredResponse can only be set if resource.ImportStateRequest.ImportStateClientCapabilities.DeferralAllowed is true.", + "(resource.ImportStateResponse).DeferredResponse can only be set if (resource.ImportStateRequest.ClientCapabilities).DeferralAllowed is true.", ), }, }, diff --git a/internal/fwserver/server_planresourcechange.go b/internal/fwserver/server_planresourcechange.go index dc469d449..f1f2b1413 100644 --- a/internal/fwserver/server_planresourcechange.go +++ b/internal/fwserver/server_planresourcechange.go @@ -289,7 +289,7 @@ func (s *Server) PlanResourceChange(ctx context.Context, req *PlanResourceChange "Resource Deferral Not Allowed", "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "(*resource.ModifyPlanResponse).DeferredResponse can only be set if (resource.ModifyPlanRequest).ClientCapabilities.DeferralAllowed is true.", + "(resource.ModifyPlanResponse).DeferredResponse can only be set if (resource.ModifyPlanRequest.ClientCapabilities.DeferralAllowed is true.", ) return } diff --git a/internal/fwserver/server_planresourcechange_test.go b/internal/fwserver/server_planresourcechange_test.go index 78d6b050e..2d24da5af 100644 --- a/internal/fwserver/server_planresourcechange_test.go +++ b/internal/fwserver/server_planresourcechange_test.go @@ -6095,7 +6095,7 @@ func TestServerPlanResourceChange(t *testing.T) { "Resource Deferral Not Allowed", "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "(*resource.ModifyPlanResponse).DeferredResponse can only be set if (resource.ModifyPlanRequest).ClientCapabilities.DeferralAllowed is true.", + "(resource.ModifyPlanResponse).DeferredResponse can only be set if (resource.ModifyPlanRequest.ClientCapabilities.DeferralAllowed is true.", ), }, PlannedState: &tfsdk.State{ diff --git a/internal/fwserver/server_readdatasource.go b/internal/fwserver/server_readdatasource.go index b08d3caba..15693e42d 100644 --- a/internal/fwserver/server_readdatasource.go +++ b/internal/fwserver/server_readdatasource.go @@ -96,7 +96,7 @@ func (s *Server) ReadDataSource(ctx context.Context, req *ReadDataSourceRequest, "Data Source Deferral Not Allowed", "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "datasource.DeferredResponse can only be set if datasource.ReadRequest.ReadClientCapabilities.DeferralAllowed is true.", + "(datasource.ReadResponse).DeferredResponse can only be set if (datasource.ReadRequest.ClientCapabilities).DeferralAllowed is true.", ) return } diff --git a/internal/fwserver/server_readdatasource_test.go b/internal/fwserver/server_readdatasource_test.go index 663f5ea77..6ad360b5d 100644 --- a/internal/fwserver/server_readdatasource_test.go +++ b/internal/fwserver/server_readdatasource_test.go @@ -414,7 +414,7 @@ func TestServerReadDataSource(t *testing.T) { "Data Source Deferral Not Allowed", "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "datasource.DeferredResponse can only be set if datasource.ReadRequest.ReadClientCapabilities.DeferralAllowed is true.", + "(datasource.ReadResponse).DeferredResponse can only be set if (datasource.ReadRequest.ClientCapabilities).DeferralAllowed is true.", ), }, State: testStateUnchanged, diff --git a/internal/fwserver/server_readresource.go b/internal/fwserver/server_readresource.go index 7b7831a9f..1381e2f27 100644 --- a/internal/fwserver/server_readresource.go +++ b/internal/fwserver/server_readresource.go @@ -126,7 +126,7 @@ func (s *Server) ReadResource(ctx context.Context, req *ReadResourceRequest, res "Resource Deferral Not Allowed", "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "resource.DeferredResponse can only be set if resource.ReadRequest.ReadClientCapabilities.DeferralAllowed is true.", + "(resource.ReadResponse).DeferredResponse can only be set if (resource.ReadRequest.ClientCapabilities).DeferralAllowed is true.", ) return } diff --git a/internal/fwserver/server_readresource_test.go b/internal/fwserver/server_readresource_test.go index d7f4e9e9d..2708ad22a 100644 --- a/internal/fwserver/server_readresource_test.go +++ b/internal/fwserver/server_readresource_test.go @@ -573,7 +573,7 @@ func TestServerReadResource(t *testing.T) { "Resource Deferral Not Allowed", "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "resource.DeferredResponse can only be set if resource.ReadRequest.ReadClientCapabilities.DeferralAllowed is true.", + "(resource.ReadResponse).DeferredResponse can only be set if (resource.ReadRequest.ClientCapabilities).DeferralAllowed is true.", ), }, NewState: testCurrentState, diff --git a/resource/import_state.go b/resource/import_state.go index 658cc2bb7..58ab58dd4 100644 --- a/resource/import_state.go +++ b/resource/import_state.go @@ -12,7 +12,9 @@ import ( "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) -// TODO: doc +// ImportStateClientCapabilities allows Terraform to publish information +// regarding optionally supported protocol features for the ImportResourceState RPC, +// such as forward-compatible Terraform behavior changes. type ImportStateClientCapabilities struct { // DeferralAllowed indicates whether the Terraform client initiating // the request allows a deferral response. @@ -31,7 +33,8 @@ type ImportStateRequest struct { // is not stored in the state unless the provider explicitly stores it. ID string - //TODO: doc + // ClientCapabilities defines optionally supported protocol features for the + // ImportResourceState RPC, such as forward-compatible Terraform behavior changes. ClientCapabilities *ImportStateClientCapabilities } diff --git a/resource/modify_plan.go b/resource/modify_plan.go index 6f3f563e4..b620677ee 100644 --- a/resource/modify_plan.go +++ b/resource/modify_plan.go @@ -10,7 +10,9 @@ import ( "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) -// TODO: doc +// ModifyPlanClientCapabilities allows Terraform to publish information +// regarding optionally supported protocol features for the PlanResourceChange RPC, +// such as forward-compatible Terraform behavior changes. type ModifyPlanClientCapabilities struct { // DeferralAllowed indicates whether the Terraform client initiating // the request allows a deferral response. @@ -47,7 +49,8 @@ type ModifyPlanRequest struct { // ModifyPlanResponse.Private to update or remove a value. Private *privatestate.ProviderData - //TODO: doc + // ClientCapabilities defines optionally supported protocol features for the + // PlanResourceChange RPC, such as forward-compatible Terraform behavior changes. ClientCapabilities *ModifyPlanClientCapabilities } diff --git a/resource/read.go b/resource/read.go index 2255e06aa..4a1182a76 100644 --- a/resource/read.go +++ b/resource/read.go @@ -9,7 +9,9 @@ import ( "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) -// TODO: doc +// ReadClientCapabilities allows Terraform to publish information +// regarding optionally supported protocol features for the ReadResource RPC, +// such as forward-compatible Terraform behavior changes. type ReadClientCapabilities struct { // DeferralAllowed indicates whether the Terraform client initiating // the request allows a deferral response. @@ -37,7 +39,8 @@ type ReadRequest struct { // ProviderMeta is metadata from the provider_meta block of the module. ProviderMeta tfsdk.Config - //TODO: doc + // ClientCapabilities defines optionally supported protocol features for the + // ReadResource RPC, such as forward-compatible Terraform behavior changes. ClientCapabilities *ReadClientCapabilities } From 3a5a8c30ade0dd4210f4ec4c6c969179359d940a Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Tue, 7 May 2024 17:22:29 -0400 Subject: [PATCH 09/25] Add copyright headers --- datasource/deferred.go | 3 +++ resource/deferred.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/datasource/deferred.go b/datasource/deferred.go index 8e5f275ba..f92eb852e 100644 --- a/datasource/deferred.go +++ b/datasource/deferred.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package datasource const ( diff --git a/resource/deferred.go b/resource/deferred.go index f677e7b86..723888b58 100644 --- a/resource/deferred.go +++ b/resource/deferred.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package resource const ( From 04042c2b41e2eaf21caf9e77d1486ea979cc3a1c Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Wed, 8 May 2024 11:12:06 -0400 Subject: [PATCH 10/25] Add changelog entries --- .changes/unreleased/FEATURES-20240508-105105.yaml | 6 ++++++ .changes/unreleased/FEATURES-20240508-105141.yaml | 6 ++++++ .changes/unreleased/FEATURES-20240508-110715.yaml | 6 ++++++ .changes/unreleased/FEATURES-20240508-111023.yaml | 6 ++++++ 4 files changed, 24 insertions(+) create mode 100644 .changes/unreleased/FEATURES-20240508-105105.yaml create mode 100644 .changes/unreleased/FEATURES-20240508-105141.yaml create mode 100644 .changes/unreleased/FEATURES-20240508-110715.yaml create mode 100644 .changes/unreleased/FEATURES-20240508-111023.yaml diff --git a/.changes/unreleased/FEATURES-20240508-105105.yaml b/.changes/unreleased/FEATURES-20240508-105105.yaml new file mode 100644 index 000000000..9b1c418bb --- /dev/null +++ b/.changes/unreleased/FEATURES-20240508-105105.yaml @@ -0,0 +1,6 @@ +kind: FEATURES +body: 'resource: Add `DeferredResponse` field to `ReadResponse`, `ModifyPlanResponse`, and `ImportStateResponse` + which indicates a resource deferred action to the Terraform client' +time: 2024-05-08T10:51:05.90518-04:00 +custom: + Issue: "999" diff --git a/.changes/unreleased/FEATURES-20240508-105141.yaml b/.changes/unreleased/FEATURES-20240508-105141.yaml new file mode 100644 index 000000000..62ab936f6 --- /dev/null +++ b/.changes/unreleased/FEATURES-20240508-105141.yaml @@ -0,0 +1,6 @@ +kind: FEATURES +body: 'datasource: Add `DeferredResponse` field to `ReadResponse` which indicates a data source deferred action + to the Terraform client' +time: 2024-05-08T10:51:41.663935-04:00 +custom: + Issue: "999" diff --git a/.changes/unreleased/FEATURES-20240508-110715.yaml b/.changes/unreleased/FEATURES-20240508-110715.yaml new file mode 100644 index 000000000..15f79b2de --- /dev/null +++ b/.changes/unreleased/FEATURES-20240508-110715.yaml @@ -0,0 +1,6 @@ +kind: FEATURES +body: 'resource: Add `ClientCapabilities` field to `ReadRequest`, `ModifyPlanRequest`, and `ImportStateRequest` + which specifies optionally supported protocol features for the Terraform client' +time: 2024-05-08T11:07:15.955126-04:00 +custom: + Issue: "999" diff --git a/.changes/unreleased/FEATURES-20240508-111023.yaml b/.changes/unreleased/FEATURES-20240508-111023.yaml new file mode 100644 index 000000000..2007aded5 --- /dev/null +++ b/.changes/unreleased/FEATURES-20240508-111023.yaml @@ -0,0 +1,6 @@ +kind: FEATURES +body: 'datasource: Add `ClientCapabilities` field to `ReadRequest` which specifies + optionally supported protocol features for the Terraform client' +time: 2024-05-08T11:10:23.66584-04:00 +custom: + Issue: "999" From ea6400558d409037ed30c53c695425652ed7e995 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Fri, 10 May 2024 14:29:20 -0400 Subject: [PATCH 11/25] Apply suggestions from code review Co-authored-by: Austin Valle --- datasource/read.go | 4 ++-- internal/fromproto5/readdatasource_test.go | 1 - resource/import_state.go | 2 +- resource/modify_plan.go | 2 +- resource/read.go | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/datasource/read.go b/datasource/read.go index 12ac76c1f..7c7c9a3db 100644 --- a/datasource/read.go +++ b/datasource/read.go @@ -52,9 +52,9 @@ type ReadResponse struct { Diagnostics diag.Diagnostics // DeferredResponse indicates that Terraform should defer - // importing this resource. + // reading this data source. // // This field can only be set if - // `(datasource.ReadRequest.ReadClientCapabilities).DeferralAllowed` is true. + // `(datasource.ReadRequest).ClientCapabilities.DeferralAllowed` is true. DeferredResponse *DeferredResponse } diff --git a/internal/fromproto5/readdatasource_test.go b/internal/fromproto5/readdatasource_test.go index b5bdfca53..a398674da 100644 --- a/internal/fromproto5/readdatasource_test.go +++ b/internal/fromproto5/readdatasource_test.go @@ -1,5 +1,4 @@ // Copyright (c) HashiCorp, Inc. - // SPDX-License-Identifier: MPL-2.0 package fromproto5_test diff --git a/resource/import_state.go b/resource/import_state.go index 58ab58dd4..ae772950d 100644 --- a/resource/import_state.go +++ b/resource/import_state.go @@ -62,7 +62,7 @@ type ImportStateResponse struct { // importing this resource. // // This field can only be set if - // `(resource.ImportStateRequest.ImportStateClientCapabilities).DeferralAllowed` is true. + // `(resource.ImportStateRequest).ClientCapabilities.DeferralAllowed` is true. DeferredResponse *DeferredResponse } diff --git a/resource/modify_plan.go b/resource/modify_plan.go index b620677ee..167387e0c 100644 --- a/resource/modify_plan.go +++ b/resource/modify_plan.go @@ -83,6 +83,6 @@ type ModifyPlanResponse struct { // importing this resource. // // This field can only be set if - // `(resource.ModifyPlanRequest.ModifyPlanClientCapabilities).DeferralAllowed` is true. + // `(resource.ModifyPlanRequest).ClientCapabilities.DeferralAllowed` is true. DeferredResponse *DeferredResponse } diff --git a/resource/read.go b/resource/read.go index 4a1182a76..893b1e68c 100644 --- a/resource/read.go +++ b/resource/read.go @@ -68,6 +68,6 @@ type ReadResponse struct { // importing this resource. // // This field can only be set if - // `(resource.ReadRequest.ReadClientCapabilities).DeferralAllowed` is true. + // `(resource.ReadRequest).ClientCapabilities.DeferralAllowed` is true. DeferredResponse *DeferredResponse } From ac952fceb6e7093422a7e93c3ee20c2cb44826ee Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Fri, 10 May 2024 14:41:13 -0400 Subject: [PATCH 12/25] Add comment and changelog notes to indicate experimental nature of deferred actions. --- .changes/unreleased/NOTES-20240510-143136.yaml | 7 +++++++ datasource/deferred.go | 6 ++++++ datasource/read.go | 6 ++++++ resource/deferred.go | 6 ++++++ resource/import_state.go | 6 ++++++ resource/modify_plan.go | 6 ++++++ resource/read.go | 6 ++++++ 7 files changed, 43 insertions(+) create mode 100644 .changes/unreleased/NOTES-20240510-143136.yaml diff --git a/.changes/unreleased/NOTES-20240510-143136.yaml b/.changes/unreleased/NOTES-20240510-143136.yaml new file mode 100644 index 000000000..d073e42aa --- /dev/null +++ b/.changes/unreleased/NOTES-20240510-143136.yaml @@ -0,0 +1,7 @@ +kind: NOTES +body: This release contains support for deferred actions, which is an experimental + feature only available in prerelease builds of Terraform 1.9 and later. This functionality + is subject to change and is not protected by version compatibility guarantees. +time: 2024-05-10T14:31:36.644869-04:00 +custom: + Issue: "999" diff --git a/datasource/deferred.go b/datasource/deferred.go index f92eb852e..b1b03ee72 100644 --- a/datasource/deferred.go +++ b/datasource/deferred.go @@ -21,12 +21,18 @@ const ( ) // DeferredResponse is used to indicate to Terraform that a change needs to be deferred for a reason. +// +// NOTE: This functionality is related to deferred action support, which is currently experimental and is subject +// to change or break without warning. It is not protected by version compatibility guarantees. type DeferredResponse struct { // Reason is the reason for deferring the change. Reason DeferredReason } // DeferredReason represents different reasons for deferring a change. +// +// NOTE: This functionality is related to deferred action support, which is currently experimental and is subject +// to change or break without warning. It is not protected by version compatibility guarantees. type DeferredReason int32 func (d DeferredReason) String() string { diff --git a/datasource/read.go b/datasource/read.go index 7c7c9a3db..241ff91a8 100644 --- a/datasource/read.go +++ b/datasource/read.go @@ -14,6 +14,9 @@ import ( type ReadClientCapabilities struct { // DeferralAllowed indicates whether the Terraform client initiating // the request allows a deferral response. + // + // NOTE: This functionality is related to deferred action support, which is currently experimental and is subject + // to change or break without warning. It is not protected by version compatibility guarantees. DeferralAllowed bool } @@ -56,5 +59,8 @@ type ReadResponse struct { // // This field can only be set if // `(datasource.ReadRequest).ClientCapabilities.DeferralAllowed` is true. + // + // NOTE: This functionality is related to deferred action support, which is currently experimental and is subject + // to change or break without warning. It is not protected by version compatibility guarantees. DeferredResponse *DeferredResponse } diff --git a/resource/deferred.go b/resource/deferred.go index 723888b58..0009f6872 100644 --- a/resource/deferred.go +++ b/resource/deferred.go @@ -21,12 +21,18 @@ const ( ) // DeferredResponse is used to indicate to Terraform that a change needs to be deferred for a reason. +// +// NOTE: This functionality is related to deferred action support, which is currently experimental and is subject +// to change or break without warning. It is not protected by version compatibility guarantees. type DeferredResponse struct { // Reason is the reason for deferring the change. Reason DeferredReason } // DeferredReason represents different reasons for deferring a change. +// +// NOTE: This functionality is related to deferred action support, which is currently experimental and is subject +// to change or break without warning. It is not protected by version compatibility guarantees. type DeferredReason int32 func (d DeferredReason) String() string { diff --git a/resource/import_state.go b/resource/import_state.go index ae772950d..22a5f7aa2 100644 --- a/resource/import_state.go +++ b/resource/import_state.go @@ -18,6 +18,9 @@ import ( type ImportStateClientCapabilities struct { // DeferralAllowed indicates whether the Terraform client initiating // the request allows a deferral response. + // + // NOTE: This functionality is related to deferred action support, which is currently experimental and is subject + // to change or break without warning. It is not protected by version compatibility guarantees. DeferralAllowed bool } @@ -63,6 +66,9 @@ type ImportStateResponse struct { // // This field can only be set if // `(resource.ImportStateRequest).ClientCapabilities.DeferralAllowed` is true. + // + // NOTE: This functionality is related to deferred action support, which is currently experimental and is subject + // to change or break without warning. It is not protected by version compatibility guarantees. DeferredResponse *DeferredResponse } diff --git a/resource/modify_plan.go b/resource/modify_plan.go index 167387e0c..1cde9acd1 100644 --- a/resource/modify_plan.go +++ b/resource/modify_plan.go @@ -16,6 +16,9 @@ import ( type ModifyPlanClientCapabilities struct { // DeferralAllowed indicates whether the Terraform client initiating // the request allows a deferral response. + // + // NOTE: This functionality is related to deferred action support, which is currently experimental and is subject + // to change or break without warning. It is not protected by version compatibility guarantees. DeferralAllowed bool } @@ -84,5 +87,8 @@ type ModifyPlanResponse struct { // // This field can only be set if // `(resource.ModifyPlanRequest).ClientCapabilities.DeferralAllowed` is true. + // + // NOTE: This functionality is related to deferred action support, which is currently experimental and is subject + // to change or break without warning. It is not protected by version compatibility guarantees. DeferredResponse *DeferredResponse } diff --git a/resource/read.go b/resource/read.go index 893b1e68c..661b9d1bb 100644 --- a/resource/read.go +++ b/resource/read.go @@ -15,6 +15,9 @@ import ( type ReadClientCapabilities struct { // DeferralAllowed indicates whether the Terraform client initiating // the request allows a deferral response. + // + // NOTE: This functionality is related to deferred action support, which is currently experimental and is subject + // to change or break without warning. It is not protected by version compatibility guarantees. DeferralAllowed bool } @@ -69,5 +72,8 @@ type ReadResponse struct { // // This field can only be set if // `(resource.ReadRequest).ClientCapabilities.DeferralAllowed` is true. + // + // NOTE: This functionality is related to deferred action support, which is currently experimental and is subject + // to change or break without warning. It is not protected by version compatibility guarantees. DeferredResponse *DeferredResponse } From bcadaeeb9f5da5062116ebcb1ad44682830b7e0a Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Mon, 13 May 2024 16:02:12 -0400 Subject: [PATCH 13/25] Rename constant to be specific to data sources --- datasource/deferred.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/datasource/deferred.go b/datasource/deferred.go index b1b03ee72..9631c0210 100644 --- a/datasource/deferred.go +++ b/datasource/deferred.go @@ -8,9 +8,9 @@ const ( // Provider developers should not use it. DeferredReasonUnknown DeferredReason = 0 - // DeferredReasonResourceConfigUnknown is used to indicate that the resource configuration + // DeferredReasonDataSourceConfigUnknown is used to indicate that the resource configuration // is partially unknown and the real values need to be known before the change can be planned. - DeferredReasonResourceConfigUnknown DeferredReason = 1 + DeferredReasonDataSourceConfigUnknown DeferredReason = 1 // DeferredReasonProviderConfigUnknown is used to indicate that the provider configuration // is partially unknown and the real values need to be known before the change can be planned. @@ -40,7 +40,7 @@ func (d DeferredReason) String() string { case 0: return "Unknown" case 1: - return "Resource Config Unknown" + return "Data Source Config Unknown" case 2: return "Provider Config Unknown" case 3: From 3777d5d0096250acdb97f77f6adce078998ef175 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Mon, 13 May 2024 16:06:18 -0400 Subject: [PATCH 14/25] Remove TODO comment --- internal/fwserver/server_importresourcestate.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/fwserver/server_importresourcestate.go b/internal/fwserver/server_importresourcestate.go index 211060b01..d6de73851 100644 --- a/internal/fwserver/server_importresourcestate.go +++ b/internal/fwserver/server_importresourcestate.go @@ -36,7 +36,6 @@ type ImportResourceStateRequest struct { // the ImportedResource TypeName of the ImportResourceStateResponse. TypeName string - //TODO: doc ClientCapabilities *resource.ImportStateClientCapabilities } From 0c4891542173b5993939fd3d7a35b100366ab63d Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Mon, 13 May 2024 16:08:12 -0400 Subject: [PATCH 15/25] Remove unnecessary nil check --- internal/fwserver/server_importresourcestate.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/fwserver/server_importresourcestate.go b/internal/fwserver/server_importresourcestate.go index d6de73851..528587d72 100644 --- a/internal/fwserver/server_importresourcestate.go +++ b/internal/fwserver/server_importresourcestate.go @@ -142,10 +142,7 @@ func (s *Server) ImportResourceState(ctx context.Context, req *ImportResourceSta private.Provider = importResp.Private } - if importResp.DeferredResponse != nil { - resp.Deferred = importResp.DeferredResponse - } - + resp.Deferred = importResp.DeferredResponse resp.ImportedResources = []ImportedResource{ { State: importResp.State, From 95ef72ee9037867e44b622879b0af1cf03360ddc Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Mon, 13 May 2024 16:54:51 -0400 Subject: [PATCH 16/25] Add default values for `ClientCapabilities` request fields --- datasource/read.go | 2 +- internal/fromproto5/client_capabilities.go | 56 +++++++++++++++++++ internal/fromproto5/importresourcestate.go | 13 ++--- internal/fromproto5/planresourcechange.go | 11 +--- internal/fromproto5/readdatasource.go | 11 +--- internal/fromproto5/readresource.go | 9 +-- internal/fromproto6/client_capabilities.go | 56 +++++++++++++++++++ internal/fromproto6/importresourcestate.go | 13 ++--- internal/fromproto6/planresourcechange.go | 11 +--- internal/fromproto6/readdatasource.go | 11 +--- internal/fromproto6/readresource.go | 9 +-- .../fwserver/server_importresourcestate.go | 9 ++- .../fwserver/server_planresourcechange.go | 15 +++-- internal/fwserver/server_readdatasource.go | 4 +- internal/fwserver/server_readresource.go | 6 +- resource/import_state.go | 2 +- resource/modify_plan.go | 2 +- resource/read.go | 2 +- 18 files changed, 162 insertions(+), 80 deletions(-) create mode 100644 internal/fromproto5/client_capabilities.go create mode 100644 internal/fromproto6/client_capabilities.go diff --git a/datasource/read.go b/datasource/read.go index 241ff91a8..496cd2bd8 100644 --- a/datasource/read.go +++ b/datasource/read.go @@ -37,7 +37,7 @@ type ReadRequest struct { // ClientCapabilities defines optionally supported protocol features for the // ReadDataSource RPC, such as forward-compatible Terraform behavior changes. - ClientCapabilities *ReadClientCapabilities + ClientCapabilities ReadClientCapabilities } // ReadResponse represents a response to a ReadRequest. An diff --git a/internal/fromproto5/client_capabilities.go b/internal/fromproto5/client_capabilities.go new file mode 100644 index 000000000..fde821ac9 --- /dev/null +++ b/internal/fromproto5/client_capabilities.go @@ -0,0 +1,56 @@ +package fromproto5 + +import ( + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func ReadDataSourceClientCapabilities(in *tfprotov5.ReadDataSourceClientCapabilities) *datasource.ReadClientCapabilities { + if in == nil { + return nil + } + + resp := &datasource.ReadClientCapabilities{ + DeferralAllowed: in.DeferralAllowed, + } + + return resp +} + +func ReadResourceClientCapabilities(in *tfprotov5.ReadResourceClientCapabilities) *resource.ReadClientCapabilities { + if in == nil { + return nil + } + + resp := &resource.ReadClientCapabilities{ + DeferralAllowed: in.DeferralAllowed, + } + + return resp +} + +func ModifyPlanClientCapabilities(in *tfprotov5.PlanResourceChangeClientCapabilities) *resource.ModifyPlanClientCapabilities { + if in == nil { + return nil + } + + resp := &resource.ModifyPlanClientCapabilities{ + DeferralAllowed: in.DeferralAllowed, + } + + return resp +} + +func ImportStateClientCapabilities(in *tfprotov5.ImportResourceStateClientCapabilities) *resource.ImportStateClientCapabilities { + if in == nil { + return nil + } + + resp := &resource.ImportStateClientCapabilities{ + DeferralAllowed: in.DeferralAllowed, + } + + return resp +} diff --git a/internal/fromproto5/importresourcestate.go b/internal/fromproto5/importresourcestate.go index d074f0c9e..ec40c2119 100644 --- a/internal/fromproto5/importresourcestate.go +++ b/internal/fromproto5/importresourcestate.go @@ -44,15 +44,10 @@ func ImportResourceStateRequest(ctx context.Context, proto5 *tfprotov5.ImportRes Raw: tftypes.NewValue(resourceSchema.Type().TerraformType(ctx), nil), Schema: resourceSchema, }, - ID: proto5.ID, - Resource: reqResource, - TypeName: proto5.TypeName, - } - - if proto5.ClientCapabilities != nil { - fw.ClientCapabilities = &resource.ImportStateClientCapabilities{ - DeferralAllowed: proto5.ClientCapabilities.DeferralAllowed, - } + ID: proto5.ID, + Resource: reqResource, + TypeName: proto5.TypeName, + ClientCapabilities: ImportStateClientCapabilities(proto5.ClientCapabilities), } return fw, diags diff --git a/internal/fromproto5/planresourcechange.go b/internal/fromproto5/planresourcechange.go index 0852ad5db..3aaed62b8 100644 --- a/internal/fromproto5/planresourcechange.go +++ b/internal/fromproto5/planresourcechange.go @@ -39,8 +39,9 @@ func PlanResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.PlanResour } fw := &fwserver.PlanResourceChangeRequest{ - ResourceSchema: resourceSchema, - Resource: reqResource, + ResourceSchema: resourceSchema, + Resource: reqResource, + ClientCapabilities: ModifyPlanClientCapabilities(proto5.ClientCapabilities), } config, configDiags := Config(ctx, proto5.Config, resourceSchema) @@ -73,11 +74,5 @@ func PlanResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.PlanResour fw.PriorPrivate = privateData - if proto5.ClientCapabilities != nil { - fw.ClientCapabilities = &resource.ModifyPlanClientCapabilities{ - DeferralAllowed: proto5.ClientCapabilities.DeferralAllowed, - } - } - return fw, diags } diff --git a/internal/fromproto5/readdatasource.go b/internal/fromproto5/readdatasource.go index 105ffad67..9f6fa4ddc 100644 --- a/internal/fromproto5/readdatasource.go +++ b/internal/fromproto5/readdatasource.go @@ -38,8 +38,9 @@ func ReadDataSourceRequest(ctx context.Context, proto5 *tfprotov5.ReadDataSource } fw := &fwserver.ReadDataSourceRequest{ - DataSource: dataSource, - DataSourceSchema: dataSourceSchema, + DataSource: dataSource, + DataSourceSchema: dataSourceSchema, + ClientCapabilities: ReadDataSourceClientCapabilities(proto5.ClientCapabilities), } config, configDiags := Config(ctx, proto5.Config, dataSourceSchema) @@ -54,11 +55,5 @@ func ReadDataSourceRequest(ctx context.Context, proto5 *tfprotov5.ReadDataSource fw.ProviderMeta = providerMeta - if proto5.ClientCapabilities != nil { - fw.ClientCapabilities = &datasource.ReadClientCapabilities{ - DeferralAllowed: proto5.ClientCapabilities.DeferralAllowed, - } - } - return fw, diags } diff --git a/internal/fromproto5/readresource.go b/internal/fromproto5/readresource.go index a3860f6df..04d4b4d2e 100644 --- a/internal/fromproto5/readresource.go +++ b/internal/fromproto5/readresource.go @@ -25,7 +25,8 @@ func ReadResourceRequest(ctx context.Context, proto5 *tfprotov5.ReadResourceRequ var diags diag.Diagnostics fw := &fwserver.ReadResourceRequest{ - Resource: reqResource, + Resource: reqResource, + ClientCapabilities: ReadResourceClientCapabilities(proto5.ClientCapabilities), } currentState, currentStateDiags := State(ctx, proto5.CurrentState, resourceSchema) @@ -46,11 +47,5 @@ func ReadResourceRequest(ctx context.Context, proto5 *tfprotov5.ReadResourceRequ fw.Private = privateData - if proto5.ClientCapabilities != nil { - fw.ClientCapabilities = &resource.ReadClientCapabilities{ - DeferralAllowed: proto5.ClientCapabilities.DeferralAllowed, - } - } - return fw, diags } diff --git a/internal/fromproto6/client_capabilities.go b/internal/fromproto6/client_capabilities.go new file mode 100644 index 000000000..c2f54e498 --- /dev/null +++ b/internal/fromproto6/client_capabilities.go @@ -0,0 +1,56 @@ +package fromproto6 + +import ( + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func ReadDataSourceClientCapabilities(in *tfprotov6.ReadDataSourceClientCapabilities) *datasource.ReadClientCapabilities { + if in == nil { + return nil + } + + resp := &datasource.ReadClientCapabilities{ + DeferralAllowed: in.DeferralAllowed, + } + + return resp +} + +func ReadResourceClientCapabilities(in *tfprotov6.ReadResourceClientCapabilities) *resource.ReadClientCapabilities { + if in == nil { + return nil + } + + resp := &resource.ReadClientCapabilities{ + DeferralAllowed: in.DeferralAllowed, + } + + return resp +} + +func ModifyPlanClientCapabilities(in *tfprotov6.PlanResourceChangeClientCapabilities) *resource.ModifyPlanClientCapabilities { + if in == nil { + return nil + } + + resp := &resource.ModifyPlanClientCapabilities{ + DeferralAllowed: in.DeferralAllowed, + } + + return resp +} + +func ImportStateClientCapabilities(in *tfprotov6.ImportResourceStateClientCapabilities) *resource.ImportStateClientCapabilities { + if in == nil { + return nil + } + + resp := &resource.ImportStateClientCapabilities{ + DeferralAllowed: in.DeferralAllowed, + } + + return resp +} diff --git a/internal/fromproto6/importresourcestate.go b/internal/fromproto6/importresourcestate.go index 431fadd4c..7070901cd 100644 --- a/internal/fromproto6/importresourcestate.go +++ b/internal/fromproto6/importresourcestate.go @@ -44,15 +44,10 @@ func ImportResourceStateRequest(ctx context.Context, proto6 *tfprotov6.ImportRes Raw: tftypes.NewValue(resourceSchema.Type().TerraformType(ctx), nil), Schema: resourceSchema, }, - ID: proto6.ID, - Resource: reqResource, - TypeName: proto6.TypeName, - } - - if proto6.ClientCapabilities != nil { - fw.ClientCapabilities = &resource.ImportStateClientCapabilities{ - DeferralAllowed: proto6.ClientCapabilities.DeferralAllowed, - } + ID: proto6.ID, + Resource: reqResource, + TypeName: proto6.TypeName, + ClientCapabilities: ImportStateClientCapabilities(proto6.ClientCapabilities), } return fw, diags diff --git a/internal/fromproto6/planresourcechange.go b/internal/fromproto6/planresourcechange.go index a957c9b9e..6a10ee180 100644 --- a/internal/fromproto6/planresourcechange.go +++ b/internal/fromproto6/planresourcechange.go @@ -39,8 +39,9 @@ func PlanResourceChangeRequest(ctx context.Context, proto6 *tfprotov6.PlanResour } fw := &fwserver.PlanResourceChangeRequest{ - ResourceSchema: resourceSchema, - Resource: reqResource, + ResourceSchema: resourceSchema, + Resource: reqResource, + ClientCapabilities: ModifyPlanClientCapabilities(proto6.ClientCapabilities), } config, configDiags := Config(ctx, proto6.Config, resourceSchema) @@ -73,11 +74,5 @@ func PlanResourceChangeRequest(ctx context.Context, proto6 *tfprotov6.PlanResour fw.PriorPrivate = privateData - if proto6.ClientCapabilities != nil { - fw.ClientCapabilities = &resource.ModifyPlanClientCapabilities{ - DeferralAllowed: proto6.ClientCapabilities.DeferralAllowed, - } - } - return fw, diags } diff --git a/internal/fromproto6/readdatasource.go b/internal/fromproto6/readdatasource.go index edd540688..b84b7b108 100644 --- a/internal/fromproto6/readdatasource.go +++ b/internal/fromproto6/readdatasource.go @@ -38,8 +38,9 @@ func ReadDataSourceRequest(ctx context.Context, proto6 *tfprotov6.ReadDataSource } fw := &fwserver.ReadDataSourceRequest{ - DataSourceSchema: dataSourceSchema, - DataSource: dataSource, + DataSourceSchema: dataSourceSchema, + DataSource: dataSource, + ClientCapabilities: ReadDataSourceClientCapabilities(proto6.ClientCapabilities), } config, configDiags := Config(ctx, proto6.Config, dataSourceSchema) @@ -54,11 +55,5 @@ func ReadDataSourceRequest(ctx context.Context, proto6 *tfprotov6.ReadDataSource fw.ProviderMeta = providerMeta - if proto6.ClientCapabilities != nil { - fw.ClientCapabilities = &datasource.ReadClientCapabilities{ - DeferralAllowed: proto6.ClientCapabilities.DeferralAllowed, - } - } - return fw, diags } diff --git a/internal/fromproto6/readresource.go b/internal/fromproto6/readresource.go index 2679b6612..a42f669d6 100644 --- a/internal/fromproto6/readresource.go +++ b/internal/fromproto6/readresource.go @@ -25,7 +25,8 @@ func ReadResourceRequest(ctx context.Context, proto6 *tfprotov6.ReadResourceRequ var diags diag.Diagnostics fw := &fwserver.ReadResourceRequest{ - Resource: reqResource, + Resource: reqResource, + ClientCapabilities: ReadResourceClientCapabilities(proto6.ClientCapabilities), } currentState, currentStateDiags := State(ctx, proto6.CurrentState, resourceSchema) @@ -46,11 +47,5 @@ func ReadResourceRequest(ctx context.Context, proto6 *tfprotov6.ReadResourceRequ fw.Private = privateData - if proto6.ClientCapabilities != nil { - fw.ClientCapabilities = &resource.ReadClientCapabilities{ - DeferralAllowed: proto6.ClientCapabilities.DeferralAllowed, - } - } - return fw, diags } diff --git a/internal/fwserver/server_importresourcestate.go b/internal/fwserver/server_importresourcestate.go index 528587d72..4a8c0a5dd 100644 --- a/internal/fwserver/server_importresourcestate.go +++ b/internal/fwserver/server_importresourcestate.go @@ -93,8 +93,11 @@ func (s *Server) ImportResourceState(ctx context.Context, req *ImportResourceSta } importReq := resource.ImportStateRequest{ - ID: req.ID, - ClientCapabilities: req.ClientCapabilities, + ID: req.ID, + } + + if req.ClientCapabilities != nil { + importReq.ClientCapabilities = *req.ClientCapabilities } privateProviderData := privatestate.EmptyProviderData(ctx) @@ -117,7 +120,7 @@ func (s *Server) ImportResourceState(ctx context.Context, req *ImportResourceSta return } - if (importReq.ClientCapabilities == nil || !importReq.ClientCapabilities.DeferralAllowed) && importResp.DeferredResponse != nil { + if !importReq.ClientCapabilities.DeferralAllowed && importResp.DeferredResponse != nil { resp.Diagnostics.AddError( "Resource Import Deferral Not Allowed", "An unexpected error was encountered when importing the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ diff --git a/internal/fwserver/server_planresourcechange.go b/internal/fwserver/server_planresourcechange.go index f1f2b1413..0549fe3d3 100644 --- a/internal/fwserver/server_planresourcechange.go +++ b/internal/fwserver/server_planresourcechange.go @@ -262,17 +262,20 @@ func (s *Server) PlanResourceChange(ctx context.Context, req *PlanResourceChange logging.FrameworkTrace(ctx, "Resource implements ResourceWithModifyPlan") modifyPlanReq := resource.ModifyPlanRequest{ - Config: *req.Config, - Plan: stateToPlan(*resp.PlannedState), - State: *req.PriorState, - Private: resp.PlannedPrivate.Provider, - ClientCapabilities: req.ClientCapabilities, + Config: *req.Config, + Plan: stateToPlan(*resp.PlannedState), + State: *req.PriorState, + Private: resp.PlannedPrivate.Provider, } if req.ProviderMeta != nil { modifyPlanReq.ProviderMeta = *req.ProviderMeta } + if req.ClientCapabilities != nil { + modifyPlanReq.ClientCapabilities = *req.ClientCapabilities + } + modifyPlanResp := resource.ModifyPlanResponse{ Diagnostics: resp.Diagnostics, Plan: modifyPlanReq.Plan, @@ -284,7 +287,7 @@ func (s *Server) PlanResourceChange(ctx context.Context, req *PlanResourceChange resourceWithModifyPlan.ModifyPlan(ctx, modifyPlanReq, &modifyPlanResp) logging.FrameworkTrace(ctx, "Called provider defined Resource ModifyPlan") - if (modifyPlanReq.ClientCapabilities == nil || !modifyPlanReq.ClientCapabilities.DeferralAllowed) && modifyPlanResp.DeferredResponse != nil { + if !modifyPlanReq.ClientCapabilities.DeferralAllowed && modifyPlanResp.DeferredResponse != nil { resp.Diagnostics.AddError( "Resource Deferral Not Allowed", "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ diff --git a/internal/fwserver/server_readdatasource.go b/internal/fwserver/server_readdatasource.go index 15693e42d..4ff33fe45 100644 --- a/internal/fwserver/server_readdatasource.go +++ b/internal/fwserver/server_readdatasource.go @@ -77,7 +77,9 @@ func (s *Server) ReadDataSource(ctx context.Context, req *ReadDataSourceRequest, readReq.ProviderMeta = *req.ProviderMeta } - readReq.ClientCapabilities = req.ClientCapabilities + if req.ClientCapabilities != nil { + readReq.ClientCapabilities = *req.ClientCapabilities + } logging.FrameworkTrace(ctx, "Calling provider defined DataSource Read") req.DataSource.Read(ctx, readReq, &readResp) diff --git a/internal/fwserver/server_readresource.go b/internal/fwserver/server_readresource.go index 1381e2f27..1991cbfd0 100644 --- a/internal/fwserver/server_readresource.go +++ b/internal/fwserver/server_readresource.go @@ -85,6 +85,10 @@ func (s *Server) ReadResource(ctx context.Context, req *ReadResourceRequest, res readReq.ProviderMeta = *req.ProviderMeta } + if req.ClientCapabilities != nil { + readReq.ClientCapabilities = *req.ClientCapabilities + } + privateProviderData := privatestate.EmptyProviderData(ctx) readReq.Private = privateProviderData @@ -99,8 +103,6 @@ func (s *Server) ReadResource(ctx context.Context, req *ReadResourceRequest, res resp.Private = req.Private } - readReq.ClientCapabilities = req.ClientCapabilities - logging.FrameworkTrace(ctx, "Calling provider defined Resource Read") req.Resource.Read(ctx, readReq, &readResp) logging.FrameworkTrace(ctx, "Called provider defined Resource Read") diff --git a/resource/import_state.go b/resource/import_state.go index 22a5f7aa2..0c425715b 100644 --- a/resource/import_state.go +++ b/resource/import_state.go @@ -38,7 +38,7 @@ type ImportStateRequest struct { // ClientCapabilities defines optionally supported protocol features for the // ImportResourceState RPC, such as forward-compatible Terraform behavior changes. - ClientCapabilities *ImportStateClientCapabilities + ClientCapabilities ImportStateClientCapabilities } // ImportStateResponse represents a response to a ImportStateRequest. diff --git a/resource/modify_plan.go b/resource/modify_plan.go index 1cde9acd1..28d9fc0f7 100644 --- a/resource/modify_plan.go +++ b/resource/modify_plan.go @@ -54,7 +54,7 @@ type ModifyPlanRequest struct { // ClientCapabilities defines optionally supported protocol features for the // PlanResourceChange RPC, such as forward-compatible Terraform behavior changes. - ClientCapabilities *ModifyPlanClientCapabilities + ClientCapabilities ModifyPlanClientCapabilities } // ModifyPlanResponse represents a response to a diff --git a/resource/read.go b/resource/read.go index 661b9d1bb..7e61aac55 100644 --- a/resource/read.go +++ b/resource/read.go @@ -44,7 +44,7 @@ type ReadRequest struct { // ClientCapabilities defines optionally supported protocol features for the // ReadResource RPC, such as forward-compatible Terraform behavior changes. - ClientCapabilities *ReadClientCapabilities + ClientCapabilities ReadClientCapabilities } // ReadResponse represents a response to a ReadRequest. An From c5156c533f18ebf83a833e9b913eea8f585930e9 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Mon, 13 May 2024 16:58:42 -0400 Subject: [PATCH 17/25] Rename `DeferredResponse` to `Deferred` --- .changes/unreleased/FEATURES-20240508-105105.yaml | 2 +- .changes/unreleased/FEATURES-20240508-105141.yaml | 2 +- datasource/deferred.go | 4 ++-- datasource/read.go | 4 ++-- internal/fwserver/server_importresourcestate.go | 8 ++++---- internal/fwserver/server_importresourcestate_test.go | 8 ++++---- internal/fwserver/server_planresourcechange.go | 8 ++++---- internal/fwserver/server_planresourcechange_test.go | 8 ++++---- internal/fwserver/server_readdatasource.go | 6 +++--- internal/fwserver/server_readdatasource_test.go | 10 +++++----- internal/fwserver/server_readresource.go | 6 +++--- internal/fwserver/server_readresource_test.go | 10 +++++----- internal/toproto5/planresourcechange_test.go | 2 +- internal/toproto5/readdatasource_test.go | 2 +- internal/toproto5/readresource_test.go | 2 +- internal/toproto6/planresourcechange_test.go | 2 +- internal/toproto6/readdatasource_test.go | 2 +- internal/toproto6/readresource_test.go | 2 +- resource/deferred.go | 4 ++-- resource/import_state.go | 4 ++-- resource/modify_plan.go | 4 ++-- resource/read.go | 4 ++-- 22 files changed, 52 insertions(+), 52 deletions(-) diff --git a/.changes/unreleased/FEATURES-20240508-105105.yaml b/.changes/unreleased/FEATURES-20240508-105105.yaml index 9b1c418bb..67d8700bb 100644 --- a/.changes/unreleased/FEATURES-20240508-105105.yaml +++ b/.changes/unreleased/FEATURES-20240508-105105.yaml @@ -1,5 +1,5 @@ kind: FEATURES -body: 'resource: Add `DeferredResponse` field to `ReadResponse`, `ModifyPlanResponse`, and `ImportStateResponse` +body: 'resource: Add `Deferred` field to `ReadResponse`, `ModifyPlanResponse`, and `ImportStateResponse` which indicates a resource deferred action to the Terraform client' time: 2024-05-08T10:51:05.90518-04:00 custom: diff --git a/.changes/unreleased/FEATURES-20240508-105141.yaml b/.changes/unreleased/FEATURES-20240508-105141.yaml index 62ab936f6..82f1ccbcd 100644 --- a/.changes/unreleased/FEATURES-20240508-105141.yaml +++ b/.changes/unreleased/FEATURES-20240508-105141.yaml @@ -1,5 +1,5 @@ kind: FEATURES -body: 'datasource: Add `DeferredResponse` field to `ReadResponse` which indicates a data source deferred action +body: 'datasource: Add `Deferred` field to `ReadResponse` which indicates a data source deferred action to the Terraform client' time: 2024-05-08T10:51:41.663935-04:00 custom: diff --git a/datasource/deferred.go b/datasource/deferred.go index 9631c0210..ee740f694 100644 --- a/datasource/deferred.go +++ b/datasource/deferred.go @@ -20,11 +20,11 @@ const ( DeferredReasonAbsentPrereq DeferredReason = 3 ) -// DeferredResponse is used to indicate to Terraform that a change needs to be deferred for a reason. +// Deferred is used to indicate to Terraform that a change needs to be deferred for a reason. // // NOTE: This functionality is related to deferred action support, which is currently experimental and is subject // to change or break without warning. It is not protected by version compatibility guarantees. -type DeferredResponse struct { +type Deferred struct { // Reason is the reason for deferring the change. Reason DeferredReason } diff --git a/datasource/read.go b/datasource/read.go index 496cd2bd8..751b9754f 100644 --- a/datasource/read.go +++ b/datasource/read.go @@ -54,7 +54,7 @@ type ReadResponse struct { // warnings or errors generated. Diagnostics diag.Diagnostics - // DeferredResponse indicates that Terraform should defer + // Deferred indicates that Terraform should defer // reading this data source. // // This field can only be set if @@ -62,5 +62,5 @@ type ReadResponse struct { // // NOTE: This functionality is related to deferred action support, which is currently experimental and is subject // to change or break without warning. It is not protected by version compatibility guarantees. - DeferredResponse *DeferredResponse + Deferred *Deferred } diff --git a/internal/fwserver/server_importresourcestate.go b/internal/fwserver/server_importresourcestate.go index 4a8c0a5dd..0a2c91e2c 100644 --- a/internal/fwserver/server_importresourcestate.go +++ b/internal/fwserver/server_importresourcestate.go @@ -44,7 +44,7 @@ type ImportResourceStateRequest struct { type ImportResourceStateResponse struct { Diagnostics diag.Diagnostics ImportedResources []ImportedResource - Deferred *resource.DeferredResponse + Deferred *resource.Deferred } // ImportResourceState implements the framework server ImportResourceState RPC. @@ -120,12 +120,12 @@ func (s *Server) ImportResourceState(ctx context.Context, req *ImportResourceSta return } - if !importReq.ClientCapabilities.DeferralAllowed && importResp.DeferredResponse != nil { + if !importReq.ClientCapabilities.DeferralAllowed && importResp.Deferred != nil { resp.Diagnostics.AddError( "Resource Import Deferral Not Allowed", "An unexpected error was encountered when importing the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "(resource.ImportStateResponse).DeferredResponse can only be set if (resource.ImportStateRequest.ClientCapabilities).DeferralAllowed is true.", + "(resource.ImportStateResponse).Deferred can only be set if (resource.ImportStateRequest.ClientCapabilities).DeferralAllowed is true.", ) return } @@ -145,7 +145,7 @@ func (s *Server) ImportResourceState(ctx context.Context, req *ImportResourceSta private.Provider = importResp.Private } - resp.Deferred = importResp.DeferredResponse + resp.Deferred = importResp.Deferred resp.ImportedResources = []ImportedResource{ { State: importResp.State, diff --git a/internal/fwserver/server_importresourcestate_test.go b/internal/fwserver/server_importresourcestate_test.go index 7b0177837..d1f7dc097 100644 --- a/internal/fwserver/server_importresourcestate_test.go +++ b/internal/fwserver/server_importresourcestate_test.go @@ -317,7 +317,7 @@ func TestServerImportResourceState(t *testing.T) { resp.Diagnostics.AddError("unexpected req.ID value: %s", req.ID) } - resp.DeferredResponse = &resource.DeferredResponse{ + resp.Deferred = &resource.Deferred{ Reason: resource.DeferredReasonAbsentPrereq, } @@ -336,7 +336,7 @@ func TestServerImportResourceState(t *testing.T) { Private: testEmptyPrivate, }, }, - Deferred: &resource.DeferredResponse{Reason: resource.DeferredReasonAbsentPrereq}, + Deferred: &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq}, }, }, "request-deferral-not-allowed-response-deferral": { @@ -353,7 +353,7 @@ func TestServerImportResourceState(t *testing.T) { resp.Diagnostics.AddError("unexpected req.ID value: %s", req.ID) } - resp.DeferredResponse = &resource.DeferredResponse{ + resp.Deferred = &resource.Deferred{ Reason: resource.DeferredReasonAbsentPrereq, } @@ -369,7 +369,7 @@ func TestServerImportResourceState(t *testing.T) { "Resource Import Deferral Not Allowed", "An unexpected error was encountered when importing the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "(resource.ImportStateResponse).DeferredResponse can only be set if (resource.ImportStateRequest.ClientCapabilities).DeferralAllowed is true.", + "(resource.ImportStateResponse).Deferred can only be set if (resource.ImportStateRequest.ClientCapabilities).DeferralAllowed is true.", ), }, }, diff --git a/internal/fwserver/server_planresourcechange.go b/internal/fwserver/server_planresourcechange.go index 0549fe3d3..77df93039 100644 --- a/internal/fwserver/server_planresourcechange.go +++ b/internal/fwserver/server_planresourcechange.go @@ -39,7 +39,7 @@ type PlanResourceChangeRequest struct { // PlanResourceChangeResponse is the framework server response for the // PlanResourceChange RPC. type PlanResourceChangeResponse struct { - Deferred *resource.DeferredResponse + Deferred *resource.Deferred Diagnostics diag.Diagnostics PlannedPrivate *privatestate.Data PlannedState *tfsdk.State @@ -287,12 +287,12 @@ func (s *Server) PlanResourceChange(ctx context.Context, req *PlanResourceChange resourceWithModifyPlan.ModifyPlan(ctx, modifyPlanReq, &modifyPlanResp) logging.FrameworkTrace(ctx, "Called provider defined Resource ModifyPlan") - if !modifyPlanReq.ClientCapabilities.DeferralAllowed && modifyPlanResp.DeferredResponse != nil { + if !modifyPlanReq.ClientCapabilities.DeferralAllowed && modifyPlanResp.Deferred != nil { resp.Diagnostics.AddError( "Resource Deferral Not Allowed", "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "(resource.ModifyPlanResponse).DeferredResponse can only be set if (resource.ModifyPlanRequest.ClientCapabilities.DeferralAllowed is true.", + "(resource.ModifyPlanResponse).Deferred can only be set if (resource.ModifyPlanRequest.ClientCapabilities.DeferralAllowed is true.", ) return } @@ -301,7 +301,7 @@ func (s *Server) PlanResourceChange(ctx context.Context, req *PlanResourceChange resp.PlannedState = planToState(modifyPlanResp.Plan) resp.RequiresReplace = append(resp.RequiresReplace, modifyPlanResp.RequiresReplace...) resp.PlannedPrivate.Provider = modifyPlanResp.Private - resp.Deferred = modifyPlanResp.DeferredResponse + resp.Deferred = modifyPlanResp.Deferred } // Ensure deterministic RequiresReplace by sorting and deduplicating diff --git a/internal/fwserver/server_planresourcechange_test.go b/internal/fwserver/server_planresourcechange_test.go index d23a73137..60c6e451a 100644 --- a/internal/fwserver/server_planresourcechange_test.go +++ b/internal/fwserver/server_planresourcechange_test.go @@ -6044,14 +6044,14 @@ func TestServerPlanResourceChange(t *testing.T) { Resource: &testprovider.ResourceWithModifyPlan{ ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { if req.ClientCapabilities.DeferralAllowed == true { - resp.DeferredResponse = &resource.DeferredResponse{Reason: resource.DeferredReasonAbsentPrereq} + resp.Deferred = &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq} } }, }, }, expectedResponse: &fwserver.PlanResourceChangeResponse{ - Deferred: &resource.DeferredResponse{Reason: resource.DeferredReasonAbsentPrereq}, + Deferred: &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq}, PlannedState: &tfsdk.State{ Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ "test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), @@ -6085,7 +6085,7 @@ func TestServerPlanResourceChange(t *testing.T) { ResourceSchema: testSchema, Resource: &testprovider.ResourceWithModifyPlan{ ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - resp.DeferredResponse = &resource.DeferredResponse{Reason: resource.DeferredReasonAbsentPrereq} + resp.Deferred = &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq} }, }, }, @@ -6095,7 +6095,7 @@ func TestServerPlanResourceChange(t *testing.T) { "Resource Deferral Not Allowed", "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "(resource.ModifyPlanResponse).DeferredResponse can only be set if (resource.ModifyPlanRequest.ClientCapabilities.DeferralAllowed is true.", + "(resource.ModifyPlanResponse).Deferred can only be set if (resource.ModifyPlanRequest.ClientCapabilities.DeferralAllowed is true.", ), }, PlannedState: &tfsdk.State{ diff --git a/internal/fwserver/server_readdatasource.go b/internal/fwserver/server_readdatasource.go index 4ff33fe45..6fae4b9c6 100644 --- a/internal/fwserver/server_readdatasource.go +++ b/internal/fwserver/server_readdatasource.go @@ -27,7 +27,7 @@ type ReadDataSourceRequest struct { // ReadDataSourceResponse is the framework server response for the // ReadDataSource RPC. type ReadDataSourceResponse struct { - Deferred *datasource.DeferredResponse + Deferred *datasource.Deferred Diagnostics diag.Diagnostics State *tfsdk.State } @@ -87,7 +87,7 @@ func (s *Server) ReadDataSource(ctx context.Context, req *ReadDataSourceRequest, resp.Diagnostics = readResp.Diagnostics resp.State = &readResp.State - resp.Deferred = readResp.DeferredResponse + resp.Deferred = readResp.Deferred if resp.Diagnostics.HasError() { return @@ -98,7 +98,7 @@ func (s *Server) ReadDataSource(ctx context.Context, req *ReadDataSourceRequest, "Data Source Deferral Not Allowed", "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "(datasource.ReadResponse).DeferredResponse can only be set if (datasource.ReadRequest.ClientCapabilities).DeferralAllowed is true.", + "(datasource.ReadResponse).Deferred can only be set if (datasource.ReadRequest.ClientCapabilities).DeferralAllowed is true.", ) return } diff --git a/internal/fwserver/server_readdatasource_test.go b/internal/fwserver/server_readdatasource_test.go index 6ad360b5d..f946c9314 100644 --- a/internal/fwserver/server_readdatasource_test.go +++ b/internal/fwserver/server_readdatasource_test.go @@ -370,7 +370,7 @@ func TestServerReadDataSource(t *testing.T) { resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - resp.DeferredResponse = &datasource.DeferredResponse{Reason: datasource.DeferredReasonAbsentPrereq} + resp.Deferred = &datasource.Deferred{Reason: datasource.DeferredReasonAbsentPrereq} if config.TestRequired.ValueString() != "test-config-value" { resp.Diagnostics.AddError("unexpected req.Config value: %s", config.TestRequired.ValueString()) @@ -381,7 +381,7 @@ func TestServerReadDataSource(t *testing.T) { }, expectedResponse: &fwserver.ReadDataSourceResponse{ State: testStateUnchanged, - Deferred: &datasource.DeferredResponse{Reason: datasource.DeferredReasonAbsentPrereq}, + Deferred: &datasource.Deferred{Reason: datasource.DeferredReasonAbsentPrereq}, }, }, "request-deferral-not-allowed-response-deferral": { @@ -400,7 +400,7 @@ func TestServerReadDataSource(t *testing.T) { resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - resp.DeferredResponse = &datasource.DeferredResponse{Reason: datasource.DeferredReasonAbsentPrereq} + resp.Deferred = &datasource.Deferred{Reason: datasource.DeferredReasonAbsentPrereq} if config.TestRequired.ValueString() != "test-config-value" { resp.Diagnostics.AddError("unexpected req.Config value: %s", config.TestRequired.ValueString()) @@ -414,11 +414,11 @@ func TestServerReadDataSource(t *testing.T) { "Data Source Deferral Not Allowed", "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "(datasource.ReadResponse).DeferredResponse can only be set if (datasource.ReadRequest.ClientCapabilities).DeferralAllowed is true.", + "(datasource.ReadResponse).Deferred can only be set if (datasource.ReadRequest.ClientCapabilities).DeferralAllowed is true.", ), }, State: testStateUnchanged, - Deferred: &datasource.DeferredResponse{Reason: datasource.DeferredReasonAbsentPrereq}, + Deferred: &datasource.Deferred{Reason: datasource.DeferredReasonAbsentPrereq}, }, }, } diff --git a/internal/fwserver/server_readresource.go b/internal/fwserver/server_readresource.go index 1991cbfd0..c3aa84afd 100644 --- a/internal/fwserver/server_readresource.go +++ b/internal/fwserver/server_readresource.go @@ -27,7 +27,7 @@ type ReadResourceRequest struct { // ReadResourceResponse is the framework server response for the // ReadResource RPC. type ReadResourceResponse struct { - Deferred *resource.DeferredResponse + Deferred *resource.Deferred Diagnostics diag.Diagnostics NewState *tfsdk.State Private *privatestate.Data @@ -109,7 +109,7 @@ func (s *Server) ReadResource(ctx context.Context, req *ReadResourceRequest, res resp.Diagnostics = readResp.Diagnostics resp.NewState = &readResp.State - resp.Deferred = readResp.DeferredResponse + resp.Deferred = readResp.Deferred if readResp.Private != nil { if resp.Private == nil { @@ -128,7 +128,7 @@ func (s *Server) ReadResource(ctx context.Context, req *ReadResourceRequest, res "Resource Deferral Not Allowed", "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "(resource.ReadResponse).DeferredResponse can only be set if (resource.ReadRequest.ClientCapabilities).DeferralAllowed is true.", + "(resource.ReadResponse).Deferred can only be set if (resource.ReadRequest.ClientCapabilities).DeferralAllowed is true.", ) return } diff --git a/internal/fwserver/server_readresource_test.go b/internal/fwserver/server_readresource_test.go index 2708ad22a..4ecacd20d 100644 --- a/internal/fwserver/server_readresource_test.go +++ b/internal/fwserver/server_readresource_test.go @@ -529,7 +529,7 @@ func TestServerReadResource(t *testing.T) { resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - resp.DeferredResponse = &resource.DeferredResponse{Reason: resource.DeferredReasonAbsentPrereq} + resp.Deferred = &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq} if data.TestRequired.ValueString() != "test-currentstate-value" { resp.Diagnostics.AddError("unexpected req.State value: %s", data.TestRequired.ValueString()) @@ -541,7 +541,7 @@ func TestServerReadResource(t *testing.T) { expectedResponse: &fwserver.ReadResourceResponse{ NewState: testCurrentState, Private: testEmptyPrivate, - Deferred: &resource.DeferredResponse{Reason: resource.DeferredReasonAbsentPrereq}, + Deferred: &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq}, }, }, "request-deferral-not-allowed-response-deferral": { @@ -559,7 +559,7 @@ func TestServerReadResource(t *testing.T) { resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - resp.DeferredResponse = &resource.DeferredResponse{Reason: resource.DeferredReasonAbsentPrereq} + resp.Deferred = &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq} if data.TestRequired.ValueString() != "test-currentstate-value" { resp.Diagnostics.AddError("unexpected req.State value: %s", data.TestRequired.ValueString()) @@ -573,12 +573,12 @@ func TestServerReadResource(t *testing.T) { "Resource Deferral Not Allowed", "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "(resource.ReadResponse).DeferredResponse can only be set if (resource.ReadRequest.ClientCapabilities).DeferralAllowed is true.", + "(resource.ReadResponse).Deferred can only be set if (resource.ReadRequest.ClientCapabilities).DeferralAllowed is true.", ), }, NewState: testCurrentState, Private: testEmptyPrivate, - Deferred: &resource.DeferredResponse{Reason: resource.DeferredReasonAbsentPrereq}, + Deferred: &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq}, }, }, } diff --git a/internal/toproto5/planresourcechange_test.go b/internal/toproto5/planresourcechange_test.go index bf0ddfff4..d4ed9e674 100644 --- a/internal/toproto5/planresourcechange_test.go +++ b/internal/toproto5/planresourcechange_test.go @@ -70,7 +70,7 @@ func TestPlanResourceChangeResponse(t *testing.T) { testEmptyProviderData := privatestate.EmptyProviderData(context.Background()) - testDeferred := &resource.DeferredResponse{ + testDeferred := &resource.Deferred{ Reason: resource.DeferredReasonAbsentPrereq, } diff --git a/internal/toproto5/readdatasource_test.go b/internal/toproto5/readdatasource_test.go index 40e42d131..bc39fd51a 100644 --- a/internal/toproto5/readdatasource_test.go +++ b/internal/toproto5/readdatasource_test.go @@ -34,7 +34,7 @@ func TestReadDataSourceResponse(t *testing.T) { testProto5DynamicValue, err := tfprotov5.NewDynamicValue(testProto5Type, testProto5Value) - testDeferral := &datasource.DeferredResponse{ + testDeferral := &datasource.Deferred{ Reason: datasource.DeferredReasonAbsentPrereq, } diff --git a/internal/toproto5/readresource_test.go b/internal/toproto5/readresource_test.go index f9b3dce77..582e7090a 100644 --- a/internal/toproto5/readresource_test.go +++ b/internal/toproto5/readresource_test.go @@ -69,7 +69,7 @@ func TestReadResourceResponse(t *testing.T) { }, } - testDeferral := &resource.DeferredResponse{ + testDeferral := &resource.Deferred{ Reason: resource.DeferredReasonAbsentPrereq, } diff --git a/internal/toproto6/planresourcechange_test.go b/internal/toproto6/planresourcechange_test.go index d94f9c148..ad8376e16 100644 --- a/internal/toproto6/planresourcechange_test.go +++ b/internal/toproto6/planresourcechange_test.go @@ -70,7 +70,7 @@ func TestPlanResourceChangeResponse(t *testing.T) { testEmptyProviderData := privatestate.EmptyProviderData(context.Background()) - testDeferred := &resource.DeferredResponse{ + testDeferred := &resource.Deferred{ Reason: resource.DeferredReasonAbsentPrereq, } diff --git a/internal/toproto6/readdatasource_test.go b/internal/toproto6/readdatasource_test.go index fb1ae4dd5..593452609 100644 --- a/internal/toproto6/readdatasource_test.go +++ b/internal/toproto6/readdatasource_test.go @@ -34,7 +34,7 @@ func TestReadDataSourceResponse(t *testing.T) { testProto6DynamicValue, err := tfprotov6.NewDynamicValue(testProto6Type, testProto6Value) - testDeferral := &datasource.DeferredResponse{ + testDeferral := &datasource.Deferred{ Reason: datasource.DeferredReasonAbsentPrereq, } diff --git a/internal/toproto6/readresource_test.go b/internal/toproto6/readresource_test.go index cfd856c7b..46d928c31 100644 --- a/internal/toproto6/readresource_test.go +++ b/internal/toproto6/readresource_test.go @@ -69,7 +69,7 @@ func TestReadResourceResponse(t *testing.T) { testEmptyProviderData := privatestate.EmptyProviderData(context.Background()) - testDeferral := &resource.DeferredResponse{ + testDeferral := &resource.Deferred{ Reason: resource.DeferredReasonAbsentPrereq, } diff --git a/resource/deferred.go b/resource/deferred.go index 0009f6872..ca8d8d4ed 100644 --- a/resource/deferred.go +++ b/resource/deferred.go @@ -20,11 +20,11 @@ const ( DeferredReasonAbsentPrereq DeferredReason = 3 ) -// DeferredResponse is used to indicate to Terraform that a change needs to be deferred for a reason. +// Deferred is used to indicate to Terraform that a change needs to be deferred for a reason. // // NOTE: This functionality is related to deferred action support, which is currently experimental and is subject // to change or break without warning. It is not protected by version compatibility guarantees. -type DeferredResponse struct { +type Deferred struct { // Reason is the reason for deferring the change. Reason DeferredReason } diff --git a/resource/import_state.go b/resource/import_state.go index 0c425715b..63f1b9fc5 100644 --- a/resource/import_state.go +++ b/resource/import_state.go @@ -61,7 +61,7 @@ type ImportStateResponse struct { // data during the resource's Import operation. Private *privatestate.ProviderData - // DeferredResponse indicates that Terraform should defer + // Deferred indicates that Terraform should defer // importing this resource. // // This field can only be set if @@ -69,7 +69,7 @@ type ImportStateResponse struct { // // NOTE: This functionality is related to deferred action support, which is currently experimental and is subject // to change or break without warning. It is not protected by version compatibility guarantees. - DeferredResponse *DeferredResponse + Deferred *Deferred } // ImportStatePassthroughID is a helper function to set the import diff --git a/resource/modify_plan.go b/resource/modify_plan.go index 28d9fc0f7..26540513b 100644 --- a/resource/modify_plan.go +++ b/resource/modify_plan.go @@ -82,7 +82,7 @@ type ModifyPlanResponse struct { // generated. Diagnostics diag.Diagnostics - // DeferredResponse indicates that Terraform should defer + // Deferred indicates that Terraform should defer // importing this resource. // // This field can only be set if @@ -90,5 +90,5 @@ type ModifyPlanResponse struct { // // NOTE: This functionality is related to deferred action support, which is currently experimental and is subject // to change or break without warning. It is not protected by version compatibility guarantees. - DeferredResponse *DeferredResponse + Deferred *Deferred } diff --git a/resource/read.go b/resource/read.go index 7e61aac55..4e4240036 100644 --- a/resource/read.go +++ b/resource/read.go @@ -67,7 +67,7 @@ type ReadResponse struct { // warnings or errors generated. Diagnostics diag.Diagnostics - // DeferredResponse indicates that Terraform should defer + // Deferred indicates that Terraform should defer // importing this resource. // // This field can only be set if @@ -75,5 +75,5 @@ type ReadResponse struct { // // NOTE: This functionality is related to deferred action support, which is currently experimental and is subject // to change or break without warning. It is not protected by version compatibility guarantees. - DeferredResponse *DeferredResponse + Deferred *Deferred } From 96166fb831153878e4a4b80fa86cf84b70fc824d Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Mon, 13 May 2024 17:11:38 -0400 Subject: [PATCH 18/25] Remove error handling for deferral response without deferral capability --- .../fwserver/server_importresourcestate.go | 10 ---- .../server_importresourcestate_test.go | 37 +------------- .../fwserver/server_planresourcechange.go | 10 ---- .../server_planresourcechange_test.go | 48 +------------------ internal/fwserver/server_readdatasource.go | 10 ---- .../fwserver/server_readdatasource_test.go | 39 +-------------- internal/fwserver/server_readresource.go | 10 ---- internal/fwserver/server_readresource_test.go | 39 +-------------- 8 files changed, 4 insertions(+), 199 deletions(-) diff --git a/internal/fwserver/server_importresourcestate.go b/internal/fwserver/server_importresourcestate.go index 0a2c91e2c..54782c572 100644 --- a/internal/fwserver/server_importresourcestate.go +++ b/internal/fwserver/server_importresourcestate.go @@ -120,16 +120,6 @@ func (s *Server) ImportResourceState(ctx context.Context, req *ImportResourceSta return } - if !importReq.ClientCapabilities.DeferralAllowed && importResp.Deferred != nil { - resp.Diagnostics.AddError( - "Resource Import Deferral Not Allowed", - "An unexpected error was encountered when importing the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ - "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "(resource.ImportStateResponse).Deferred can only be set if (resource.ImportStateRequest.ClientCapabilities).DeferralAllowed is true.", - ) - return - } - if importResp.State.Raw.Equal(req.EmptyState.Raw) { resp.Diagnostics.AddError( "Missing Resource Import State", diff --git a/internal/fwserver/server_importresourcestate_test.go b/internal/fwserver/server_importresourcestate_test.go index d1f7dc097..6d5ad88a8 100644 --- a/internal/fwserver/server_importresourcestate_test.go +++ b/internal/fwserver/server_importresourcestate_test.go @@ -303,7 +303,7 @@ func TestServerImportResourceState(t *testing.T) { }, }, }, - "request-deferral-allowed-response-deferral": { + "response-importedresources-deferral": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, }, @@ -339,41 +339,6 @@ func TestServerImportResourceState(t *testing.T) { Deferred: &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq}, }, }, - "request-deferral-not-allowed-response-deferral": { - server: &fwserver.Server{ - Provider: &testprovider.Provider{}, - }, - request: &fwserver.ImportResourceStateRequest{ - EmptyState: *testEmptyState, - ID: "test-id", - Resource: &testprovider.ResourceWithImportState{ - Resource: &testprovider.Resource{}, - ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - if req.ID != "test-id" { - resp.Diagnostics.AddError("unexpected req.ID value: %s", req.ID) - } - - resp.Deferred = &resource.Deferred{ - Reason: resource.DeferredReasonAbsentPrereq, - } - - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) - - }, - }, - TypeName: "test_resource", - }, - expectedResponse: &fwserver.ImportResourceStateResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Resource Import Deferral Not Allowed", - "An unexpected error was encountered when importing the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ - "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "(resource.ImportStateResponse).Deferred can only be set if (resource.ImportStateRequest.ClientCapabilities).DeferralAllowed is true.", - ), - }, - }, - }, } for name, testCase := range testCases { diff --git a/internal/fwserver/server_planresourcechange.go b/internal/fwserver/server_planresourcechange.go index 77df93039..84477e33d 100644 --- a/internal/fwserver/server_planresourcechange.go +++ b/internal/fwserver/server_planresourcechange.go @@ -287,16 +287,6 @@ func (s *Server) PlanResourceChange(ctx context.Context, req *PlanResourceChange resourceWithModifyPlan.ModifyPlan(ctx, modifyPlanReq, &modifyPlanResp) logging.FrameworkTrace(ctx, "Called provider defined Resource ModifyPlan") - if !modifyPlanReq.ClientCapabilities.DeferralAllowed && modifyPlanResp.Deferred != nil { - resp.Diagnostics.AddError( - "Resource Deferral Not Allowed", - "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ - "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "(resource.ModifyPlanResponse).Deferred can only be set if (resource.ModifyPlanRequest.ClientCapabilities.DeferralAllowed is true.", - ) - return - } - resp.Diagnostics = modifyPlanResp.Diagnostics resp.PlannedState = planToState(modifyPlanResp.Plan) resp.RequiresReplace = append(resp.RequiresReplace, modifyPlanResp.RequiresReplace...) diff --git a/internal/fwserver/server_planresourcechange_test.go b/internal/fwserver/server_planresourcechange_test.go index 60c6e451a..8342f2c9f 100644 --- a/internal/fwserver/server_planresourcechange_test.go +++ b/internal/fwserver/server_planresourcechange_test.go @@ -6019,7 +6019,7 @@ func TestServerPlanResourceChange(t *testing.T) { PlannedPrivate: testPrivateProvider, }, }, - "create-resourcewithmodifyplan-request-deferral-allowed-response-deferral": { + "create-resourcewithmodifyplan-response-deferral": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, }, @@ -6062,52 +6062,6 @@ func TestServerPlanResourceChange(t *testing.T) { PlannedPrivate: testEmptyPrivate, }, }, - "create-resourcewithmodifyplan-request-deferral-not-allowed-response-deferral": { - server: &fwserver.Server{ - Provider: &testprovider.Provider{}, - }, - request: &fwserver.PlanResourceChangeRequest{ - Config: &tfsdk.Config{ - Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ - "test_computed": tftypes.NewValue(tftypes.String, nil), - "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), - }), - Schema: testSchema, - }, - ProposedNewState: &tfsdk.Plan{ - Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ - "test_computed": tftypes.NewValue(tftypes.String, nil), - "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), - }), - Schema: testSchema, - }, - PriorState: testEmptyState, - ResourceSchema: testSchema, - Resource: &testprovider.ResourceWithModifyPlan{ - ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - resp.Deferred = &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq} - }, - }, - }, - expectedResponse: &fwserver.PlanResourceChangeResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Resource Deferral Not Allowed", - "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ - "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "(resource.ModifyPlanResponse).Deferred can only be set if (resource.ModifyPlanRequest.ClientCapabilities.DeferralAllowed is true.", - ), - }, - PlannedState: &tfsdk.State{ - Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ - "test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), - "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), - }), - Schema: testSchema, - }, - PlannedPrivate: testEmptyPrivate, - }, - }, } for name, testCase := range testCases { diff --git a/internal/fwserver/server_readdatasource.go b/internal/fwserver/server_readdatasource.go index 6fae4b9c6..c640179d4 100644 --- a/internal/fwserver/server_readdatasource.go +++ b/internal/fwserver/server_readdatasource.go @@ -93,16 +93,6 @@ func (s *Server) ReadDataSource(ctx context.Context, req *ReadDataSourceRequest, return } - if (req.ClientCapabilities == nil || !req.ClientCapabilities.DeferralAllowed) && resp.Deferred != nil { - resp.Diagnostics.AddError( - "Data Source Deferral Not Allowed", - "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ - "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "(datasource.ReadResponse).Deferred can only be set if (datasource.ReadRequest.ClientCapabilities).DeferralAllowed is true.", - ) - return - } - semanticEqualityReq := SchemaSemanticEqualityRequest{ PriorData: fwschemadata.Data{ Description: fwschemadata.DataDescriptionConfiguration, diff --git a/internal/fwserver/server_readdatasource_test.go b/internal/fwserver/server_readdatasource_test.go index f946c9314..11987b211 100644 --- a/internal/fwserver/server_readdatasource_test.go +++ b/internal/fwserver/server_readdatasource_test.go @@ -354,7 +354,7 @@ func TestServerReadDataSource(t *testing.T) { }, }, }, - "request-deferral-allowed-response-deferral": { + "response-deferral": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, }, @@ -384,43 +384,6 @@ func TestServerReadDataSource(t *testing.T) { Deferred: &datasource.Deferred{Reason: datasource.DeferredReasonAbsentPrereq}, }, }, - "request-deferral-not-allowed-response-deferral": { - server: &fwserver.Server{ - Provider: &testprovider.Provider{}, - }, - request: &fwserver.ReadDataSourceRequest{ - Config: testConfig, - DataSourceSchema: testSchema, - DataSource: &testprovider.DataSource{ - ReadMethod: func(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { - var config struct { - TestComputed types.String `tfsdk:"test_computed"` - TestRequired types.String `tfsdk:"test_required"` - } - - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - - resp.Deferred = &datasource.Deferred{Reason: datasource.DeferredReasonAbsentPrereq} - - if config.TestRequired.ValueString() != "test-config-value" { - resp.Diagnostics.AddError("unexpected req.Config value: %s", config.TestRequired.ValueString()) - } - }, - }, - }, - expectedResponse: &fwserver.ReadDataSourceResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Data Source Deferral Not Allowed", - "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ - "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "(datasource.ReadResponse).Deferred can only be set if (datasource.ReadRequest.ClientCapabilities).DeferralAllowed is true.", - ), - }, - State: testStateUnchanged, - Deferred: &datasource.Deferred{Reason: datasource.DeferredReasonAbsentPrereq}, - }, - }, } for name, testCase := range testCases { diff --git a/internal/fwserver/server_readresource.go b/internal/fwserver/server_readresource.go index c3aa84afd..f2998c4d1 100644 --- a/internal/fwserver/server_readresource.go +++ b/internal/fwserver/server_readresource.go @@ -123,16 +123,6 @@ func (s *Server) ReadResource(ctx context.Context, req *ReadResourceRequest, res return } - if (req.ClientCapabilities == nil || !req.ClientCapabilities.DeferralAllowed) && resp.Deferred != nil { - resp.Diagnostics.AddError( - "Resource Deferral Not Allowed", - "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ - "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "(resource.ReadResponse).Deferred can only be set if (resource.ReadRequest.ClientCapabilities).DeferralAllowed is true.", - ) - return - } - semanticEqualityReq := SchemaSemanticEqualityRequest{ PriorData: fwschemadata.Data{ Description: fwschemadata.DataDescriptionState, diff --git a/internal/fwserver/server_readresource_test.go b/internal/fwserver/server_readresource_test.go index 4ecacd20d..8ab2d910c 100644 --- a/internal/fwserver/server_readresource_test.go +++ b/internal/fwserver/server_readresource_test.go @@ -514,7 +514,7 @@ func TestServerReadResource(t *testing.T) { Private: testPrivate, }, }, - "request-deferral-allowed-response-deferral": { + "response-deferral": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, }, @@ -544,43 +544,6 @@ func TestServerReadResource(t *testing.T) { Deferred: &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq}, }, }, - "request-deferral-not-allowed-response-deferral": { - server: &fwserver.Server{ - Provider: &testprovider.Provider{}, - }, - request: &fwserver.ReadResourceRequest{ - CurrentState: testCurrentState, - Resource: &testprovider.Resource{ - ReadMethod: func(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var data struct { - TestComputed types.String `tfsdk:"test_computed"` - TestRequired types.String `tfsdk:"test_required"` - } - - resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - - resp.Deferred = &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq} - - if data.TestRequired.ValueString() != "test-currentstate-value" { - resp.Diagnostics.AddError("unexpected req.State value: %s", data.TestRequired.ValueString()) - } - }, - }, - }, - expectedResponse: &fwserver.ReadResourceResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Resource Deferral Not Allowed", - "An unexpected error was encountered when reading the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ - "The resource requested a deferral but the Terraform client does not support deferrals, "+ - "(resource.ReadResponse).Deferred can only be set if (resource.ReadRequest.ClientCapabilities).DeferralAllowed is true.", - ), - }, - NewState: testCurrentState, - Private: testEmptyPrivate, - Deferred: &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq}, - }, - }, } for name, testCase := range testCases { From 0a6b8a558992735f73c8a208578c7c635211e1d7 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Mon, 13 May 2024 17:16:28 -0400 Subject: [PATCH 19/25] Remove variable indirection in tests --- internal/fromproto5/planresourcechange_test.go | 6 +++--- internal/fromproto5/readdatasource_test.go | 6 +++--- internal/fromproto5/readresource_test.go | 6 +++--- internal/fromproto6/planresourcechange_test.go | 6 +++--- internal/fromproto6/readdatasource_test.go | 6 +++--- internal/fromproto6/readresource_test.go | 6 +++--- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/internal/fromproto5/planresourcechange_test.go b/internal/fromproto5/planresourcechange_test.go index e629b5d38..3b9e9f861 100644 --- a/internal/fromproto5/planresourcechange_test.go +++ b/internal/fromproto5/planresourcechange_test.go @@ -54,8 +54,6 @@ func TestPlanResourceChangeRequest(t *testing.T) { testProviderData := privatestate.MustProviderData(context.Background(), testProviderKeyValue) - testClientCapabilities := tfprotov5.PlanResourceChangeClientCapabilities{DeferralAllowed: true} - testCases := map[string]struct { input *tfprotov5.PlanResourceChangeRequest resourceSchema fwschema.Schema @@ -221,7 +219,9 @@ func TestPlanResourceChangeRequest(t *testing.T) { }, "client-capabilities": { input: &tfprotov5.PlanResourceChangeRequest{ - ClientCapabilities: &testClientCapabilities, + ClientCapabilities: &tfprotov5.PlanResourceChangeClientCapabilities{ + DeferralAllowed: true, + }, }, resourceSchema: testFwSchema, expected: &fwserver.PlanResourceChangeRequest{ diff --git a/internal/fromproto5/readdatasource_test.go b/internal/fromproto5/readdatasource_test.go index a398674da..aaa9ccd0f 100644 --- a/internal/fromproto5/readdatasource_test.go +++ b/internal/fromproto5/readdatasource_test.go @@ -47,8 +47,6 @@ func TestReadDataSourceRequest(t *testing.T) { }, } - testClientCapabilities := tfprotov5.ReadDataSourceClientCapabilities{DeferralAllowed: true} - testCases := map[string]struct { input *tfprotov5.ReadDataSourceRequest dataSourceSchema fwschema.Schema @@ -140,7 +138,9 @@ func TestReadDataSourceRequest(t *testing.T) { }, "client-capabilities": { input: &tfprotov5.ReadDataSourceRequest{ - ClientCapabilities: &testClientCapabilities, + ClientCapabilities: &tfprotov5.ReadDataSourceClientCapabilities{ + DeferralAllowed: true, + }, }, dataSourceSchema: testFwSchema, expected: &fwserver.ReadDataSourceRequest{ diff --git a/internal/fromproto5/readresource_test.go b/internal/fromproto5/readresource_test.go index 28aadec15..00fbbeb27 100644 --- a/internal/fromproto5/readresource_test.go +++ b/internal/fromproto5/readresource_test.go @@ -56,8 +56,6 @@ func TestReadResourceRequest(t *testing.T) { testEmptyProviderData := privatestate.EmptyProviderData(context.Background()) - testClientCapabilities := tfprotov5.ReadResourceClientCapabilities{DeferralAllowed: true} - testCases := map[string]struct { input *tfprotov5.ReadResourceRequest resourceSchema fwschema.Schema @@ -176,7 +174,9 @@ func TestReadResourceRequest(t *testing.T) { }, "client-capabilities": { input: &tfprotov5.ReadResourceRequest{ - ClientCapabilities: &testClientCapabilities, + ClientCapabilities: &tfprotov5.ReadResourceClientCapabilities{ + DeferralAllowed: true, + }, }, resourceSchema: testFwSchema, expected: &fwserver.ReadResourceRequest{ diff --git a/internal/fromproto6/planresourcechange_test.go b/internal/fromproto6/planresourcechange_test.go index 5b96bbe79..5edfd149e 100644 --- a/internal/fromproto6/planresourcechange_test.go +++ b/internal/fromproto6/planresourcechange_test.go @@ -54,8 +54,6 @@ func TestPlanResourceChangeRequest(t *testing.T) { testProviderData := privatestate.MustProviderData(context.Background(), testProviderKeyValue) - testClientCapabilities := tfprotov6.PlanResourceChangeClientCapabilities{DeferralAllowed: true} - testCases := map[string]struct { input *tfprotov6.PlanResourceChangeRequest resourceSchema fwschema.Schema @@ -221,7 +219,9 @@ func TestPlanResourceChangeRequest(t *testing.T) { }, "client-capabilities": { input: &tfprotov6.PlanResourceChangeRequest{ - ClientCapabilities: &testClientCapabilities, + ClientCapabilities: &tfprotov6.PlanResourceChangeClientCapabilities{ + DeferralAllowed: true, + }, }, resourceSchema: testFwSchema, expected: &fwserver.PlanResourceChangeRequest{ diff --git a/internal/fromproto6/readdatasource_test.go b/internal/fromproto6/readdatasource_test.go index 73332b45a..869092f60 100644 --- a/internal/fromproto6/readdatasource_test.go +++ b/internal/fromproto6/readdatasource_test.go @@ -47,8 +47,6 @@ func TestReadDataSourceRequest(t *testing.T) { }, } - testClientCapabilities := tfprotov6.ReadDataSourceClientCapabilities{DeferralAllowed: true} - testCases := map[string]struct { input *tfprotov6.ReadDataSourceRequest dataSourceSchema fwschema.Schema @@ -140,7 +138,9 @@ func TestReadDataSourceRequest(t *testing.T) { }, "client-capabilities": { input: &tfprotov6.ReadDataSourceRequest{ - ClientCapabilities: &testClientCapabilities, + ClientCapabilities: &tfprotov6.ReadDataSourceClientCapabilities{ + DeferralAllowed: true, + }, }, dataSourceSchema: testFwSchema, expected: &fwserver.ReadDataSourceRequest{ diff --git a/internal/fromproto6/readresource_test.go b/internal/fromproto6/readresource_test.go index a6a238188..6a3eeab98 100644 --- a/internal/fromproto6/readresource_test.go +++ b/internal/fromproto6/readresource_test.go @@ -56,8 +56,6 @@ func TestReadResourceRequest(t *testing.T) { testEmptyProviderData := privatestate.EmptyProviderData(context.Background()) - testClientCapabilities := tfprotov6.ReadResourceClientCapabilities{DeferralAllowed: true} - testCases := map[string]struct { input *tfprotov6.ReadResourceRequest resourceSchema fwschema.Schema @@ -176,7 +174,9 @@ func TestReadResourceRequest(t *testing.T) { }, "client-capabilities": { input: &tfprotov6.ReadResourceRequest{ - ClientCapabilities: &testClientCapabilities, + ClientCapabilities: &tfprotov6.ReadResourceClientCapabilities{ + DeferralAllowed: true, + }, }, resourceSchema: testFwSchema, expected: &fwserver.ReadResourceRequest{ From f1112d5d8e6b51fd51f0a898e62fa54ba2a94c6a Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Mon, 13 May 2024 17:17:57 -0400 Subject: [PATCH 20/25] Add copyright headers --- internal/fromproto5/client_capabilities.go | 3 +++ internal/fromproto6/client_capabilities.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/internal/fromproto5/client_capabilities.go b/internal/fromproto5/client_capabilities.go index fde821ac9..8faebd822 100644 --- a/internal/fromproto5/client_capabilities.go +++ b/internal/fromproto5/client_capabilities.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package fromproto5 import ( diff --git a/internal/fromproto6/client_capabilities.go b/internal/fromproto6/client_capabilities.go index c2f54e498..eac8ff85d 100644 --- a/internal/fromproto6/client_capabilities.go +++ b/internal/fromproto6/client_capabilities.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package fromproto6 import ( From 2433dcaa4edf8602d81d50581d5dd504a7a12304 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Tue, 14 May 2024 13:33:11 -0400 Subject: [PATCH 21/25] Apply suggestions from code review Co-authored-by: Brian Flad --- datasource/read.go | 4 ++-- resource/modify_plan.go | 4 ++-- resource/read.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/datasource/read.go b/datasource/read.go index 751b9754f..43e002368 100644 --- a/datasource/read.go +++ b/datasource/read.go @@ -54,8 +54,8 @@ type ReadResponse struct { // warnings or errors generated. Diagnostics diag.Diagnostics - // Deferred indicates that Terraform should defer - // reading this data source. + // Deferred indicates that Terraform should defer reading this + // data source until a followup apply operation. // // This field can only be set if // `(datasource.ReadRequest).ClientCapabilities.DeferralAllowed` is true. diff --git a/resource/modify_plan.go b/resource/modify_plan.go index 26540513b..28843cec1 100644 --- a/resource/modify_plan.go +++ b/resource/modify_plan.go @@ -82,8 +82,8 @@ type ModifyPlanResponse struct { // generated. Diagnostics diag.Diagnostics - // Deferred indicates that Terraform should defer - // importing this resource. + // Deferred indicates that Terraform should defer importing this + // resource until a followup apply operation. // // This field can only be set if // `(resource.ModifyPlanRequest).ClientCapabilities.DeferralAllowed` is true. diff --git a/resource/read.go b/resource/read.go index 4e4240036..53c4cb832 100644 --- a/resource/read.go +++ b/resource/read.go @@ -67,8 +67,8 @@ type ReadResponse struct { // warnings or errors generated. Diagnostics diag.Diagnostics - // Deferred indicates that Terraform should defer - // importing this resource. + // Deferred indicates that Terraform should defer refreshing this + // resource until a followup plan operation. // // This field can only be set if // `(resource.ReadRequest).ClientCapabilities.DeferralAllowed` is true. From 50aaca97b974b3084f8119bbd949058b67049bcf Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Tue, 14 May 2024 14:10:29 -0400 Subject: [PATCH 22/25] Add unit tests for client capabilities --- .../server_importresourcestate_test.go | 127 +++++++++---- .../server_planresourcechange_test.go | 171 +++++++++++++----- .../fwserver/server_readdatasource_test.go | 115 +++++++++--- internal/fwserver/server_readresource_test.go | 113 +++++++++--- 4 files changed, 390 insertions(+), 136 deletions(-) diff --git a/internal/fwserver/server_importresourcestate_test.go b/internal/fwserver/server_importresourcestate_test.go index 6d5ad88a8..14e63148a 100644 --- a/internal/fwserver/server_importresourcestate_test.go +++ b/internal/fwserver/server_importresourcestate_test.go @@ -99,6 +99,67 @@ func TestServerImportResourceState(t *testing.T) { }, expectedResponse: &fwserver.ImportResourceStateResponse{}, }, + "request-client-capabilities-deferral-allowed": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ImportResourceStateRequest{ + ClientCapabilities: testDeferral, + EmptyState: *testEmptyState, + ID: "test-id", + Resource: &testprovider.ResourceWithImportState{ + Resource: &testprovider.Resource{}, + ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + if req.ClientCapabilities.DeferralAllowed != true { + resp.Diagnostics.AddError("Unexpected req.ClientCapabilities.DeferralAllowed value", + "expected: true but got: false") + } + + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + }, + }, + TypeName: "test_resource", + }, + expectedResponse: &fwserver.ImportResourceStateResponse{ + ImportedResources: []fwserver.ImportedResource{ + { + State: *testState, + TypeName: "test_resource", + Private: testEmptyPrivate, + }, + }, + }, + }, + "request-client-capabilities-unset": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ImportResourceStateRequest{ + EmptyState: *testEmptyState, + ID: "test-id", + Resource: &testprovider.ResourceWithImportState{ + Resource: &testprovider.Resource{}, + ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + if req.ClientCapabilities.DeferralAllowed != false { + resp.Diagnostics.AddError("Unexpected req.ClientCapabilities.DeferralAllowed value", + "expected: false but got: true") + } + + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + }, + }, + TypeName: "test_resource", + }, + expectedResponse: &fwserver.ImportResourceStateResponse{ + ImportedResources: []fwserver.ImportedResource{ + { + State: *testState, + TypeName: "test_resource", + Private: testEmptyPrivate, + }, + }, + }, + }, "request-id": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -249,7 +310,7 @@ func TestServerImportResourceState(t *testing.T) { }, }, }, - "response-importedresources-private": { + "response-importedresources-deferral": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, }, @@ -259,26 +320,33 @@ func TestServerImportResourceState(t *testing.T) { Resource: &testprovider.ResourceWithImportState{ Resource: &testprovider.Resource{}, ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - diags := resp.Private.SetKey(ctx, "providerKeyOne", []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`)) + if req.ID != "test-id" { + resp.Diagnostics.AddError("unexpected req.ID value: %s", req.ID) + } - resp.Diagnostics.Append(diags...) + resp.Deferred = &resource.Deferred{ + Reason: resource.DeferredReasonAbsentPrereq, + } resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + }, }, - TypeName: "test_resource", + TypeName: "test_resource", + ClientCapabilities: testDeferral, }, expectedResponse: &fwserver.ImportResourceStateResponse{ ImportedResources: []fwserver.ImportedResource{ { State: *testState, TypeName: "test_resource", - Private: testPrivate, + Private: testEmptyPrivate, }, }, + Deferred: &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq}, }, }, - "response-importedresources-empty-state": { + "response-importedresources-private": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, }, @@ -288,22 +356,26 @@ func TestServerImportResourceState(t *testing.T) { Resource: &testprovider.ResourceWithImportState{ Resource: &testprovider.Resource{}, ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - // Intentionally empty + diags := resp.Private.SetKey(ctx, "providerKeyOne", []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`)) + + resp.Diagnostics.Append(diags...) + + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) }, }, TypeName: "test_resource", }, expectedResponse: &fwserver.ImportResourceStateResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Missing Resource Import State", - "An unexpected error was encountered when importing the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ - "Resource ImportState method returned no State in response. If import is intentionally not supported, remove the Resource type ImportState method or return an error.", - ), + ImportedResources: []fwserver.ImportedResource{ + { + State: *testState, + TypeName: "test_resource", + Private: testPrivate, + }, }, }, }, - "response-importedresources-deferral": { + "response-importedresources-empty-state": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, }, @@ -313,30 +385,19 @@ func TestServerImportResourceState(t *testing.T) { Resource: &testprovider.ResourceWithImportState{ Resource: &testprovider.Resource{}, ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - if req.ID != "test-id" { - resp.Diagnostics.AddError("unexpected req.ID value: %s", req.ID) - } - - resp.Deferred = &resource.Deferred{ - Reason: resource.DeferredReasonAbsentPrereq, - } - - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) - + // Intentionally empty }, }, - TypeName: "test_resource", - ClientCapabilities: testDeferral, + TypeName: "test_resource", }, expectedResponse: &fwserver.ImportResourceStateResponse{ - ImportedResources: []fwserver.ImportedResource{ - { - State: *testState, - TypeName: "test_resource", - Private: testEmptyPrivate, - }, + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Missing Resource Import State", + "An unexpected error was encountered when importing the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ + "Resource ImportState method returned no State in response. If import is intentionally not supported, remove the Resource type ImportState method or return an error.", + ), }, - Deferred: &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq}, }, }, } diff --git a/internal/fwserver/server_planresourcechange_test.go b/internal/fwserver/server_planresourcechange_test.go index 8342f2c9f..dfbe15ab1 100644 --- a/internal/fwserver/server_planresourcechange_test.go +++ b/internal/fwserver/server_planresourcechange_test.go @@ -2783,6 +2783,91 @@ func TestServerPlanResourceChange(t *testing.T) { PlannedPrivate: testEmptyPrivate, }, }, + "create-resourcewithmodifyplan-request-client-capabilities-deferral-allowed": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.PlanResourceChangeRequest{ + ClientCapabilities: testDeferralAllowed, + Config: &tfsdk.Config{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + ProposedNewState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + PriorState: testEmptyState, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithModifyPlan{ + ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if req.ClientCapabilities.DeferralAllowed != true { + resp.Diagnostics.AddError("Unexpected req.ClientCapabilities.DeferralAllowed value", + "expected: true but got: false") + } + + }, + }, + }, + expectedResponse: &fwserver.PlanResourceChangeResponse{ + PlannedState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + PlannedPrivate: testEmptyPrivate, + }, + }, + "create-resourcewithmodifyplan-request-client-capabilities-unset": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.PlanResourceChangeRequest{ + Config: &tfsdk.Config{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + ProposedNewState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + PriorState: testEmptyState, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithModifyPlan{ + ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if req.ClientCapabilities.DeferralAllowed != false { + resp.Diagnostics.AddError("Unexpected req.ClientCapabilities.DeferralAllowed value", + "expected: false but got: true") + } + + }, + }, + }, + expectedResponse: &fwserver.PlanResourceChangeResponse{ + PlannedState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + PlannedPrivate: testEmptyPrivate, + }, + }, "create-resourcewithmodifyplan-request-config": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -2964,6 +3049,49 @@ func TestServerPlanResourceChange(t *testing.T) { PlannedPrivate: testEmptyPrivate, }, }, + "create-resourcewithmodifyplan-response-deferral": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.PlanResourceChangeRequest{ + ClientCapabilities: testDeferralAllowed, + Config: &tfsdk.Config{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + ProposedNewState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + PriorState: testEmptyState, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithModifyPlan{ + ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if req.ClientCapabilities.DeferralAllowed == true { + resp.Deferred = &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq} + } + + }, + }, + }, + expectedResponse: &fwserver.PlanResourceChangeResponse{ + Deferred: &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq}, + PlannedState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + PlannedPrivate: testEmptyPrivate, + }, + }, "create-resourcewithmodifyplan-response-diagnostics": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -6019,49 +6147,6 @@ func TestServerPlanResourceChange(t *testing.T) { PlannedPrivate: testPrivateProvider, }, }, - "create-resourcewithmodifyplan-response-deferral": { - server: &fwserver.Server{ - Provider: &testprovider.Provider{}, - }, - request: &fwserver.PlanResourceChangeRequest{ - ClientCapabilities: testDeferralAllowed, - Config: &tfsdk.Config{ - Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ - "test_computed": tftypes.NewValue(tftypes.String, nil), - "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), - }), - Schema: testSchema, - }, - ProposedNewState: &tfsdk.Plan{ - Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ - "test_computed": tftypes.NewValue(tftypes.String, nil), - "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), - }), - Schema: testSchema, - }, - PriorState: testEmptyState, - ResourceSchema: testSchema, - Resource: &testprovider.ResourceWithModifyPlan{ - ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if req.ClientCapabilities.DeferralAllowed == true { - resp.Deferred = &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq} - } - - }, - }, - }, - expectedResponse: &fwserver.PlanResourceChangeResponse{ - Deferred: &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq}, - PlannedState: &tfsdk.State{ - Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ - "test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), - "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), - }), - Schema: testSchema, - }, - PlannedPrivate: testEmptyPrivate, - }, - }, } for name, testCase := range testCases { diff --git a/internal/fwserver/server_readdatasource_test.go b/internal/fwserver/server_readdatasource_test.go index 11987b211..6cca8863e 100644 --- a/internal/fwserver/server_readdatasource_test.go +++ b/internal/fwserver/server_readdatasource_test.go @@ -114,6 +114,61 @@ func TestServerReadDataSource(t *testing.T) { }, expectedResponse: &fwserver.ReadDataSourceResponse{}, }, + "request-client-capabilities-deferral-allowed": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ReadDataSourceRequest{ + ClientCapabilities: testDeferralAllowed, + Config: testConfig, + DataSourceSchema: testSchema, + DataSource: &testprovider.DataSource{ + ReadMethod: func(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + if req.ClientCapabilities.DeferralAllowed != true { + resp.Diagnostics.AddError("Unexpected req.ClientCapabilities.DeferralAllowed value", + "expected: true but got: false") + } + + var config struct { + TestComputed types.String `tfsdk:"test_computed"` + TestRequired types.String `tfsdk:"test_required"` + } + + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + }, + }, + }, + expectedResponse: &fwserver.ReadDataSourceResponse{ + State: testStateUnchanged, + }, + }, + "request-client-capabilities-unset": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ReadDataSourceRequest{ + Config: testConfig, + DataSourceSchema: testSchema, + DataSource: &testprovider.DataSource{ + ReadMethod: func(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + if req.ClientCapabilities.DeferralAllowed != false { + resp.Diagnostics.AddError("Unexpected req.ClientCapabilities.DeferralAllowed value", + "expected: false but got: true") + } + + var config struct { + TestComputed types.String `tfsdk:"test_computed"` + TestRequired types.String `tfsdk:"test_required"` + } + + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + }, + }, + }, + expectedResponse: &fwserver.ReadDataSourceResponse{ + State: testStateUnchanged, + }, + }, "request-config": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -208,6 +263,36 @@ func TestServerReadDataSource(t *testing.T) { State: testStateUnchanged, }, }, + "response-deferral": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ReadDataSourceRequest{ + Config: testConfig, + DataSourceSchema: testSchema, + DataSource: &testprovider.DataSource{ + ReadMethod: func(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var config struct { + TestComputed types.String `tfsdk:"test_computed"` + TestRequired types.String `tfsdk:"test_required"` + } + + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + + resp.Deferred = &datasource.Deferred{Reason: datasource.DeferredReasonAbsentPrereq} + + if config.TestRequired.ValueString() != "test-config-value" { + resp.Diagnostics.AddError("unexpected req.Config value: %s", config.TestRequired.ValueString()) + } + }, + }, + ClientCapabilities: testDeferralAllowed, + }, + expectedResponse: &fwserver.ReadDataSourceResponse{ + State: testStateUnchanged, + Deferred: &datasource.Deferred{Reason: datasource.DeferredReasonAbsentPrereq}, + }, + }, "response-diagnostics": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -354,36 +439,6 @@ func TestServerReadDataSource(t *testing.T) { }, }, }, - "response-deferral": { - server: &fwserver.Server{ - Provider: &testprovider.Provider{}, - }, - request: &fwserver.ReadDataSourceRequest{ - Config: testConfig, - DataSourceSchema: testSchema, - DataSource: &testprovider.DataSource{ - ReadMethod: func(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { - var config struct { - TestComputed types.String `tfsdk:"test_computed"` - TestRequired types.String `tfsdk:"test_required"` - } - - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - - resp.Deferred = &datasource.Deferred{Reason: datasource.DeferredReasonAbsentPrereq} - - if config.TestRequired.ValueString() != "test-config-value" { - resp.Diagnostics.AddError("unexpected req.Config value: %s", config.TestRequired.ValueString()) - } - }, - }, - ClientCapabilities: testDeferralAllowed, - }, - expectedResponse: &fwserver.ReadDataSourceResponse{ - State: testStateUnchanged, - Deferred: &datasource.Deferred{Reason: datasource.DeferredReasonAbsentPrereq}, - }, - }, } for name, testCase := range testCases { diff --git a/internal/fwserver/server_readresource_test.go b/internal/fwserver/server_readresource_test.go index 8ab2d910c..e05de42ca 100644 --- a/internal/fwserver/server_readresource_test.go +++ b/internal/fwserver/server_readresource_test.go @@ -150,6 +150,59 @@ func TestServerReadResource(t *testing.T) { }, expectedResponse: &fwserver.ReadResourceResponse{}, }, + "request-client-capabilities-deferral-allowed": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ReadResourceRequest{ + ClientCapabilities: testDeferralAllowed, + CurrentState: testCurrentState, + Resource: &testprovider.Resource{ + ReadMethod: func(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + if req.ClientCapabilities.DeferralAllowed != true { + resp.Diagnostics.AddError("Unexpected req.ClientCapabilities.DeferralAllowed value", + "expected: true but got: false") + } + var data struct { + TestComputed types.String `tfsdk:"test_computed"` + TestRequired types.String `tfsdk:"test_required"` + } + + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + }, + }, + }, + expectedResponse: &fwserver.ReadResourceResponse{ + NewState: testCurrentState, + Private: testEmptyPrivate, + }, + }, + "request-client-capabilities-unset": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ReadResourceRequest{ + CurrentState: testCurrentState, + Resource: &testprovider.Resource{ + ReadMethod: func(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + if req.ClientCapabilities.DeferralAllowed != false { + resp.Diagnostics.AddError("Unexpected req.ClientCapabilities.DeferralAllowed value", + "expected: false but got: true") + } + var data struct { + TestComputed types.String `tfsdk:"test_computed"` + TestRequired types.String `tfsdk:"test_required"` + } + + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + }, + }, + }, + expectedResponse: &fwserver.ReadResourceResponse{ + NewState: testCurrentState, + Private: testEmptyPrivate, + }, + }, "request-currentstate-missing": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -312,6 +365,36 @@ func TestServerReadResource(t *testing.T) { Private: testEmptyPrivate, }, }, + "response-deferral": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ReadResourceRequest{ + CurrentState: testCurrentState, + Resource: &testprovider.Resource{ + ReadMethod: func(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data struct { + TestComputed types.String `tfsdk:"test_computed"` + TestRequired types.String `tfsdk:"test_required"` + } + + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + resp.Deferred = &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq} + + if data.TestRequired.ValueString() != "test-currentstate-value" { + resp.Diagnostics.AddError("unexpected req.State value: %s", data.TestRequired.ValueString()) + } + }, + }, + ClientCapabilities: testDeferralAllowed, + }, + expectedResponse: &fwserver.ReadResourceResponse{ + NewState: testCurrentState, + Private: testEmptyPrivate, + Deferred: &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq}, + }, + }, "response-diagnostics": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -514,36 +597,6 @@ func TestServerReadResource(t *testing.T) { Private: testPrivate, }, }, - "response-deferral": { - server: &fwserver.Server{ - Provider: &testprovider.Provider{}, - }, - request: &fwserver.ReadResourceRequest{ - CurrentState: testCurrentState, - Resource: &testprovider.Resource{ - ReadMethod: func(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var data struct { - TestComputed types.String `tfsdk:"test_computed"` - TestRequired types.String `tfsdk:"test_required"` - } - - resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - - resp.Deferred = &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq} - - if data.TestRequired.ValueString() != "test-currentstate-value" { - resp.Diagnostics.AddError("unexpected req.State value: %s", data.TestRequired.ValueString()) - } - }, - }, - ClientCapabilities: testDeferralAllowed, - }, - expectedResponse: &fwserver.ReadResourceResponse{ - NewState: testCurrentState, - Private: testEmptyPrivate, - Deferred: &resource.Deferred{Reason: resource.DeferredReasonAbsentPrereq}, - }, - }, } for name, testCase := range testCases { From 5ab0c24a87d972487d3e50c3aa64193e963e35d9 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Tue, 14 May 2024 16:23:48 -0400 Subject: [PATCH 23/25] Move client capabilities defaulting behavior to `fromproto5/6` package --- internal/fromproto5/client_capabilities.go | 44 ++++++++++--------- .../fromproto5/importresourcestate_test.go | 15 ++++++- .../fromproto5/planresourcechange_test.go | 12 ++++- internal/fromproto5/readdatasource_test.go | 12 ++++- internal/fromproto5/readresource_test.go | 11 ++++- internal/fromproto6/client_capabilities.go | 44 ++++++++++--------- .../fromproto6/importresourcestate_test.go | 15 ++++++- .../fromproto6/planresourcechange_test.go | 12 ++++- internal/fromproto6/readdatasource_test.go | 12 ++++- internal/fromproto6/readresource_test.go | 11 ++++- .../fwserver/server_importresourcestate.go | 9 ++-- .../server_importresourcestate_test.go | 32 +------------- .../fwserver/server_planresourcechange.go | 15 +++---- .../server_planresourcechange_test.go | 44 +------------------ internal/fwserver/server_readdatasource.go | 7 +-- .../fwserver/server_readdatasource_test.go | 29 +----------- internal/fwserver/server_readresource.go | 7 +-- internal/fwserver/server_readresource_test.go | 28 +----------- 18 files changed, 157 insertions(+), 202 deletions(-) diff --git a/internal/fromproto5/client_capabilities.go b/internal/fromproto5/client_capabilities.go index 8faebd822..8f1075737 100644 --- a/internal/fromproto5/client_capabilities.go +++ b/internal/fromproto5/client_capabilities.go @@ -10,50 +10,54 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource" ) -func ReadDataSourceClientCapabilities(in *tfprotov5.ReadDataSourceClientCapabilities) *datasource.ReadClientCapabilities { +func ReadDataSourceClientCapabilities(in *tfprotov5.ReadDataSourceClientCapabilities) datasource.ReadClientCapabilities { if in == nil { - return nil + // Client did not indicate any supported capabilities + return datasource.ReadClientCapabilities{ + DeferralAllowed: false, + } } - resp := &datasource.ReadClientCapabilities{ + return datasource.ReadClientCapabilities{ DeferralAllowed: in.DeferralAllowed, } - - return resp } -func ReadResourceClientCapabilities(in *tfprotov5.ReadResourceClientCapabilities) *resource.ReadClientCapabilities { +func ReadResourceClientCapabilities(in *tfprotov5.ReadResourceClientCapabilities) resource.ReadClientCapabilities { if in == nil { - return nil + // Client did not indicate any supported capabilities + return resource.ReadClientCapabilities{ + DeferralAllowed: false, + } } - resp := &resource.ReadClientCapabilities{ + return resource.ReadClientCapabilities{ DeferralAllowed: in.DeferralAllowed, } - - return resp } -func ModifyPlanClientCapabilities(in *tfprotov5.PlanResourceChangeClientCapabilities) *resource.ModifyPlanClientCapabilities { +func ModifyPlanClientCapabilities(in *tfprotov5.PlanResourceChangeClientCapabilities) resource.ModifyPlanClientCapabilities { if in == nil { - return nil + // Client did not indicate any supported capabilities + return resource.ModifyPlanClientCapabilities{ + DeferralAllowed: false, + } } - resp := &resource.ModifyPlanClientCapabilities{ + return resource.ModifyPlanClientCapabilities{ DeferralAllowed: in.DeferralAllowed, } - - return resp } -func ImportStateClientCapabilities(in *tfprotov5.ImportResourceStateClientCapabilities) *resource.ImportStateClientCapabilities { +func ImportStateClientCapabilities(in *tfprotov5.ImportResourceStateClientCapabilities) resource.ImportStateClientCapabilities { if in == nil { - return nil + // Client did not indicate any supported capabilities + return resource.ImportStateClientCapabilities{ + DeferralAllowed: false, + } } - resp := &resource.ImportStateClientCapabilities{ + return resource.ImportStateClientCapabilities{ DeferralAllowed: in.DeferralAllowed, } - - return resp } diff --git a/internal/fromproto5/importresourcestate_test.go b/internal/fromproto5/importresourcestate_test.go index f3de43bcf..ba58522af 100644 --- a/internal/fromproto5/importresourcestate_test.go +++ b/internal/fromproto5/importresourcestate_test.go @@ -98,11 +98,24 @@ func TestImportResourceStateRequest(t *testing.T) { expected: &fwserver.ImportResourceStateRequest{ EmptyState: testFwEmptyState, ID: "test-id", - ClientCapabilities: &resource.ImportStateClientCapabilities{ + ClientCapabilities: resource.ImportStateClientCapabilities{ DeferralAllowed: true, }, }, }, + "client-capabilities-unset": { + input: &tfprotov5.ImportResourceStateRequest{ + ID: "test-id", + }, + resourceSchema: testFwSchema, + expected: &fwserver.ImportResourceStateRequest{ + EmptyState: testFwEmptyState, + ID: "test-id", + ClientCapabilities: resource.ImportStateClientCapabilities{ + DeferralAllowed: false, + }, + }, + }, } for name, testCase := range testCases { diff --git a/internal/fromproto5/planresourcechange_test.go b/internal/fromproto5/planresourcechange_test.go index 3b9e9f861..f20a94c7e 100644 --- a/internal/fromproto5/planresourcechange_test.go +++ b/internal/fromproto5/planresourcechange_test.go @@ -225,12 +225,22 @@ func TestPlanResourceChangeRequest(t *testing.T) { }, resourceSchema: testFwSchema, expected: &fwserver.PlanResourceChangeRequest{ - ClientCapabilities: &resource.ModifyPlanClientCapabilities{ + ClientCapabilities: resource.ModifyPlanClientCapabilities{ DeferralAllowed: true, }, ResourceSchema: testFwSchema, }, }, + "client-capabilities-unset": { + input: &tfprotov5.PlanResourceChangeRequest{}, + resourceSchema: testFwSchema, + expected: &fwserver.PlanResourceChangeRequest{ + ClientCapabilities: resource.ModifyPlanClientCapabilities{ + DeferralAllowed: false, + }, + ResourceSchema: testFwSchema, + }, + }, } for name, testCase := range testCases { diff --git a/internal/fromproto5/readdatasource_test.go b/internal/fromproto5/readdatasource_test.go index aaa9ccd0f..cbc054e2d 100644 --- a/internal/fromproto5/readdatasource_test.go +++ b/internal/fromproto5/readdatasource_test.go @@ -145,11 +145,21 @@ func TestReadDataSourceRequest(t *testing.T) { dataSourceSchema: testFwSchema, expected: &fwserver.ReadDataSourceRequest{ DataSourceSchema: testFwSchema, - ClientCapabilities: &datasource.ReadClientCapabilities{ + ClientCapabilities: datasource.ReadClientCapabilities{ DeferralAllowed: true, }, }, }, + "client-capabilities-unset": { + input: &tfprotov5.ReadDataSourceRequest{}, + dataSourceSchema: testFwSchema, + expected: &fwserver.ReadDataSourceRequest{ + DataSourceSchema: testFwSchema, + ClientCapabilities: datasource.ReadClientCapabilities{ + DeferralAllowed: false, + }, + }, + }, } for name, testCase := range testCases { diff --git a/internal/fromproto5/readresource_test.go b/internal/fromproto5/readresource_test.go index 00fbbeb27..fb19e9041 100644 --- a/internal/fromproto5/readresource_test.go +++ b/internal/fromproto5/readresource_test.go @@ -180,11 +180,20 @@ func TestReadResourceRequest(t *testing.T) { }, resourceSchema: testFwSchema, expected: &fwserver.ReadResourceRequest{ - ClientCapabilities: &resource.ReadClientCapabilities{ + ClientCapabilities: resource.ReadClientCapabilities{ DeferralAllowed: true, }, }, }, + "client-capabilities-unset": { + input: &tfprotov5.ReadResourceRequest{}, + resourceSchema: testFwSchema, + expected: &fwserver.ReadResourceRequest{ + ClientCapabilities: resource.ReadClientCapabilities{ + DeferralAllowed: false, + }, + }, + }, } for name, testCase := range testCases { diff --git a/internal/fromproto6/client_capabilities.go b/internal/fromproto6/client_capabilities.go index eac8ff85d..f459291ab 100644 --- a/internal/fromproto6/client_capabilities.go +++ b/internal/fromproto6/client_capabilities.go @@ -10,50 +10,54 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource" ) -func ReadDataSourceClientCapabilities(in *tfprotov6.ReadDataSourceClientCapabilities) *datasource.ReadClientCapabilities { +func ReadDataSourceClientCapabilities(in *tfprotov6.ReadDataSourceClientCapabilities) datasource.ReadClientCapabilities { if in == nil { - return nil + // Client did not indicate any supported capabilities + return datasource.ReadClientCapabilities{ + DeferralAllowed: false, + } } - resp := &datasource.ReadClientCapabilities{ + return datasource.ReadClientCapabilities{ DeferralAllowed: in.DeferralAllowed, } - - return resp } -func ReadResourceClientCapabilities(in *tfprotov6.ReadResourceClientCapabilities) *resource.ReadClientCapabilities { +func ReadResourceClientCapabilities(in *tfprotov6.ReadResourceClientCapabilities) resource.ReadClientCapabilities { if in == nil { - return nil + // Client did not indicate any supported capabilities + return resource.ReadClientCapabilities{ + DeferralAllowed: false, + } } - resp := &resource.ReadClientCapabilities{ + return resource.ReadClientCapabilities{ DeferralAllowed: in.DeferralAllowed, } - - return resp } -func ModifyPlanClientCapabilities(in *tfprotov6.PlanResourceChangeClientCapabilities) *resource.ModifyPlanClientCapabilities { +func ModifyPlanClientCapabilities(in *tfprotov6.PlanResourceChangeClientCapabilities) resource.ModifyPlanClientCapabilities { if in == nil { - return nil + // Client did not indicate any supported capabilities + return resource.ModifyPlanClientCapabilities{ + DeferralAllowed: false, + } } - resp := &resource.ModifyPlanClientCapabilities{ + return resource.ModifyPlanClientCapabilities{ DeferralAllowed: in.DeferralAllowed, } - - return resp } -func ImportStateClientCapabilities(in *tfprotov6.ImportResourceStateClientCapabilities) *resource.ImportStateClientCapabilities { +func ImportStateClientCapabilities(in *tfprotov6.ImportResourceStateClientCapabilities) resource.ImportStateClientCapabilities { if in == nil { - return nil + // Client did not indicate any supported capabilities + return resource.ImportStateClientCapabilities{ + DeferralAllowed: false, + } } - resp := &resource.ImportStateClientCapabilities{ + return resource.ImportStateClientCapabilities{ DeferralAllowed: in.DeferralAllowed, } - - return resp } diff --git a/internal/fromproto6/importresourcestate_test.go b/internal/fromproto6/importresourcestate_test.go index fb11a340d..6c72913f7 100644 --- a/internal/fromproto6/importresourcestate_test.go +++ b/internal/fromproto6/importresourcestate_test.go @@ -98,11 +98,24 @@ func TestImportResourceStateRequest(t *testing.T) { expected: &fwserver.ImportResourceStateRequest{ EmptyState: testFwEmptyState, ID: "test-id", - ClientCapabilities: &resource.ImportStateClientCapabilities{ + ClientCapabilities: resource.ImportStateClientCapabilities{ DeferralAllowed: true, }, }, }, + "client-capabilities-unset": { + input: &tfprotov6.ImportResourceStateRequest{ + ID: "test-id", + }, + resourceSchema: testFwSchema, + expected: &fwserver.ImportResourceStateRequest{ + EmptyState: testFwEmptyState, + ID: "test-id", + ClientCapabilities: resource.ImportStateClientCapabilities{ + DeferralAllowed: false, + }, + }, + }, } for name, testCase := range testCases { diff --git a/internal/fromproto6/planresourcechange_test.go b/internal/fromproto6/planresourcechange_test.go index 5edfd149e..e30dfe99d 100644 --- a/internal/fromproto6/planresourcechange_test.go +++ b/internal/fromproto6/planresourcechange_test.go @@ -225,12 +225,22 @@ func TestPlanResourceChangeRequest(t *testing.T) { }, resourceSchema: testFwSchema, expected: &fwserver.PlanResourceChangeRequest{ - ClientCapabilities: &resource.ModifyPlanClientCapabilities{ + ClientCapabilities: resource.ModifyPlanClientCapabilities{ DeferralAllowed: true, }, ResourceSchema: testFwSchema, }, }, + "client-capabilities-unset": { + input: &tfprotov6.PlanResourceChangeRequest{}, + resourceSchema: testFwSchema, + expected: &fwserver.PlanResourceChangeRequest{ + ClientCapabilities: resource.ModifyPlanClientCapabilities{ + DeferralAllowed: false, + }, + ResourceSchema: testFwSchema, + }, + }, } for name, testCase := range testCases { diff --git a/internal/fromproto6/readdatasource_test.go b/internal/fromproto6/readdatasource_test.go index 869092f60..08ef5124a 100644 --- a/internal/fromproto6/readdatasource_test.go +++ b/internal/fromproto6/readdatasource_test.go @@ -145,11 +145,21 @@ func TestReadDataSourceRequest(t *testing.T) { dataSourceSchema: testFwSchema, expected: &fwserver.ReadDataSourceRequest{ DataSourceSchema: testFwSchema, - ClientCapabilities: &datasource.ReadClientCapabilities{ + ClientCapabilities: datasource.ReadClientCapabilities{ DeferralAllowed: true, }, }, }, + "client-capabilities-unset": { + input: &tfprotov6.ReadDataSourceRequest{}, + dataSourceSchema: testFwSchema, + expected: &fwserver.ReadDataSourceRequest{ + DataSourceSchema: testFwSchema, + ClientCapabilities: datasource.ReadClientCapabilities{ + DeferralAllowed: false, + }, + }, + }, } for name, testCase := range testCases { diff --git a/internal/fromproto6/readresource_test.go b/internal/fromproto6/readresource_test.go index 6a3eeab98..03d6eea86 100644 --- a/internal/fromproto6/readresource_test.go +++ b/internal/fromproto6/readresource_test.go @@ -180,11 +180,20 @@ func TestReadResourceRequest(t *testing.T) { }, resourceSchema: testFwSchema, expected: &fwserver.ReadResourceRequest{ - ClientCapabilities: &resource.ReadClientCapabilities{ + ClientCapabilities: resource.ReadClientCapabilities{ DeferralAllowed: true, }, }, }, + "client-capabilities-unset": { + input: &tfprotov6.ReadResourceRequest{}, + resourceSchema: testFwSchema, + expected: &fwserver.ReadResourceRequest{ + ClientCapabilities: resource.ReadClientCapabilities{ + DeferralAllowed: false, + }, + }, + }, } for name, testCase := range testCases { diff --git a/internal/fwserver/server_importresourcestate.go b/internal/fwserver/server_importresourcestate.go index 54782c572..22d8da5a1 100644 --- a/internal/fwserver/server_importresourcestate.go +++ b/internal/fwserver/server_importresourcestate.go @@ -36,7 +36,7 @@ type ImportResourceStateRequest struct { // the ImportedResource TypeName of the ImportResourceStateResponse. TypeName string - ClientCapabilities *resource.ImportStateClientCapabilities + ClientCapabilities resource.ImportStateClientCapabilities } // ImportResourceStateResponse is the framework server response for the @@ -93,11 +93,8 @@ func (s *Server) ImportResourceState(ctx context.Context, req *ImportResourceSta } importReq := resource.ImportStateRequest{ - ID: req.ID, - } - - if req.ClientCapabilities != nil { - importReq.ClientCapabilities = *req.ClientCapabilities + ID: req.ID, + ClientCapabilities: req.ClientCapabilities, } privateProviderData := privatestate.EmptyProviderData(ctx) diff --git a/internal/fwserver/server_importresourcestate_test.go b/internal/fwserver/server_importresourcestate_test.go index 14e63148a..26eca6913 100644 --- a/internal/fwserver/server_importresourcestate_test.go +++ b/internal/fwserver/server_importresourcestate_test.go @@ -84,7 +84,7 @@ func TestServerImportResourceState(t *testing.T) { Provider: testEmptyProviderData, } - testDeferral := &resource.ImportStateClientCapabilities{ + testDeferral := resource.ImportStateClientCapabilities{ DeferralAllowed: true, } @@ -130,36 +130,6 @@ func TestServerImportResourceState(t *testing.T) { }, }, }, - "request-client-capabilities-unset": { - server: &fwserver.Server{ - Provider: &testprovider.Provider{}, - }, - request: &fwserver.ImportResourceStateRequest{ - EmptyState: *testEmptyState, - ID: "test-id", - Resource: &testprovider.ResourceWithImportState{ - Resource: &testprovider.Resource{}, - ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - if req.ClientCapabilities.DeferralAllowed != false { - resp.Diagnostics.AddError("Unexpected req.ClientCapabilities.DeferralAllowed value", - "expected: false but got: true") - } - - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) - }, - }, - TypeName: "test_resource", - }, - expectedResponse: &fwserver.ImportResourceStateResponse{ - ImportedResources: []fwserver.ImportedResource{ - { - State: *testState, - TypeName: "test_resource", - Private: testEmptyPrivate, - }, - }, - }, - }, "request-id": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, diff --git a/internal/fwserver/server_planresourcechange.go b/internal/fwserver/server_planresourcechange.go index 84477e33d..3a0acb6ca 100644 --- a/internal/fwserver/server_planresourcechange.go +++ b/internal/fwserver/server_planresourcechange.go @@ -26,7 +26,7 @@ import ( // PlanResourceChangeRequest is the framework server request for the // PlanResourceChange RPC. type PlanResourceChangeRequest struct { - ClientCapabilities *resource.ModifyPlanClientCapabilities + ClientCapabilities resource.ModifyPlanClientCapabilities Config *tfsdk.Config PriorPrivate *privatestate.Data PriorState *tfsdk.State @@ -262,20 +262,17 @@ func (s *Server) PlanResourceChange(ctx context.Context, req *PlanResourceChange logging.FrameworkTrace(ctx, "Resource implements ResourceWithModifyPlan") modifyPlanReq := resource.ModifyPlanRequest{ - Config: *req.Config, - Plan: stateToPlan(*resp.PlannedState), - State: *req.PriorState, - Private: resp.PlannedPrivate.Provider, + ClientCapabilities: req.ClientCapabilities, + Config: *req.Config, + Plan: stateToPlan(*resp.PlannedState), + State: *req.PriorState, + Private: resp.PlannedPrivate.Provider, } if req.ProviderMeta != nil { modifyPlanReq.ProviderMeta = *req.ProviderMeta } - if req.ClientCapabilities != nil { - modifyPlanReq.ClientCapabilities = *req.ClientCapabilities - } - modifyPlanResp := resource.ModifyPlanResponse{ Diagnostics: resp.Diagnostics, Plan: modifyPlanReq.Plan, diff --git a/internal/fwserver/server_planresourcechange_test.go b/internal/fwserver/server_planresourcechange_test.go index dfbe15ab1..1a60eda38 100644 --- a/internal/fwserver/server_planresourcechange_test.go +++ b/internal/fwserver/server_planresourcechange_test.go @@ -1230,7 +1230,7 @@ func TestServerPlanResourceChange(t *testing.T) { Provider: testEmptyProviderData, } - testDeferralAllowed := &resource.ModifyPlanClientCapabilities{ + testDeferralAllowed := resource.ModifyPlanClientCapabilities{ DeferralAllowed: true, } @@ -2826,48 +2826,6 @@ func TestServerPlanResourceChange(t *testing.T) { PlannedPrivate: testEmptyPrivate, }, }, - "create-resourcewithmodifyplan-request-client-capabilities-unset": { - server: &fwserver.Server{ - Provider: &testprovider.Provider{}, - }, - request: &fwserver.PlanResourceChangeRequest{ - Config: &tfsdk.Config{ - Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ - "test_computed": tftypes.NewValue(tftypes.String, nil), - "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), - }), - Schema: testSchema, - }, - ProposedNewState: &tfsdk.Plan{ - Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ - "test_computed": tftypes.NewValue(tftypes.String, nil), - "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), - }), - Schema: testSchema, - }, - PriorState: testEmptyState, - ResourceSchema: testSchema, - Resource: &testprovider.ResourceWithModifyPlan{ - ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if req.ClientCapabilities.DeferralAllowed != false { - resp.Diagnostics.AddError("Unexpected req.ClientCapabilities.DeferralAllowed value", - "expected: false but got: true") - } - - }, - }, - }, - expectedResponse: &fwserver.PlanResourceChangeResponse{ - PlannedState: &tfsdk.State{ - Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ - "test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), - "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), - }), - Schema: testSchema, - }, - PlannedPrivate: testEmptyPrivate, - }, - }, "create-resourcewithmodifyplan-request-config": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, diff --git a/internal/fwserver/server_readdatasource.go b/internal/fwserver/server_readdatasource.go index c640179d4..ee0c4fb07 100644 --- a/internal/fwserver/server_readdatasource.go +++ b/internal/fwserver/server_readdatasource.go @@ -17,7 +17,7 @@ import ( // ReadDataSourceRequest is the framework server request for the // ReadDataSource RPC. type ReadDataSourceRequest struct { - ClientCapabilities *datasource.ReadClientCapabilities + ClientCapabilities datasource.ReadClientCapabilities Config *tfsdk.Config DataSourceSchema fwschema.Schema DataSource datasource.DataSource @@ -58,6 +58,7 @@ func (s *Server) ReadDataSource(ctx context.Context, req *ReadDataSourceRequest, } readReq := datasource.ReadRequest{ + ClientCapabilities: req.ClientCapabilities, Config: tfsdk.Config{ Schema: req.DataSourceSchema, }, @@ -77,10 +78,6 @@ func (s *Server) ReadDataSource(ctx context.Context, req *ReadDataSourceRequest, readReq.ProviderMeta = *req.ProviderMeta } - if req.ClientCapabilities != nil { - readReq.ClientCapabilities = *req.ClientCapabilities - } - logging.FrameworkTrace(ctx, "Calling provider defined DataSource Read") req.DataSource.Read(ctx, readReq, &readResp) logging.FrameworkTrace(ctx, "Called provider defined DataSource Read") diff --git a/internal/fwserver/server_readdatasource_test.go b/internal/fwserver/server_readdatasource_test.go index 6cca8863e..2a7a645e4 100644 --- a/internal/fwserver/server_readdatasource_test.go +++ b/internal/fwserver/server_readdatasource_test.go @@ -99,7 +99,7 @@ func TestServerReadDataSource(t *testing.T) { Schema: testSchema, } - testDeferralAllowed := &datasource.ReadClientCapabilities{ + testDeferralAllowed := datasource.ReadClientCapabilities{ DeferralAllowed: true, } @@ -142,33 +142,6 @@ func TestServerReadDataSource(t *testing.T) { State: testStateUnchanged, }, }, - "request-client-capabilities-unset": { - server: &fwserver.Server{ - Provider: &testprovider.Provider{}, - }, - request: &fwserver.ReadDataSourceRequest{ - Config: testConfig, - DataSourceSchema: testSchema, - DataSource: &testprovider.DataSource{ - ReadMethod: func(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { - if req.ClientCapabilities.DeferralAllowed != false { - resp.Diagnostics.AddError("Unexpected req.ClientCapabilities.DeferralAllowed value", - "expected: false but got: true") - } - - var config struct { - TestComputed types.String `tfsdk:"test_computed"` - TestRequired types.String `tfsdk:"test_required"` - } - - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - }, - }, - }, - expectedResponse: &fwserver.ReadDataSourceResponse{ - State: testStateUnchanged, - }, - }, "request-config": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, diff --git a/internal/fwserver/server_readresource.go b/internal/fwserver/server_readresource.go index f2998c4d1..2ec8e6cc7 100644 --- a/internal/fwserver/server_readresource.go +++ b/internal/fwserver/server_readresource.go @@ -17,7 +17,7 @@ import ( // ReadResourceRequest is the framework server request for the // ReadResource RPC. type ReadResourceRequest struct { - ClientCapabilities *resource.ReadClientCapabilities + ClientCapabilities resource.ReadClientCapabilities CurrentState *tfsdk.State Resource resource.Resource Private *privatestate.Data @@ -69,6 +69,7 @@ func (s *Server) ReadResource(ctx context.Context, req *ReadResourceRequest, res } readReq := resource.ReadRequest{ + ClientCapabilities: req.ClientCapabilities, State: tfsdk.State{ Schema: req.CurrentState.Schema, Raw: req.CurrentState.Raw.Copy(), @@ -85,10 +86,6 @@ func (s *Server) ReadResource(ctx context.Context, req *ReadResourceRequest, res readReq.ProviderMeta = *req.ProviderMeta } - if req.ClientCapabilities != nil { - readReq.ClientCapabilities = *req.ClientCapabilities - } - privateProviderData := privatestate.EmptyProviderData(ctx) readReq.Private = privateProviderData diff --git a/internal/fwserver/server_readresource_test.go b/internal/fwserver/server_readresource_test.go index e05de42ca..964de02dc 100644 --- a/internal/fwserver/server_readresource_test.go +++ b/internal/fwserver/server_readresource_test.go @@ -135,7 +135,7 @@ func TestServerReadResource(t *testing.T) { Provider: testEmptyProviderData, } - testDeferralAllowed := &resource.ReadClientCapabilities{ + testDeferralAllowed := resource.ReadClientCapabilities{ DeferralAllowed: true, } @@ -177,32 +177,6 @@ func TestServerReadResource(t *testing.T) { Private: testEmptyPrivate, }, }, - "request-client-capabilities-unset": { - server: &fwserver.Server{ - Provider: &testprovider.Provider{}, - }, - request: &fwserver.ReadResourceRequest{ - CurrentState: testCurrentState, - Resource: &testprovider.Resource{ - ReadMethod: func(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - if req.ClientCapabilities.DeferralAllowed != false { - resp.Diagnostics.AddError("Unexpected req.ClientCapabilities.DeferralAllowed value", - "expected: false but got: true") - } - var data struct { - TestComputed types.String `tfsdk:"test_computed"` - TestRequired types.String `tfsdk:"test_required"` - } - - resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - }, - }, - }, - expectedResponse: &fwserver.ReadResourceResponse{ - NewState: testCurrentState, - Private: testEmptyPrivate, - }, - }, "request-currentstate-missing": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, From 038d486cc33cb09a72141cc4df9bad23f578750b Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Tue, 14 May 2024 16:36:14 -0400 Subject: [PATCH 24/25] Move `toproto5/6` `Deferred` conversion handling to its own files --- internal/toproto5/deferred.go | 29 ++++++++++++++++++++++++ internal/toproto5/importresourcestate.go | 7 +----- internal/toproto5/planresourcechange.go | 7 +----- internal/toproto5/readdatasource.go | 7 +----- internal/toproto5/readresource.go | 7 +----- internal/toproto6/deferred.go | 29 ++++++++++++++++++++++++ 6 files changed, 62 insertions(+), 24 deletions(-) create mode 100644 internal/toproto5/deferred.go create mode 100644 internal/toproto6/deferred.go diff --git a/internal/toproto5/deferred.go b/internal/toproto5/deferred.go new file mode 100644 index 000000000..3c5c4b1dc --- /dev/null +++ b/internal/toproto5/deferred.go @@ -0,0 +1,29 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto5 + +import ( + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func DataSourceDeferred(fw *datasource.Deferred) *tfprotov5.Deferred { + if fw == nil { + return nil + } + return &tfprotov5.Deferred{ + Reason: tfprotov5.DeferredReason(fw.Reason), + } +} + +func ResourceDeferred(fw *resource.Deferred) *tfprotov5.Deferred { + if fw == nil { + return nil + } + return &tfprotov5.Deferred{ + Reason: tfprotov5.DeferredReason(fw.Reason), + } +} diff --git a/internal/toproto5/importresourcestate.go b/internal/toproto5/importresourcestate.go index 92e438c42..7d29a5ca1 100644 --- a/internal/toproto5/importresourcestate.go +++ b/internal/toproto5/importresourcestate.go @@ -19,6 +19,7 @@ func ImportResourceStateResponse(ctx context.Context, fw *fwserver.ImportResourc } proto5 := &tfprotov5.ImportResourceStateResponse{ + Deferred: ResourceDeferred(fw.Deferred), Diagnostics: Diagnostics(ctx, fw.Diagnostics), } @@ -34,11 +35,5 @@ func ImportResourceStateResponse(ctx context.Context, fw *fwserver.ImportResourc proto5.ImportedResources = append(proto5.ImportedResources, proto5ImportedResource) } - if fw.Deferred != nil { - proto5.Deferred = &tfprotov5.Deferred{ - Reason: tfprotov5.DeferredReason(fw.Deferred.Reason), - } - } - return proto5 } diff --git a/internal/toproto5/planresourcechange.go b/internal/toproto5/planresourcechange.go index 6b2abcc75..f3292a963 100644 --- a/internal/toproto5/planresourcechange.go +++ b/internal/toproto5/planresourcechange.go @@ -20,6 +20,7 @@ func PlanResourceChangeResponse(ctx context.Context, fw *fwserver.PlanResourceCh } proto5 := &tfprotov5.PlanResourceChangeResponse{ + Deferred: ResourceDeferred(fw.Deferred), Diagnostics: Diagnostics(ctx, fw.Diagnostics), } @@ -38,11 +39,5 @@ func PlanResourceChangeResponse(ctx context.Context, fw *fwserver.PlanResourceCh proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) proto5.PlannedPrivate = plannedPrivate - if fw.Deferred != nil { - proto5.Deferred = &tfprotov5.Deferred{ - Reason: tfprotov5.DeferredReason(fw.Deferred.Reason), - } - } - return proto5 } diff --git a/internal/toproto5/readdatasource.go b/internal/toproto5/readdatasource.go index f70e6e186..3c3e28629 100644 --- a/internal/toproto5/readdatasource.go +++ b/internal/toproto5/readdatasource.go @@ -19,6 +19,7 @@ func ReadDataSourceResponse(ctx context.Context, fw *fwserver.ReadDataSourceResp } proto5 := &tfprotov5.ReadDataSourceResponse{ + Deferred: DataSourceDeferred(fw.Deferred), Diagnostics: Diagnostics(ctx, fw.Diagnostics), } @@ -27,11 +28,5 @@ func ReadDataSourceResponse(ctx context.Context, fw *fwserver.ReadDataSourceResp proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) proto5.State = state - if fw.Deferred != nil { - proto5.Deferred = &tfprotov5.Deferred{ - Reason: tfprotov5.DeferredReason(fw.Deferred.Reason), - } - } - return proto5 } diff --git a/internal/toproto5/readresource.go b/internal/toproto5/readresource.go index 5f6ff7aef..9193a3950 100644 --- a/internal/toproto5/readresource.go +++ b/internal/toproto5/readresource.go @@ -19,6 +19,7 @@ func ReadResourceResponse(ctx context.Context, fw *fwserver.ReadResourceResponse } proto5 := &tfprotov5.ReadResourceResponse{ + Deferred: ResourceDeferred(fw.Deferred), Diagnostics: Diagnostics(ctx, fw.Diagnostics), } @@ -32,11 +33,5 @@ func ReadResourceResponse(ctx context.Context, fw *fwserver.ReadResourceResponse proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) proto5.Private = newPrivate - if fw.Deferred != nil { - proto5.Deferred = &tfprotov5.Deferred{ - Reason: tfprotov5.DeferredReason(fw.Deferred.Reason), - } - } - return proto5 } diff --git a/internal/toproto6/deferred.go b/internal/toproto6/deferred.go new file mode 100644 index 000000000..10afa8fdc --- /dev/null +++ b/internal/toproto6/deferred.go @@ -0,0 +1,29 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto6 + +import ( + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func DataSourceDeferred(fw *datasource.Deferred) *tfprotov6.Deferred { + if fw == nil { + return nil + } + return &tfprotov6.Deferred{ + Reason: tfprotov6.DeferredReason(fw.Reason), + } +} + +func ResourceDeferred(fw *resource.Deferred) *tfprotov6.Deferred { + if fw == nil { + return nil + } + return &tfprotov6.Deferred{ + Reason: tfprotov6.DeferredReason(fw.Reason), + } +} From 8f0b6aa50ab9e7e72014e928ae4ab7830f253bde Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Wed, 15 May 2024 11:26:54 -0400 Subject: [PATCH 25/25] Use `ResourceDeferred()` and `DataSourceDeferred()` functions in `toproto6` package --- internal/toproto6/importresourcestate.go | 7 +------ internal/toproto6/planresourcechange.go | 7 +------ internal/toproto6/readdatasource.go | 7 +------ internal/toproto6/readresource.go | 7 +------ 4 files changed, 4 insertions(+), 24 deletions(-) diff --git a/internal/toproto6/importresourcestate.go b/internal/toproto6/importresourcestate.go index 447671c37..125f25ffb 100644 --- a/internal/toproto6/importresourcestate.go +++ b/internal/toproto6/importresourcestate.go @@ -19,6 +19,7 @@ func ImportResourceStateResponse(ctx context.Context, fw *fwserver.ImportResourc } proto6 := &tfprotov6.ImportResourceStateResponse{ + Deferred: ResourceDeferred(fw.Deferred), Diagnostics: Diagnostics(ctx, fw.Diagnostics), } @@ -34,11 +35,5 @@ func ImportResourceStateResponse(ctx context.Context, fw *fwserver.ImportResourc proto6.ImportedResources = append(proto6.ImportedResources, proto6ImportedResource) } - if fw.Deferred != nil { - proto6.Deferred = &tfprotov6.Deferred{ - Reason: tfprotov6.DeferredReason(fw.Deferred.Reason), - } - } - return proto6 } diff --git a/internal/toproto6/planresourcechange.go b/internal/toproto6/planresourcechange.go index d9499f979..486f7ab02 100644 --- a/internal/toproto6/planresourcechange.go +++ b/internal/toproto6/planresourcechange.go @@ -20,6 +20,7 @@ func PlanResourceChangeResponse(ctx context.Context, fw *fwserver.PlanResourceCh } proto6 := &tfprotov6.PlanResourceChangeResponse{ + Deferred: ResourceDeferred(fw.Deferred), Diagnostics: Diagnostics(ctx, fw.Diagnostics), } @@ -38,11 +39,5 @@ func PlanResourceChangeResponse(ctx context.Context, fw *fwserver.PlanResourceCh proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) proto6.PlannedPrivate = plannedPrivate - if fw.Deferred != nil { - proto6.Deferred = &tfprotov6.Deferred{ - Reason: tfprotov6.DeferredReason(fw.Deferred.Reason), - } - } - return proto6 } diff --git a/internal/toproto6/readdatasource.go b/internal/toproto6/readdatasource.go index 0f1738625..051f004a5 100644 --- a/internal/toproto6/readdatasource.go +++ b/internal/toproto6/readdatasource.go @@ -19,6 +19,7 @@ func ReadDataSourceResponse(ctx context.Context, fw *fwserver.ReadDataSourceResp } proto6 := &tfprotov6.ReadDataSourceResponse{ + Deferred: DataSourceDeferred(fw.Deferred), Diagnostics: Diagnostics(ctx, fw.Diagnostics), } @@ -27,11 +28,5 @@ func ReadDataSourceResponse(ctx context.Context, fw *fwserver.ReadDataSourceResp proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) proto6.State = state - if fw.Deferred != nil { - proto6.Deferred = &tfprotov6.Deferred{ - Reason: tfprotov6.DeferredReason(fw.Deferred.Reason), - } - } - return proto6 } diff --git a/internal/toproto6/readresource.go b/internal/toproto6/readresource.go index 6f515fce9..2de7adf84 100644 --- a/internal/toproto6/readresource.go +++ b/internal/toproto6/readresource.go @@ -19,6 +19,7 @@ func ReadResourceResponse(ctx context.Context, fw *fwserver.ReadResourceResponse } proto6 := &tfprotov6.ReadResourceResponse{ + Deferred: ResourceDeferred(fw.Deferred), Diagnostics: Diagnostics(ctx, fw.Diagnostics), } @@ -32,11 +33,5 @@ func ReadResourceResponse(ctx context.Context, fw *fwserver.ReadResourceResponse proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) proto6.Private = newPrivate - if fw.Deferred != nil { - proto6.Deferred = &tfprotov6.Deferred{ - Reason: tfprotov6.DeferredReason(fw.Deferred.Reason), - } - } - return proto6 }