-
Notifications
You must be signed in to change notification settings - Fork 95
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce internal/fwserver package and migrate GetProviderSchema (#319)
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.
- Loading branch information
Showing
14 changed files
with
4,038 additions
and
157 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
// Package fromproto6 contains functions to convert from protocol version 6 | ||
// (tfprotov6) types to framework types. | ||
package fromproto6 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package fromproto6 | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver" | ||
"github.com/hashicorp/terraform-plugin-go/tfprotov6" | ||
) | ||
|
||
// GetProviderSchemaRequest returns the *fwserver.GetProviderSchemaRequest | ||
// equivalent of a *tfprotov6.GetProviderSchemaRequest. | ||
func GetProviderSchemaRequest(ctx context.Context, proto6 *tfprotov6.GetProviderSchemaRequest) *fwserver.GetProviderSchemaRequest { | ||
if proto6 == nil { | ||
return nil | ||
} | ||
|
||
fw := &fwserver.GetProviderSchemaRequest{} | ||
|
||
return fw | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package fromproto6_test | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/google/go-cmp/cmp" | ||
"github.com/hashicorp/terraform-plugin-framework/internal/fromproto6" | ||
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver" | ||
"github.com/hashicorp/terraform-plugin-go/tfprotov6" | ||
) | ||
|
||
func TestGetProviderSchemaRequest(t *testing.T) { | ||
t.Parallel() | ||
|
||
testCases := map[string]struct { | ||
input *tfprotov6.GetProviderSchemaRequest | ||
expected *fwserver.GetProviderSchemaRequest | ||
}{ | ||
"nil": { | ||
input: nil, | ||
expected: nil, | ||
}, | ||
"empty": { | ||
input: &tfprotov6.GetProviderSchemaRequest{}, | ||
expected: &fwserver.GetProviderSchemaRequest{}, | ||
}, | ||
} | ||
|
||
for name, testCase := range testCases { | ||
name, testCase := name, testCase | ||
|
||
t.Run(name, func(t *testing.T) { | ||
t.Parallel() | ||
|
||
got := fromproto6.GetProviderSchemaRequest(context.Background(), testCase.input) | ||
|
||
if diff := cmp.Diff(got, testCase.expected); diff != "" { | ||
t.Errorf("unexpected difference: %s", diff) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// Package fwserver contains the framework provider server implementation. | ||
// This package should only ever contain framework-native types, while specific | ||
// protocol version compatible implementations, such as proto6server, are | ||
// implemented on top of this abstraction. | ||
package fwserver |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package fwserver | ||
|
||
import ( | ||
"github.com/hashicorp/terraform-plugin-framework/tfsdk" | ||
) | ||
|
||
// Server implements the framework provider server. Protocol specific | ||
// implementations wrap this handling along with calling all request and | ||
// response type conversions. | ||
type Server struct { | ||
Provider tfsdk.Provider | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package fwserver | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/diag" | ||
"github.com/hashicorp/terraform-plugin-framework/internal/logging" | ||
"github.com/hashicorp/terraform-plugin-framework/tfsdk" | ||
) | ||
|
||
// GetProviderSchemaRequest is the framework server request for the | ||
// GetProviderSchema RPC. | ||
type GetProviderSchemaRequest struct{} | ||
|
||
// GetProviderSchemaResponse is the framework server response for the | ||
// GetProviderSchema RPC. | ||
type GetProviderSchemaResponse struct { | ||
Provider *tfsdk.Schema | ||
ProviderMeta *tfsdk.Schema | ||
ResourceSchemas map[string]*tfsdk.Schema | ||
DataSourceSchemas map[string]*tfsdk.Schema | ||
Diagnostics diag.Diagnostics | ||
} | ||
|
||
// GetProviderSchema implements the framework server GetProviderSchema RPC. | ||
func (s *Server) GetProviderSchema(ctx context.Context, req *GetProviderSchemaRequest, resp *GetProviderSchemaResponse) { | ||
logging.FrameworkDebug(ctx, "Calling provider defined Provider GetSchema") | ||
providerSchema, diags := s.Provider.GetSchema(ctx) | ||
logging.FrameworkDebug(ctx, "Called provider defined Provider GetSchema") | ||
|
||
resp.Diagnostics.Append(diags...) | ||
|
||
if diags.HasError() { | ||
return | ||
} | ||
|
||
resp.Provider = &providerSchema | ||
|
||
if pm, ok := s.Provider.(tfsdk.ProviderWithProviderMeta); ok { | ||
logging.FrameworkTrace(ctx, "Provider implements ProviderWithProviderMeta") | ||
|
||
logging.FrameworkDebug(ctx, "Calling provider defined Provider GetMetaSchema") | ||
providerMetaSchema, diags := pm.GetMetaSchema(ctx) | ||
logging.FrameworkDebug(ctx, "Called provider defined Provider GetMetaSchema") | ||
|
||
resp.Diagnostics.Append(diags...) | ||
|
||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
resp.ProviderMeta = &providerMetaSchema | ||
} | ||
|
||
// TODO: Cache GetDataSources call | ||
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/299 | ||
logging.FrameworkDebug(ctx, "Calling provider defined Provider GetResources") | ||
resourceSchemas, diags := s.Provider.GetResources(ctx) | ||
logging.FrameworkDebug(ctx, "Called provider defined Provider GetResources") | ||
|
||
resp.Diagnostics.Append(diags...) | ||
|
||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
if len(resourceSchemas) > 0 { | ||
resp.ResourceSchemas = map[string]*tfsdk.Schema{} | ||
} | ||
|
||
for k, v := range resourceSchemas { | ||
// KeyResourceType field only necessary here since we are in GetProviderSchema RPC | ||
logging.FrameworkTrace(ctx, "Found resource type", map[string]interface{}{logging.KeyResourceType: k}) | ||
|
||
logging.FrameworkDebug(ctx, "Calling provider defined ResourceType GetSchema", map[string]interface{}{logging.KeyResourceType: k}) | ||
schema, diags := v.GetSchema(ctx) | ||
logging.FrameworkDebug(ctx, "Called provider defined ResourceType GetSchema", map[string]interface{}{logging.KeyResourceType: k}) | ||
|
||
resp.Diagnostics.Append(diags...) | ||
|
||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
resp.ResourceSchemas[k] = &schema | ||
} | ||
|
||
// TODO: Cache GetDataSources call | ||
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/299 | ||
logging.FrameworkDebug(ctx, "Calling provider defined Provider GetDataSources") | ||
dataSourceSchemas, diags := s.Provider.GetDataSources(ctx) | ||
logging.FrameworkDebug(ctx, "Called provider defined Provider GetDataSources") | ||
|
||
resp.Diagnostics.Append(diags...) | ||
|
||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
if len(dataSourceSchemas) > 0 { | ||
resp.DataSourceSchemas = map[string]*tfsdk.Schema{} | ||
} | ||
|
||
for k, v := range dataSourceSchemas { | ||
// KeyDataSourceType field only necessary here since we are in GetProviderSchema RPC | ||
logging.FrameworkTrace(ctx, "Found data source type", map[string]interface{}{logging.KeyDataSourceType: k}) | ||
|
||
logging.FrameworkDebug(ctx, "Calling provider defined DataSourceType GetSchema", map[string]interface{}{logging.KeyDataSourceType: k}) | ||
schema, diags := v.GetSchema(ctx) | ||
logging.FrameworkDebug(ctx, "Called provider defined DataSourceType GetSchema", map[string]interface{}{logging.KeyDataSourceType: k}) | ||
|
||
resp.Diagnostics.Append(diags...) | ||
|
||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
resp.DataSourceSchemas[k] = &schema | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package fwserver_test | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/google/go-cmp/cmp" | ||
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver" | ||
"github.com/hashicorp/terraform-plugin-framework/internal/testing/emptyprovider" | ||
"github.com/hashicorp/terraform-plugin-framework/tfsdk" | ||
) | ||
|
||
// TODO: Migrate tfsdk.Provider bits of proto6server.testProviderServer to | ||
// new internal/testing/provider.Provider that allows customization of all | ||
// method implementations via struct fields. Then, create additional test | ||
// cases in this unit test. | ||
// | ||
// For now this testing is covered by proto6server.GetProviderSchema. | ||
// | ||
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/215 | ||
func TestServerGetProviderSchema(t *testing.T) { | ||
t.Parallel() | ||
|
||
testCases := map[string]struct { | ||
server fwserver.Server | ||
request *fwserver.GetProviderSchemaRequest | ||
expectedResponse *fwserver.GetProviderSchemaResponse | ||
}{ | ||
"empty-provider": { | ||
server: fwserver.Server{ | ||
Provider: &emptyprovider.Provider{}, | ||
}, | ||
expectedResponse: &fwserver.GetProviderSchemaResponse{ | ||
Provider: &tfsdk.Schema{}, | ||
}, | ||
}, | ||
} | ||
|
||
for name, testCase := range testCases { | ||
name, testCase := name, testCase | ||
|
||
t.Run(name, func(t *testing.T) { | ||
t.Parallel() | ||
|
||
response := &fwserver.GetProviderSchemaResponse{} | ||
testCase.server.GetProviderSchema(context.Background(), testCase.request, response) | ||
|
||
if diff := cmp.Diff(response, testCase.expectedResponse); diff != "" { | ||
t.Errorf("unexpected difference: %s", diff) | ||
} | ||
}) | ||
} | ||
} |
Oops, something went wrong.