Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tfprotov5+tfprotov6: Add deferred action support to related RPCs #403

Merged
merged 20 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/unreleased/FEATURES-20240501-174028.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: FEATURES
body: 'tfprotov5+tfprotov6: Upgraded protocols and added types to support deferred
actions'
time: 2024-05-01T17:40:28.845566-04:00
custom:
Issue: "403"
12 changes: 12 additions & 0 deletions tfprotov5/client_capabilities.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package tfprotov5

// ClientCapabilities allows Terraform to publish information regarding optionally supported
// protocol features, such as forward-compatible Terraform behavior changes.
type ClientCapabilities struct {
// DeferralAllowed signals that the Terraform client is able to
austinvalle marked this conversation as resolved.
Show resolved Hide resolved
// handle deferred responses from the provider.
DeferralAllowed bool
austinvalle marked this conversation as resolved.
Show resolved Hide resolved
}
8 changes: 8 additions & 0 deletions tfprotov5/data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ type ReadDataSourceRequest struct {
//
// This configuration will have known values for all fields.
ProviderMeta *DynamicValue

// ClientCapabilities defines optionally supported protocol features for
// the Terraform client, such as forward-compatible Terraform behavior changes.
ClientCapabilities *ClientCapabilities
}

// ReadDataSourceResponse is the response from the provider about the current
Expand All @@ -105,4 +109,8 @@ type ReadDataSourceResponse struct {
// indicates a successful validation with no warnings or errors
// generated.
Diagnostics []*Diagnostic

// Deferred is used to indicate to Terraform that the ReadDataSource operation
// needs to be deferred for a reason.
Deferred *Deferred
}
44 changes: 44 additions & 0 deletions tfprotov5/deferred.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package tfprotov5

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
)

// Deferred is used to indicate to Terraform that a change needs to be deferred for a reason.
type Deferred 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_PREREQ"
}
return "UNKNOWN"
}
21 changes: 21 additions & 0 deletions tfprotov5/internal/fromproto/client_capabilities.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package fromproto

import (
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
"github.com/hashicorp/terraform-plugin-go/tfprotov5/internal/tfplugin5"
)

func ClientCapabilities(in *tfplugin5.ClientCapabilities) *tfprotov5.ClientCapabilities {
if in == nil {
return nil
}

resp := &tfprotov5.ClientCapabilities{
DeferralAllowed: in.DeferralAllowed,
}

return resp
}
53 changes: 53 additions & 0 deletions tfprotov5/internal/fromproto/client_capabilities_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package fromproto_test

import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
"github.com/hashicorp/terraform-plugin-go/tfprotov5/internal/fromproto"
"github.com/hashicorp/terraform-plugin-go/tfprotov5/internal/tfplugin5"
)

func TestClientCapabilities(t *testing.T) {
t.Parallel()

testCases := map[string]struct {
in *tfplugin5.ClientCapabilities
expected *tfprotov5.ClientCapabilities
}{
"nil": {
in: nil,
expected: nil,
},
"zero": {
in: &tfplugin5.ClientCapabilities{},
expected: &tfprotov5.ClientCapabilities{},
},
"DeferralAllowed": {
in: &tfplugin5.ClientCapabilities{
DeferralAllowed: true,
},
expected: &tfprotov5.ClientCapabilities{
DeferralAllowed: true,
},
},
}

for name, testCase := range testCases {
name, testCase := name, testCase

t.Run(name, func(t *testing.T) {
t.Parallel()

got := fromproto.ClientCapabilities(testCase.in)

if diff := cmp.Diff(got, testCase.expected); diff != "" {
t.Errorf("unexpected difference: %s", diff)
}
})
}
}
7 changes: 4 additions & 3 deletions tfprotov5/internal/fromproto/data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ func ReadDataSourceRequest(in *tfplugin5.ReadDataSource_Request) *tfprotov5.Read
}

resp := &tfprotov5.ReadDataSourceRequest{
Config: DynamicValue(in.Config),
ProviderMeta: DynamicValue(in.ProviderMeta),
TypeName: in.TypeName,
Config: DynamicValue(in.Config),
ProviderMeta: DynamicValue(in.ProviderMeta),
TypeName: in.TypeName,
ClientCapabilities: ClientCapabilities(in.ClientCapabilities),
}

return resp
Expand Down
12 changes: 12 additions & 0 deletions tfprotov5/internal/fromproto/data_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ func TestReadDataSourceRequest(t *testing.T) {
TypeName: "test",
},
},
"DeferralAllowed": {
in: &tfplugin5.ReadDataSource_Request{
ClientCapabilities: &tfplugin5.ClientCapabilities{
DeferralAllowed: true,
},
},
expected: &tfprotov5.ReadDataSourceRequest{
ClientCapabilities: &tfprotov5.ClientCapabilities{
DeferralAllowed: true,
},
},
},
}

