Skip to content

Commit b2bd12c

Browse files
committed
Introduce internal/fwserver package and migrate GetProviderSchema
Reference: #215 This step of the provider server refactoring introduces a new `internal/fwserver` package that will contain the provider server implementation for any protocol version, there only being a version 6 implementation at the moment. This package will contain only framework-native types that aren't already defined by the `tfsdk` package that are used by provider developers today, which is necessary to prevent import cycles. The protocol specific implementations will then wrap this framework server implementation and handle any necessary conversion of protocol specific types to framework specific types and back. Eventually, the existing `internal/proto6server` implementation will make no reference to any framework native types except the framework server and protocol type conversions. For now, only the `GetProviderSchema` RPC is migrated to show what this refactoring will look like for the rest of the RPCs. This has a few benefits which can be seen here including clear abstractions for protocol specific handling versus generic framework handling and unit testing at those abstraction boundaries.
1 parent 93ea6d3 commit b2bd12c

14 files changed

+4038
-157
lines changed

internal/fromproto6/doc.go

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Package fromproto6 contains functions to convert from protocol version 6
2+
// (tfprotov6) types to framework types.
3+
package fromproto6
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package fromproto6
2+
3+
import (
4+
"context"
5+
6+
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
7+
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
8+
)
9+
10+
// GetProviderSchemaRequest returns the *fwserver.GetProviderSchemaRequest
11+
// equivalent of a *tfprotov6.GetProviderSchemaRequest.
12+
func GetProviderSchemaRequest(ctx context.Context, proto6 *tfprotov6.GetProviderSchemaRequest) *fwserver.GetProviderSchemaRequest {
13+
if proto6 == nil {
14+
return nil
15+
}
16+
17+
fw := &fwserver.GetProviderSchemaRequest{}
18+
19+
return fw
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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/internal/fromproto6"
9+
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
10+
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
11+
)
12+
13+
func TestGetProviderSchemaRequest(t *testing.T) {
14+
t.Parallel()
15+
16+
testCases := map[string]struct {
17+
input *tfprotov6.GetProviderSchemaRequest
18+
expected *fwserver.GetProviderSchemaRequest
19+
}{
20+
"nil": {
21+
input: nil,
22+
expected: nil,
23+
},
24+
"empty": {
25+
input: &tfprotov6.GetProviderSchemaRequest{},
26+
expected: &fwserver.GetProviderSchemaRequest{},
27+
},
28+
}
29+
30+
for name, testCase := range testCases {
31+
name, testCase := name, testCase
32+
33+
t.Run(name, func(t *testing.T) {
34+
t.Parallel()
35+
36+
got := fromproto6.GetProviderSchemaRequest(context.Background(), testCase.input)
37+
38+
if diff := cmp.Diff(got, testCase.expected); diff != "" {
39+
t.Errorf("unexpected difference: %s", diff)
40+
}
41+
})
42+
}
43+
}

internal/fwserver/doc.go

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Package fwserver contains the framework provider server implementation.
2+
// This package should only ever contain framework-native types, while specific
3+
// protocol version compatible implementations, such as proto6server, are
4+
// implemented on top of this abstraction.
5+
package fwserver

