Skip to content

Commit

Permalink
Migrate SchemaServer implementation to be similar to new protocol ver…
Browse files Browse the repository at this point in the history
…sion 6 implementation

Reference: #19
Reference: #37

Includes new internal tf5testserver package implementation.
  • Loading branch information
bflad committed Feb 2, 2022
1 parent ea3a0d4 commit 8e11c90
Show file tree
Hide file tree
Showing 32 changed files with 2,546 additions and 1,784 deletions.
3 changes: 3 additions & 0 deletions .changelog/39.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:breaking-change
The root package `SchemaServer` types and `NewSchemaServerFactory` function have been migrated to the `tf5muxserver` package. To upgrade, replace `tfmux.NewSchemaServerFactory` with `tf5muxserver.NewMuxServer` and replace any invocations of the previous `SchemaServerFactory` type `Server()` method with `ProviderServer()`. The underlying types are no longer exported.
```
43 changes: 18 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,40 +77,33 @@ func main() {

### Protocol Version 5

Protocol version 5 providers can be combined using the root package [`NewSchemaServerFactory()` function](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-mux#NewSchemaServerFactory):
Protocol version 5 providers can be combined using the [`tf5muxserver.NewMuxServer` function](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-mux/tf5muxserver#NewMuxServer):

```go
func main() {
ctx := context.Background()

// the ProviderServer from SDKv2
sdkv2 := sdkv2provider.Provider().GRPCProvider

// the terraform-plugin-go provider
tpg := protoprovider.Provider

factory, err := tfmux.NewSchemaServerFactory(ctx, sdkv2, tpg)
providers := []func() tfprotov5.ProviderServer{
// Example terraform-plugin-sdk ProviderServer function
// sdkprovider.Provider().ProviderServer,
//
// Example terraform-plugin-go ProviderServer function
// goprovider.Provider(),
}
muxServer, err := tf5muxserver.NewMuxServer(ctx, providers...)
if err != nil {
log.Println(err.Error())
os.Exit(1)
log.Fatalln(err.Error())
}
// Use the result to start a muxed provider
err = tf5server.Serve("registry.terraform.io/namespace/example", muxServer.ProviderServer)
if err != nil {
log.Fatalln(err.Error())
}

tf5server.Serve("registry.terraform.io/myorg/myprovider", factory.Server)
}
```

Each server needs a function that returns a
[`tfprotov5.ProviderServer`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-go/tfprotov5#ProviderServer).
Those get passed into a
[`NewSchemaServerFactory`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-mux#NewSchemaServerFactory)
function, which returns a factory capable of standing up Terraform provider
servers. Passing that factory into the
[`tf5server.Serve`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-go/tfprotov5/server#Serve)
function starts the server and lets Terraform connect to it.

## Testing

The Terraform Plugin SDK's [`helper/resource`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource) package can be used to test any provider that implements the [`tfprotov5.ProviderServer`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-go/tfprotov5#ProviderServer) interface, which includes muxed providers created using `tfmux.NewSchemaServerFactory`.
The Terraform Plugin SDK's [`helper/resource`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource) package can be used to test any provider that implements the [`tfprotov5.ProviderServer`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-go/tfprotov5#ProviderServer) interface, which includes muxed providers created using `tf5muxserver.NewMuxServer`.

You may wish to test a terraform-plugin-go provider's resources by supplying only that provider, and not the muxed provider, to the test framework: please see the example in https://github.com/hashicorp/terraform-plugin-go#testing in this case.

Expand All @@ -129,11 +122,11 @@ func init() {
// the terraform-plugin-go provider
tpg := protoprovider.Provider

factory, err := tfmux.NewSchemaServerFactory(ctx, sdkv2, tpg)
muxServer, err := tf5muxserver.NewMuxServer(ctx, sdkv2, tpg)
if err != nil {
return nil, err
}
return factory.Server(), nil
return muxServer.ProviderServer(), nil
}
}
```
Expand Down
155 changes: 155 additions & 0 deletions internal/tf5testserver/tf5testserver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package tf5testserver

import (
"context"

"github.com/hashicorp/terraform-plugin-go/tfprotov5"
)

var _ tfprotov5.ProviderServer = &TestServer{}

type TestServer struct {
DataSourceSchemas map[string]*tfprotov5.Schema
ProviderMetaSchema *tfprotov5.Schema
ProviderSchema *tfprotov5.Schema
ResourceSchemas map[string]*tfprotov5.Schema

ApplyResourceChangeCalled map[string]bool

ConfigureProviderCalled bool

ImportResourceStateCalled map[string]bool

PlanResourceChangeCalled map[string]bool

PrepareProviderConfigCalled bool
PrepareProviderConfigResponse *tfprotov5.PrepareProviderConfigResponse

ReadDataSourceCalled map[string]bool

ReadResourceCalled map[string]bool

StopProviderCalled bool
StopProviderError string

UpgradeResourceStateCalled map[string]bool

ValidateDataSourceConfigCalled map[string]bool

ValidateResourceTypeConfigCalled map[string]bool
}

func (s *TestServer) ProviderServer() tfprotov5.ProviderServer {
return s
}

