Skip to content

Commit 25f29d3

Browse files
authored
internal: Migrate ValidateProviderConfig RPC to fromproto6, fwserver, and toproto6 (#320)
Reference: #215 One particular quirk that this implementation shows is that it is not entirely possible to avoid working with `tfsdk`/`fwserver` bits in `proto6server` as the current data types within the framework (`Config`, `Plan`, `State`) require the `Schema` so data can be appropriately converted from the protocol to the framework. To support this effort and prevent repetitively executing the provider defined `GetSchema` method, a `fwserver` method is introduced that caches the `Schema` and `Diagnostics` results on first use. This methodology can also be applied in the future to resolve #299.
1 parent 494533d commit 25f29d3

26 files changed

+845
-127
lines changed

internal/fromproto6/config.go

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package fromproto6
2+
3+
import (
4+
"context"
5+
6+
"github.com/hashicorp/terraform-plugin-framework/diag"
7+
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
8+
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
9+
)
10+
11+
// Config returns the *tfsdk.Config for a *tfprotov6.DynamicValue and
12+
// *tfsdk.Schema.
13+
func Config(ctx context.Context, proto6DynamicValue *tfprotov6.DynamicValue, schema *tfsdk.Schema) (*tfsdk.Config, diag.Diagnostics) {
14+
if proto6DynamicValue == nil {
15+
return nil, nil
16+
}
17+
18+
var diags diag.Diagnostics
19+
20+
// Panic prevention here to simplify the calling implementations.
21+
// This should not happen, but just in case.
22+
if schema == nil {
23+
diags.AddError(
24+
"Unable to Convert Configuration",
25+
"An unexpected error was encountered when converting the configuration from the protocol type. "+
26+
"This is always an issue in the Terraform Provider SDK used to implement the provider and should be reported to the provider developers.\n\n"+
27+
"Please report this to the provider developer:\n\n"+
28+
"Missing schema.",
29+
)
30+
31+
return nil, diags
32+
}
33+
34+
proto6Value, err := proto6DynamicValue.Unmarshal(schema.TerraformType(ctx))
35+
36+
if err != nil {
37+
diags.AddError(
38+
"Unable to Convert Configuration",
39+
"An unexpected error was encountered when converting the configuration from the protocol type. "+
40+
"This is always an issue in the Terraform Provider SDK used to implement the provider and should be reported to the provider developers.\n\n"+
41+
"Please report this to the provider developer:\n\n"+err.Error(),
42+
)
43+
44+
return nil, diags
45+
}
46+
47+
fw := &tfsdk.Config{
48+
Raw: proto6Value,
49+
Schema: *schema,
50+
}
51+
52+
return fw, nil
53+
}

internal/fromproto6/config_test.go

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package fromproto6_test
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/google/go-cmp/cmp"
8+
"github.com/hashicorp/terraform-plugin-framework/diag"
9+
"github.com/hashicorp/terraform-plugin-framework/internal/fromproto6"
10+
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
11+
"github.com/hashicorp/terraform-plugin-framework/types"
12+
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
13+
"github.com/hashicorp/terraform-plugin-go/tftypes"
14+
)
15+
16+
func TestConfig(t *testing.T) {
17+
t.Parallel()
18+
19+
testProto6Type := tftypes.Object{
20+
AttributeTypes: map[string]tftypes.Type{
21+
"test_attribute": tftypes.String,
22+
},
23+
}
24+
25+
testProto6Value := tftypes.NewValue(testProto6Type, map[string]tftypes.Value{
26+
"test_attribute": tftypes.NewValue(tftypes.String, "test-value"),
27+
})
28+
29+
testProto6DynamicValue, err := tfprotov6.NewDynamicValue(testProto6Type, testProto6Value)
30+
31+
if err != nil {
32+
t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err)
33+
}
34+
35+
testFwSchema := &tfsdk.Schema{
36+
Attributes: map[string]tfsdk.Attribute{
37+
"test_attribute": {
38+
Required: true,
39+
Type: types.StringType,
40+
},
41+
},
42+
}
43+
44+
testFwSchemaInvalid := &tfsdk.Schema{
45+
Attributes: map[string]tfsdk.Attribute{
46+
"test_attribute": {
47+
Required: true,
48+
Type: types.BoolType,
49+
},
50+
},
51+
}
52+
53+
testCases := map[string]struct {
54+
input *tfprotov6.DynamicValue
55+
schema *tfsdk.Schema
56+
expected *tfsdk.Config
57+
expectedDiagnostics diag.Diagnostics
58+
}{
59+
"nil": {
60+
input: nil,
61+
expected: nil,
62+
},
63+
"missing-schema": {
64+
input: &testProto6DynamicValue,
65+
expected: nil,
66+
expectedDiagnostics: diag.Diagnostics{
67+
diag.NewErrorDiagnostic(
68+
"Unable to Convert Configuration",
69+
"An unexpected error was encountered when converting the configuration from the protocol type. "+
70+
"This is always an issue in the Terraform Provider SDK used to implement the provider and should be reported to the provider developers.\n\n"+
71+
"Please report this to the provider developer:\n\n"+
72+
"Missing schema.",
73+
),
74+
},
75+
},
76+
"invalid-schema": {
77+
input: &testProto6DynamicValue,
78+
schema: testFwSchemaInvalid,
79+
expected: nil,
80+
expectedDiagnostics: diag.Diagnostics{
81+
diag.NewErrorDiagnostic(
82+
"Unable to Convert Configuration",
83+
"An unexpected error was encountered when converting the configuration from the protocol type. "+
84+
"This is always an issue in the Terraform Provider SDK used to implement the provider and should be reported to the provider developers.\n\n"+
85+
"Please report this to the provider developer:\n\n"+
86+
"AttributeName(\"test_attribute\"): couldn't decode bool: msgpack: invalid code=aa decoding bool",
87+
),
88+
},
89+
},
90+
"valid": {
91+
input: &testProto6DynamicValue,
92+
schema: testFwSchema,
93+
expected: &tfsdk.Config{
94+
Raw: testProto6Value,
95+
Schema: *testFwSchema,
96+
},
97+
},
98+
}
99+
100+
for name, testCase := range testCases {
101+
name, testCase := name, testCase
102+
103+
t.Run(name, func(t *testing.T) {
104+
t.Parallel()
105+
106+
got, diags := fromproto6.Config(context.Background(), testCase.input, testCase.schema)
107+
108+
if diff := cmp.Diff(got, testCase.expected); diff != "" {
109+
t.Errorf("unexpected difference: %s", diff)
110+
}
111+
112+
if diff := cmp.Diff(diags, testCase.expectedDiagnostics); diff != "" {
113+
t.Errorf("unexpected diagnostics difference: %s", diff)
114+
}
115+
})
116+
}
117+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package fromproto6
2+
3+
import (
4+
"context"
5+
6+
"github.com/hashicorp/terraform-plugin-framework/diag"
7+
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
8+
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
9+
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
10+
)
11+
12+
// ValidateProviderConfigRequest returns the *fwserver.ValidateProviderConfigRequest
13+
// equivalent of a *tfprotov6.ValidateProviderConfigRequest.
14+
func ValidateProviderConfigRequest(ctx context.Context, proto6 *tfprotov6.ValidateProviderConfigRequest, providerSchema *tfsdk.Schema) (*fwserver.ValidateProviderConfigRequest, diag.Diagnostics) {
15+
if proto6 == nil {
16+
return nil, nil
17+
}
18+
19+
fw := &fwserver.ValidateProviderConfigRequest{}
20+
21+
config, diags := Config(ctx, proto6.Config, providerSchema)
22+
23+
fw.Config = config
24+
25+
return fw, diags
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package fromproto6_test
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/google/go-cmp/cmp"
8+
"github.com/hashicorp/terraform-plugin-framework/diag"
9+
"github.com/hashicorp/terraform-plugin-framework/internal/fromproto6"
10+
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
11+
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
12+
"github.com/hashicorp/terraform-plugin-framework/types"
13+
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
14+
"github.com/hashicorp/terraform-plugin-go/tftypes"
15+
)
16+
17+
func TestValidateProviderConfigRequest(t *testing.T) {
18+
t.Parallel()
19+
20+
testProto6Type := tftypes.Object{
21+
AttributeTypes: map[string]tftypes.Type{
22+
"test_attribute": tftypes.String,
23+
},
24+
}
25+
26+
testProto6Value := tftypes.NewValue(testProto6Type, map[string]tftypes.Value{
27+
"test_attribute": tftypes.NewValue(tftypes.String, "test-value"),
28+
})
29+
30+
testProto6DynamicValue, err := tfprotov6.NewDynamicValue(testProto6Type, testProto6Value)
31+
32+
if err != nil {
33+
t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err)
34+
}
35+
36+
testFwSchema := &tfsdk.Schema{
37+
Attributes: map[string]tfsdk.Attribute{
38+
"test_attribute": {
39+
Required: true,
40+
Type: types.StringType,
41+
},
42+
},
43+
}
44+
45+
testCases := map[string]struct {
46+
input *tfprotov6.ValidateProviderConfigRequest
47+
providerSchema *tfsdk.Schema
48+
expected *fwserver.ValidateProviderConfigRequest
49+
expectedDiagnostics diag.Diagnostics
50+
}{
51+
"nil": {
52+
input: nil,
53+
expected: nil,
54+
},
55+
"empty": {
56+
input: &tfprotov6.ValidateProviderConfigRequest{},
57+
expected: &fwserver.ValidateProviderConfigRequest{},
58+
},
59+
"config-missing-schema": {
60+
input: &tfprotov6.ValidateProviderConfigRequest{
61+
Config: &testProto6DynamicValue,
62+
},
63+
expected: &fwserver.ValidateProviderConfigRequest{},
64+
expectedDiagnostics: diag.Diagnostics{
65+
diag.NewErrorDiagnostic(
66+
"Unable to Convert Configuration",
67+
"An unexpected error was encountered when converting the configuration from the protocol type. "+
68+
"This is always an issue in the Terraform Provider SDK used to implement the provider and should be reported to the provider developers.\n\n"+
69+
"Please report this to the provider developer:\n\n"+
70+
"Missing schema.",
71+
),
72+
},
73+
},
74+
"config": {
75+
input: &tfprotov6.ValidateProviderConfigRequest{
76+
Config: &testProto6DynamicValue,
77+
},
78+
providerSchema: testFwSchema,
79+
expected: &fwserver.ValidateProviderConfigRequest{
80+
Config: &tfsdk.Config{
81+
Raw: testProto6Value,
82+
Schema: *testFwSchema,
83+
},
84+
},
85+
},
86+
}
87+
88+
for name, testCase := range testCases {
89+
name, testCase := name, testCase
90+
91+
t.Run(name, func(t *testing.T) {
92+
t.Parallel()
93+
94+
got, diags := fromproto6.ValidateProviderConfigRequest(context.Background(), testCase.input, testCase.providerSchema)
95+
96+
if diff := cmp.Diff(got, testCase.expected); diff != "" {
97+
t.Errorf("unexpected difference: %s", diff)
98+
}
99+
100+
if diff := cmp.Diff(diags, testCase.expectedDiagnostics); diff != "" {
101+
t.Errorf("unexpected diagnostics difference: %s", diff)
102+
}
103+
})
104+
}
105+
}

internal/proto6server/attribute_validation.go renamed to internal/fwserver/attribute_validation.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package proto6server
1+
package fwserver
22

33
import (
44
"context"

internal/proto6server/attribute_validation_test.go renamed to internal/fwserver/attribute_validation_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package proto6server
1+
package fwserver
22

33
import (
44
"context"

internal/proto6server/block_validation.go renamed to internal/fwserver/block_validation.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package proto6server
1+
package fwserver
22

33
import (
44
"context"

internal/proto6server/block_validation_test.go renamed to internal/fwserver/block_validation_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package proto6server
1+
package fwserver
22

33
import (
44
"context"

internal/proto6server/config.go renamed to internal/fwserver/config.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package proto6server
1+
package fwserver
22

33
import (
44
"context"

internal/proto6server/schema.go renamed to internal/fwserver/schema.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package proto6server
1+
package fwserver
22

33
import (
44
"fmt"

internal/proto6server/schema_validation.go renamed to internal/fwserver/schema_validation.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package proto6server
1+
package fwserver
22

33
import (
44
"context"

internal/proto6server/schema_validation_test.go renamed to internal/fwserver/schema_validation_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package proto6server
1+
package fwserver
22

33
import (
44
"context"

0 commit comments

Comments
 (0)