for name, testCase := range testCases {
Expand Down
5 changes: 3 additions & 2 deletions tfprotov5/internal/fromproto/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ func ConfigureProviderRequest(in *tfplugin5.Configure_Request) *tfprotov5.Config
}

resp := &tfprotov5.ConfigureProviderRequest{
Config: DynamicValue(in.Config),
TerraformVersion: in.TerraformVersion,
Config: DynamicValue(in.Config),
TerraformVersion: in.TerraformVersion,
ClientCapabilities: ClientCapabilities(in.ClientCapabilities),
}

return resp
Expand Down
12 changes: 12 additions & 0 deletions tfprotov5/internal/fromproto/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,18 @@ func TestConfigureProviderRequest(t *testing.T) {
TerraformVersion: "0.0.1",
},
},
"ClientCapabilities": {
in: &tfplugin5.Configure_Request{
ClientCapabilities: &tfplugin5.ClientCapabilities{
DeferralAllowed: true,
},
},
expected: &tfprotov5.ConfigureProviderRequest{
ClientCapabilities: &tfprotov5.ClientCapabilities{
DeferralAllowed: true,
},
},
},
}

for name, testCase := range testCases {
Expand Down
27 changes: 15 additions & 12 deletions tfprotov5/internal/fromproto/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@ func ReadResourceRequest(in *tfplugin5.ReadResource_Request) *tfprotov5.ReadReso
}

resp := &tfprotov5.ReadResourceRequest{
CurrentState: DynamicValue(in.CurrentState),
Private: in.Private,
ProviderMeta: DynamicValue(in.ProviderMeta),
TypeName: in.TypeName,
CurrentState: DynamicValue(in.CurrentState),
Private: in.Private,
ProviderMeta: DynamicValue(in.ProviderMeta),
TypeName: in.TypeName,
ClientCapabilities: ClientCapabilities(in.ClientCapabilities),
}

return resp
Expand All @@ -56,12 +57,13 @@ func PlanResourceChangeRequest(in *tfplugin5.PlanResourceChange_Request) *tfprot
}

resp := &tfprotov5.PlanResourceChangeRequest{
Config: DynamicValue(in.Config),
PriorPrivate: in.PriorPrivate,
PriorState: DynamicValue(in.PriorState),
ProposedNewState: DynamicValue(in.ProposedNewState),
ProviderMeta: DynamicValue(in.ProviderMeta),
TypeName: in.TypeName,
Config: DynamicValue(in.Config),
PriorPrivate: in.PriorPrivate,
PriorState: DynamicValue(in.PriorState),
ProposedNewState: DynamicValue(in.ProposedNewState),
ProviderMeta: DynamicValue(in.ProviderMeta),
TypeName: in.TypeName,
ClientCapabilities: ClientCapabilities(in.ClientCapabilities),
}

return resp
Expand Down Expand Up @@ -90,8 +92,9 @@ func ImportResourceStateRequest(in *tfplugin5.ImportResourceState_Request) *tfpr
}

resp := &tfprotov5.ImportResourceStateRequest{
TypeName: in.TypeName,
ID: in.Id,
TypeName: in.TypeName,
ID: in.Id,
ClientCapabilities: ClientCapabilities(in.ClientCapabilities),
}

return resp
Expand Down
36 changes: 36 additions & 0 deletions tfprotov5/internal/fromproto/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,18 @@ func TestImportResourceStateRequest(t *testing.T) {
TypeName: "test",
},
},
"ClientCapabilities": {
in: &tfplugin5.ImportResourceState_Request{
ClientCapabilities: &tfplugin5.ClientCapabilities{
DeferralAllowed: true,
},
},
expected: &tfprotov5.ImportResourceStateRequest{
ClientCapabilities: &tfprotov5.ClientCapabilities{
DeferralAllowed: true,
},
},
},
}

for name, testCase := range testCases {
Expand Down Expand Up @@ -283,6 +295,18 @@ func TestPlanResourceChangeRequest(t *testing.T) {
TypeName: "test",
},
},
"ClientCapabilities": {
in: &tfplugin5.PlanResourceChange_Request{
ClientCapabilities: &tfplugin5.ClientCapabilities{
DeferralAllowed: true,
},
},
expected: &tfprotov5.PlanResourceChangeRequest{
ClientCapabilities: &tfprotov5.ClientCapabilities{
DeferralAllowed: true,
},
},
},
}

for name, testCase := range testCases {
Expand Down Expand Up @@ -347,6 +371,18 @@ func TestReadResourceRequest(t *testing.T) {
TypeName: "test",
},
},
"ClientCapabilities": {
in: &tfplugin5.ReadResource_Request{
ClientCapabilities: &tfplugin5.ClientCapabilities{
DeferralAllowed: true,
},
},
expected: &tfprotov5.ReadResourceRequest{
ClientCapabilities: &tfprotov5.ClientCapabilities{
DeferralAllowed: true,
},
},
},
}

for name, testCase := range testCases {
Expand Down
Loading
Loading