func (s *TestServer) ApplyResourceChange(_ context.Context, req *tfprotov5.ApplyResourceChangeRequest) (*tfprotov5.ApplyResourceChangeResponse, error) {
if s.ApplyResourceChangeCalled == nil {
s.ApplyResourceChangeCalled = make(map[string]bool)
}

s.ApplyResourceChangeCalled[req.TypeName] = true
return nil, nil
}

func (s *TestServer) ConfigureProvider(_ context.Context, _ *tfprotov5.ConfigureProviderRequest) (*tfprotov5.ConfigureProviderResponse, error) {
s.ConfigureProviderCalled = true
return &tfprotov5.ConfigureProviderResponse{}, nil
}

func (s *TestServer) GetProviderSchema(_ context.Context, _ *tfprotov5.GetProviderSchemaRequest) (*tfprotov5.GetProviderSchemaResponse, error) {
if s.DataSourceSchemas == nil {
s.DataSourceSchemas = make(map[string]*tfprotov5.Schema)
}

if s.ResourceSchemas == nil {
s.ResourceSchemas = make(map[string]*tfprotov5.Schema)
}

return &tfprotov5.GetProviderSchemaResponse{
Provider: s.ProviderSchema,
ProviderMeta: s.ProviderMetaSchema,
ResourceSchemas: s.ResourceSchemas,
DataSourceSchemas: s.DataSourceSchemas,
}, nil
}

func (s *TestServer) ImportResourceState(_ context.Context, req *tfprotov5.ImportResourceStateRequest) (*tfprotov5.ImportResourceStateResponse, error) {
if s.ImportResourceStateCalled == nil {
s.ImportResourceStateCalled = make(map[string]bool)
}

s.ImportResourceStateCalled[req.TypeName] = true
return nil, nil
}

func (s *TestServer) PlanResourceChange(_ context.Context, req *tfprotov5.PlanResourceChangeRequest) (*tfprotov5.PlanResourceChangeResponse, error) {
if s.PlanResourceChangeCalled == nil {
s.PlanResourceChangeCalled = make(map[string]bool)
}

s.PlanResourceChangeCalled[req.TypeName] = true
return nil, nil
}

func (s *TestServer) ReadDataSource(_ context.Context, req *tfprotov5.ReadDataSourceRequest) (*tfprotov5.ReadDataSourceResponse, error) {
if s.ReadDataSourceCalled == nil {
s.ReadDataSourceCalled = make(map[string]bool)
}

s.ReadDataSourceCalled[req.TypeName] = true
return nil, nil
}

func (s *TestServer) ReadResource(_ context.Context, req *tfprotov5.ReadResourceRequest) (*tfprotov5.ReadResourceResponse, error) {
if s.ReadResourceCalled == nil {
s.ReadResourceCalled = make(map[string]bool)
}

s.ReadResourceCalled[req.TypeName] = true
return nil, nil
}

func (s *TestServer) StopProvider(_ context.Context, _ *tfprotov5.StopProviderRequest) (*tfprotov5.StopProviderResponse, error) {
s.StopProviderCalled = true

if s.StopProviderError != "" {
return &tfprotov5.StopProviderResponse{
Error: s.StopProviderError,
}, nil
}

return &tfprotov5.StopProviderResponse{}, nil
}

func (s *TestServer) UpgradeResourceState(_ context.Context, req *tfprotov5.UpgradeResourceStateRequest) (*tfprotov5.UpgradeResourceStateResponse, error) {
if s.UpgradeResourceStateCalled == nil {
s.UpgradeResourceStateCalled = make(map[string]bool)
}

s.UpgradeResourceStateCalled[req.TypeName] = true
return nil, nil
}

func (s *TestServer) ValidateDataSourceConfig(_ context.Context, req *tfprotov5.ValidateDataSourceConfigRequest) (*tfprotov5.ValidateDataSourceConfigResponse, error) {
if s.ValidateDataSourceConfigCalled == nil {
s.ValidateDataSourceConfigCalled = make(map[string]bool)
}

s.ValidateDataSourceConfigCalled[req.TypeName] = true
return nil, nil
}

func (s *TestServer) ValidateResourceTypeConfig(_ context.Context, req *tfprotov5.ValidateResourceTypeConfigRequest) (*tfprotov5.ValidateResourceTypeConfigResponse, error) {
if s.ValidateResourceTypeConfigCalled == nil {
s.ValidateResourceTypeConfigCalled = make(map[string]bool)
}

s.ValidateResourceTypeConfigCalled[req.TypeName] = true
return nil, nil
}

func (s *TestServer) PrepareProviderConfig(_ context.Context, req *tfprotov5.PrepareProviderConfigRequest) (*tfprotov5.PrepareProviderConfigResponse, error) {
s.PrepareProviderConfigCalled = true
return s.PrepareProviderConfigResponse, nil
}
4 changes: 2 additions & 2 deletions tf5muxserver/doc.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Package tfmux provides a multiplexer that allows joining multiple Terraform
// Package tf5muxserver provides a multiplexer that allows joining multiple Terraform
// provider servers into a single gRPC server.
//
// This allows providers to use any framework or SDK built on
// github.com/hashicorp/terraform-plugin-go to build resources for their
// provider, and to join all the resources into a single logical provider even
// though they're implemented in different SDKs or frameworks.
package tfmux
package tf5muxserver
Loading

0 comments on commit 8e11c90

Please sign in to comment.