internal/fwserver/server.go

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package fwserver
2+
3+
import (
4+
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
5+
)
6+
7+
// Server implements the framework provider server. Protocol specific
8+
// implementations wrap this handling along with calling all request and
9+
// response type conversions.
10+
type Server struct {
11+
Provider tfsdk.Provider
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package fwserver
2+
3+
import (
4+
"context"
5+
6+
"github.com/hashicorp/terraform-plugin-framework/diag"
7+
"github.com/hashicorp/terraform-plugin-framework/internal/logging"
8+
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
9+
)
10+
11+
// GetProviderSchemaRequest is the framework server request for the
12+
// GetProviderSchema RPC.
13+
type GetProviderSchemaRequest struct{}
14+
15+
// GetProviderSchemaResponse is the framework server response for the
16+
// GetProviderSchema RPC.
17+
type GetProviderSchemaResponse struct {
18+
Provider *tfsdk.Schema
19+
ProviderMeta *tfsdk.Schema
20+
ResourceSchemas map[string]*tfsdk.Schema
21+
DataSourceSchemas map[string]*tfsdk.Schema
22+
Diagnostics diag.Diagnostics
23+
}
24+
25+
// GetProviderSchema implements the framework server GetProviderSchema RPC.
26+
func (s *Server) GetProviderSchema(ctx context.Context, req *GetProviderSchemaRequest, resp *GetProviderSchemaResponse) {
27+
logging.FrameworkDebug(ctx, "Calling provider defined Provider GetSchema")
28+
providerSchema, diags := s.Provider.GetSchema(ctx)
29+
logging.FrameworkDebug(ctx, "Called provider defined Provider GetSchema")
30+
31+
resp.Diagnostics.Append(diags...)
32+
33+
if diags.HasError() {
34+
return
35+
}
36+
37+
resp.Provider = &providerSchema
38+
39+
if pm, ok := s.Provider.(tfsdk.ProviderWithProviderMeta); ok {
40+
logging.FrameworkTrace(ctx, "Provider implements ProviderWithProviderMeta")
41+
42+
logging.FrameworkDebug(ctx, "Calling provider defined Provider GetMetaSchema")
43+
providerMetaSchema, diags := pm.GetMetaSchema(ctx)
44+
logging.FrameworkDebug(ctx, "Called provider defined Provider GetMetaSchema")
45+
46+
resp.Diagnostics.Append(diags...)
47+
48+
if resp.Diagnostics.HasError() {
49+
return
50+
}
51+
52+
resp.ProviderMeta = &providerMetaSchema
53+
}
54+
55+
// TODO: Cache GetDataSources call
56+
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/299
57+
logging.FrameworkDebug(ctx, "Calling provider defined Provider GetResources")
58+
resourceSchemas, diags := s.Provider.GetResources(ctx)
59+
logging.FrameworkDebug(ctx, "Called provider defined Provider GetResources")
60+
61+
resp.Diagnostics.Append(diags...)
62+
63+
if resp.Diagnostics.HasError() {
64+
return
65+
}
66+
67+
if len(resourceSchemas) > 0 {
68+
resp.ResourceSchemas = map[string]*tfsdk.Schema{}
69+
}
70+
71+
for k, v := range resourceSchemas {
72+
// KeyResourceType field only necessary here since we are in GetProviderSchema RPC
73+
logging.FrameworkTrace(ctx, "Found resource type", map[string]interface{}{logging.KeyResourceType: k})
74+
75+
logging.FrameworkDebug(ctx, "Calling provider defined ResourceType GetSchema", map[string]interface{}{logging.KeyResourceType: k})
76+
schema, diags := v.GetSchema(ctx)
77+
logging.FrameworkDebug(ctx, "Called provider defined ResourceType GetSchema", map[string]interface{}{logging.KeyResourceType: k})
78+
79+
resp.Diagnostics.Append(diags...)
80+
81+
if resp.Diagnostics.HasError() {
82+
return
83+
}
84+
85+
resp.ResourceSchemas[k] = &schema
86+
}
87+
88+
// TODO: Cache GetDataSources call
89+
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/299
90+
logging.FrameworkDebug(ctx, "Calling provider defined Provider GetDataSources")
91+
dataSourceSchemas, diags := s.Provider.GetDataSources(ctx)
92+
logging.FrameworkDebug(ctx, "Called provider defined Provider GetDataSources")
93+
94+
resp.Diagnostics.Append(diags...)
95+
96+
if resp.Diagnostics.HasError() {
97+
return
98+
}
99+
100+
if len(dataSourceSchemas) > 0 {
101+
resp.DataSourceSchemas = map[string]*tfsdk.Schema{}
102+
}
103+
104+
for k, v := range dataSourceSchemas {
105+
// KeyDataSourceType field only necessary here since we are in GetProviderSchema RPC
106+
logging.FrameworkTrace(ctx, "Found data source type", map[string]interface{}{logging.KeyDataSourceType: k})
107+
108+
logging.FrameworkDebug(ctx, "Calling provider defined DataSourceType GetSchema", map[string]interface{}{logging.KeyDataSourceType: k})
109+
schema, diags := v.GetSchema(ctx)
110+
logging.FrameworkDebug(ctx, "Called provider defined DataSourceType GetSchema", map[string]interface{}{logging.KeyDataSourceType: k})
111+
112+
resp.Diagnostics.Append(diags...)
113+
114+
if resp.Diagnostics.HasError() {
115+
return
116+
}
117+
118+
resp.DataSourceSchemas[k] = &schema
119+
}
120+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package fwserver_test
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/google/go-cmp/cmp"
8+
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
9+
"github.com/hashicorp/terraform-plugin-framework/internal/testing/emptyprovider"
10+
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
11+
)
12+
13+
// TODO: Migrate tfsdk.Provider bits of proto6server.testProviderServer to
14+
// new internal/testing/provider.Provider that allows customization of all
15+
// method implementations via struct fields. Then, create additional test
16+
// cases in this unit test.
17+
//
18+
// For now this testing is covered by proto6server.GetProviderSchema.
19+
//
20+
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/215
21+
func TestServerGetProviderSchema(t *testing.T) {
22+
t.Parallel()
23+
24+
testCases := map[string]struct {
25+
server fwserver.Server
26+
request *fwserver.GetProviderSchemaRequest
27+
expectedResponse *fwserver.GetProviderSchemaResponse
28+
}{
29+
"empty-provider": {
30+
server: fwserver.Server{
31+
Provider: &emptyprovider.Provider{},
32+
},
33+
expectedResponse: &fwserver.GetProviderSchemaResponse{
34+
Provider: &tfsdk.Schema{},
35+
},
36+
},
37+
}
38+
39+
for name, testCase := range testCases {
40+
name, testCase := name, testCase
41+
42+
t.Run(name, func(t *testing.T) {
43+
t.Parallel()
44+
45+
response := &fwserver.GetProviderSchemaResponse{}
46+
testCase.server.GetProviderSchema(context.Background(), testCase.request, response)
47+
48+
if diff := cmp.Diff(response, testCase.expectedResponse); diff != "" {
49+
t.Errorf("unexpected difference: %s", diff)
50+
}
51+
})
52+
}
53+
}

0 commit comments

Comments
 (0)