Skip to content

Commit 24d09fe

Browse files
SBGoodsaustinvallebflad
authored
all: Add support for Manual Deferred Actions (#999)
* Implement manual deferred action support for `resource.importResourceState` * Implement manual deferred action support for `resource.readResource` * Implement manual deferred action support for `resource.modifyPlan` * Rename `DeferralReason` and `DeferralResponse` to `DeferredReason` and `DeferredResponse` respectively to match protobuf definition * Update `terraform-plugin-go` dependency to `v0.23.0` * Rename `deferral.go` to `deferred.go` * Implement manual deferred action support for data sources * Update documentation and diagnostic messages * Add copyright headers * Add changelog entries * Apply suggestions from code review Co-authored-by: Austin Valle <[email protected]> * Add comment and changelog notes to indicate experimental nature of deferred actions. * Rename constant to be specific to data sources * Remove TODO comment * Remove unnecessary nil check * Add default values for `ClientCapabilities` request fields * Rename `DeferredResponse` to `Deferred` * Remove error handling for deferral response without deferral capability * Remove variable indirection in tests * Add copyright headers * Apply suggestions from code review Co-authored-by: Brian Flad <[email protected]> * Add unit tests for client capabilities * Move client capabilities defaulting behavior to `fromproto5/6` package * Move `toproto5/6` `Deferred` conversion handling to its own files * Use `ResourceDeferred()` and `DataSourceDeferred()` functions in `toproto6` package --------- Co-authored-by: Austin Valle <[email protected]> Co-authored-by: Brian Flad <[email protected]>
1 parent ec16ec0 commit 24d09fe

Some content is hidden

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

54 files changed

+1118
-66
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kind: FEATURES
2+
body: 'resource: Add `Deferred` field to `ReadResponse`, `ModifyPlanResponse`, and `ImportStateResponse`
3+
which indicates a resource deferred action to the Terraform client'
4+
time: 2024-05-08T10:51:05.90518-04:00
5+
custom:
6+
Issue: "999"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kind: FEATURES
2+
body: 'datasource: Add `Deferred` field to `ReadResponse` which indicates a data source deferred action
3+
to the Terraform client'
4+
time: 2024-05-08T10:51:41.663935-04:00
5+
custom:
6+
Issue: "999"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kind: FEATURES
2+
body: 'resource: Add `ClientCapabilities` field to `ReadRequest`, `ModifyPlanRequest`, and `ImportStateRequest`
3+
which specifies optionally supported protocol features for the Terraform client'
4+
time: 2024-05-08T11:07:15.955126-04:00
5+
custom:
6+
Issue: "999"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kind: FEATURES
2+
body: 'datasource: Add `ClientCapabilities` field to `ReadRequest` which specifies
3+
optionally supported protocol features for the Terraform client'
4+
time: 2024-05-08T11:10:23.66584-04:00
5+
custom:
6+
Issue: "999"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
kind: NOTES
2+
body: This release contains support for deferred actions, which is an experimental
3+
feature only available in prerelease builds of Terraform 1.9 and later. This functionality
4+
is subject to change and is not protected by version compatibility guarantees.
5+
time: 2024-05-10T14:31:36.644869-04:00
6+
custom:
7+
Issue: "999"

datasource/deferred.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package datasource
5+
6+
const (
7+
// DeferredReasonUnknown is used to indicate an invalid `DeferredReason`.
8+
// Provider developers should not use it.
9+
DeferredReasonUnknown DeferredReason = 0
10+
11+
// DeferredReasonDataSourceConfigUnknown is used to indicate that the resource configuration
12+
// is partially unknown and the real values need to be known before the change can be planned.
13+
DeferredReasonDataSourceConfigUnknown DeferredReason = 1
14+
15+
// DeferredReasonProviderConfigUnknown is used to indicate that the provider configuration
16+
// is partially unknown and the real values need to be known before the change can be planned.
17+
DeferredReasonProviderConfigUnknown DeferredReason = 2
18+
19+
// DeferredReasonAbsentPrereq is used to indicate that a hard dependency has not been satisfied.
20+
DeferredReasonAbsentPrereq DeferredReason = 3
21+
)
22+
23+
// Deferred is used to indicate to Terraform that a change needs to be deferred for a reason.
24+
//
25+
// NOTE: This functionality is related to deferred action support, which is currently experimental and is subject
26+
// to change or break without warning. It is not protected by version compatibility guarantees.
27+
type Deferred struct {
28+
// Reason is the reason for deferring the change.
29+
Reason DeferredReason
30+
}
31+
32+
// DeferredReason represents different reasons for deferring a change.
33+
//
34+
// NOTE: This functionality is related to deferred action support, which is currently experimental and is subject
35+
// to change or break without warning. It is not protected by version compatibility guarantees.
36+
type DeferredReason int32
37+
38+
func (d DeferredReason) String() string {
39+
switch d {
40+
case 0:
41+
return "Unknown"
42+
case 1:
43+
return "Data Source Config Unknown"
44+
case 2:
45+
return "Provider Config Unknown"
46+
case 3:
47+
return "Absent Prerequisite"
48+
}
49+
return "Unknown"
50+
}

datasource/read.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,18 @@ import (
88
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
99
)
1010

11+
// ReadClientCapabilities allows Terraform to publish information
12+
// regarding optionally supported protocol features for the ReadDataSource RPC,
13+
// such as forward-compatible Terraform behavior changes.
14+
type ReadClientCapabilities struct {
15+
// DeferralAllowed indicates whether the Terraform client initiating
16+
// the request allows a deferral response.
17+
//
18+
// NOTE: This functionality is related to deferred action support, which is currently experimental and is subject
19+
// to change or break without warning. It is not protected by version compatibility guarantees.
20+
DeferralAllowed bool
21+
}
22+
1123
// ReadRequest represents a request for the provider to read a data
1224
// source, i.e., update values in state according to the real state of the
1325
// data source. An instance of this request struct is supplied as an argument
@@ -22,6 +34,10 @@ type ReadRequest struct {
2234

2335
// ProviderMeta is metadata from the provider_meta block of the module.
2436
ProviderMeta tfsdk.Config
37+
38+
// ClientCapabilities defines optionally supported protocol features for the
39+
// ReadDataSource RPC, such as forward-compatible Terraform behavior changes.
40+
ClientCapabilities ReadClientCapabilities
2541
}
2642

2743
// ReadResponse represents a response to a ReadRequest. An
@@ -37,4 +53,14 @@ type ReadResponse struct {
3753
// source. An empty slice indicates a successful operation with no
3854
// warnings or errors generated.
3955
Diagnostics diag.Diagnostics
56+
57+
// Deferred indicates that Terraform should defer reading this
58+
// data source until a followup apply operation.
59+
//
60+
// This field can only be set if
61+
// `(datasource.ReadRequest).ClientCapabilities.DeferralAllowed` is true.
62+
//
63+
// NOTE: This functionality is related to deferred action support, which is currently experimental and is subject
64+
// to change or break without warning. It is not protected by version compatibility guarantees.
65+
Deferred *Deferred
4066
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package fromproto5
5+
6+
import (
7+
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
8+
9+
"github.com/hashicorp/terraform-plugin-framework/datasource"
10+
"github.com/hashicorp/terraform-plugin-framework/resource"
11+
)
12+
13+
func ReadDataSourceClientCapabilities(in *tfprotov5.ReadDataSourceClientCapabilities) datasource.ReadClientCapabilities {
14+
if in == nil {
15+
// Client did not indicate any supported capabilities
16+
return datasource.ReadClientCapabilities{
17+
DeferralAllowed: false,
18+
}
19+
}
20+
21+
return datasource.ReadClientCapabilities{
22+
DeferralAllowed: in.DeferralAllowed,
23+
}
24+
}
25+
26+
func ReadResourceClientCapabilities(in *tfprotov5.ReadResourceClientCapabilities) resource.ReadClientCapabilities {
27+
if in == nil {
28+
// Client did not indicate any supported capabilities
29+
return resource.ReadClientCapabilities{
30+
DeferralAllowed: false,
31+
}
32+
}
33+
34+
return resource.ReadClientCapabilities{
35+
DeferralAllowed: in.DeferralAllowed,
36+
}
37+
}
38+
39+
func ModifyPlanClientCapabilities(in *tfprotov5.PlanResourceChangeClientCapabilities) resource.ModifyPlanClientCapabilities {
40+
if in == nil {
41+
// Client did not indicate any supported capabilities
42+
return resource.ModifyPlanClientCapabilities{
43+
DeferralAllowed: false,
44+
}
45+
}
46+
47+
return resource.ModifyPlanClientCapabilities{
48+
DeferralAllowed: in.DeferralAllowed,
49+
}
50+
}
51+
52+
func ImportStateClientCapabilities(in *tfprotov5.ImportResourceStateClientCapabilities) resource.ImportStateClientCapabilities {
53+
if in == nil {
54+
// Client did not indicate any supported capabilities
55+
return resource.ImportStateClientCapabilities{
56+
DeferralAllowed: false,
57+
}
58+
}
59+
60+
return resource.ImportStateClientCapabilities{
61+
DeferralAllowed: in.DeferralAllowed,
62+
}
63+
}

internal/fromproto5/importresourcestate.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,19 @@ package fromproto5
66
import (
77
"context"
88

9+
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
10+
"github.com/hashicorp/terraform-plugin-go/tftypes"
11+
912
"github.com/hashicorp/terraform-plugin-framework/diag"
1013
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
1114
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
1215
"github.com/hashicorp/terraform-plugin-framework/resource"
1316
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
14-
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
15-
"github.com/hashicorp/terraform-plugin-go/tftypes"
1617
)
1718

1819
// ImportResourceStateRequest returns the *fwserver.ImportResourceStateRequest
1920
// equivalent of a *tfprotov5.ImportResourceStateRequest.
20-
func ImportResourceStateRequest(ctx context.Context, proto5 *tfprotov5.ImportResourceStateRequest, resource resource.Resource, resourceSchema fwschema.Schema) (*fwserver.ImportResourceStateRequest, diag.Diagnostics) {
21+
func ImportResourceStateRequest(ctx context.Context, proto5 *tfprotov5.ImportResourceStateRequest, reqResource resource.Resource, resourceSchema fwschema.Schema) (*fwserver.ImportResourceStateRequest, diag.Diagnostics) {
2122
if proto5 == nil {
2223
return nil, nil
2324
}
@@ -43,9 +44,10 @@ func ImportResourceStateRequest(ctx context.Context, proto5 *tfprotov5.ImportRes
4344
Raw: tftypes.NewValue(resourceSchema.Type().TerraformType(ctx), nil),
4445
Schema: resourceSchema,
4546
},
46-
ID: proto5.ID,
47-
Resource: resource,
48-
TypeName: proto5.TypeName,
47+
ID: proto5.ID,
48+
Resource: reqResource,
49+
TypeName: proto5.TypeName,
50+
ClientCapabilities: ImportStateClientCapabilities(proto5.ClientCapabilities),
4951
}
5052

5153
return fw, diags

internal/fromproto5/importresourcestate_test.go

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,16 @@ import (
88
"testing"
99

1010
"github.com/google/go-cmp/cmp"
11+
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
12+
"github.com/hashicorp/terraform-plugin-go/tftypes"
13+
1114
"github.com/hashicorp/terraform-plugin-framework/diag"
1215
"github.com/hashicorp/terraform-plugin-framework/internal/fromproto5"
1316
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
1417
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
1518
"github.com/hashicorp/terraform-plugin-framework/resource"
1619
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
1720
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
18-
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
19-
"github.com/hashicorp/terraform-plugin-go/tftypes"
2021
)
2122

2223
func TestImportResourceStateRequest(t *testing.T) {
@@ -86,6 +87,35 @@ func TestImportResourceStateRequest(t *testing.T) {
8687
TypeName: "test_resource",
8788
},
8889
},
90+
"client-capabilities": {
91+
input: &tfprotov5.ImportResourceStateRequest{
92+
ID: "test-id",
93+
ClientCapabilities: &tfprotov5.ImportResourceStateClientCapabilities{
94+
DeferralAllowed: true,
95+
},
96+
},
97+
resourceSchema: testFwSchema,
98+
expected: &fwserver.ImportResourceStateRequest{
99+
EmptyState: testFwEmptyState,
100+
ID: "test-id",
101+
ClientCapabilities: resource.ImportStateClientCapabilities{
102+
DeferralAllowed: true,
103+
},
104+
},
105+
},
106+
"client-capabilities-unset": {
107+
input: &tfprotov5.ImportResourceStateRequest{
108+
ID: "test-id",
109+
},
110+
resourceSchema: testFwSchema,
111+
expected: &fwserver.ImportResourceStateRequest{
112+
EmptyState: testFwEmptyState,
113+
ID: "test-id",
114+
ClientCapabilities: resource.ImportStateClientCapabilities{
115+
DeferralAllowed: false,
116+
},
117+
},
118+
},
89119
}
90120

91121
for name, testCase := range testCases {

0 commit comments

Comments
 (0)