Skip to content

Commit 3c500cb

Browse files
committed
tf6muxserver: Initial implementation
Reference: #19 This new package implements a protocol version 6 compatible mux server. It is implemented similar to the existing protocol version 5 mux server, where validation and schema caching occurs upfront, then most RPCs act as a router. The main differences between the existing implementation and this new one are as follows: - Functionality is bundled into a package below the root package. - Naming is simplified and clarified. Previously, the capabilities of the mux server were believed to become much more dynamic, however in practice this never materialized due to the protocol. The mux server serves much more than a "Schema" so it is named "Mux" to prevent confusion. - There is no separation between a "Factory" and creating the mux server. There is not currently a benefit to introducing two layers of mux server handling. The main and only necessary entrypoint is a `NewMuxServer` function. - A `ProviderServer` method is provided to further simplify implementations, rather than needing a wrapping `func() tfprotov6.ProviderServer`. Once this package lands, the existing `SchemaServer` and `SchemaServerFactory` handling in the root package will be converted into a new `tf5muxserver` package with a similar API and structure as this one. That breaking change will occur afterwards before the next release.
1 parent 3195eb2 commit 3c500cb

34 files changed

Lines changed: 2816 additions & 1 deletion

.changelog/37.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:feature
2+
Added `tf6muxserver` package, a protocol version 6 compatible mux server
3+
```

README.md

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,47 @@ Plugin SDK or higher to be able to be used with terraform-plugin-mux.
3737

3838
## Getting Started
3939

40-
terraform-plugin-mux exposes a minimal interface:
40+
Functionality for a provider server is based on the protocol version. There are currently two main protocol versions in use today, protocol version 5 and protocol version 6, based on the development framework being used:
41+
42+
- terraform-plugin-framwork: Implements protocol version 6.
43+
- terraform-plugin-sdk: Implements protocol version 5.
44+
- terraform-plugin-go: Implements either protocol version, based on whether the `tf5server` package (protocol version 5) or `tf6server` package (protocol version 6) is being used.
45+
46+
To combine providers together, each must implement the same protocol version.
47+
48+
### Protocol Version 6
49+
50+
Protocol version 6 providers can be combined using the [`tf6muxserver.NewMuxServer` function](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-mux/tf6muxserver#NewMuxServer):
51+
52+
```go
53+
func main() {
54+
ctx := context.Background()
55+
providers := []func() tfprotov6.ProviderServer{
56+
// Example terraform-plugin-framework ProviderServer function
57+
// frameworkprovider.Provider().ProviderServer,
58+
//
59+
// Example terraform-plugin-go ProviderServer function
60+
// goprovider.Provider(),
61+
}
62+
63+
muxServer, err := tf6muxserver.NewMuxServer(ctx, providers...)
64+
65+
if err != nil {
66+
log.Fatalln(err.Error())
67+
}
68+
69+
// Use the result to start a muxed provider
70+
err = tf6server.Serve("registry.terraform.io/namespace/example", muxServer.ProviderServer)
71+
72+
if err != nil {
73+
log.Fatalln(err.Error())
74+
}
75+
}
76+
```
77+
78+
### Protocol Version 5
79+
80+
Protocol version 5 providers can be combined using the root package [`NewSchemaServerFactory()` function](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-mux#NewSchemaServerFactory):
4181

4282
```go
4383
func main() {

go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,22 @@ github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39
5656
github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
5757
github.com/hashicorp/go-hclog v1.1.0 h1:QsGcniKx5/LuX2eYoeL+Np3UKYPNaN7YKpTh29h8rbw=
5858
github.com/hashicorp/go-hclog v1.1.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
59+
github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM=
5960
github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=
61+
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
6062
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
6163
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
6264
github.com/hashicorp/terraform-plugin-go v0.7.0 h1:djd0aLO1uRr/VHaR12wryEFAPEFoHNIvRfCGkOrQqQI=
6365
github.com/hashicorp/terraform-plugin-go v0.7.0/go.mod h1:p+L2cRtja1Rr5A/S9flPLAzHyt2544MhIpAfwqnK7U0=
6466
github.com/hashicorp/terraform-plugin-log v0.2.1 h1:hl0G6ctSx7DRTE62VNsPWrq7d+JWy1kjk9ApOFrCq3I=
6567
github.com/hashicorp/terraform-plugin-log v0.2.1/go.mod h1:RW/n0x4dyITmenuirZ1ViPQGP5JQdPTZ4Wwc0rLKi94=
68+
github.com/hashicorp/terraform-registry-address v0.0.0-20210412075316-9b2996cce896 h1:1FGtlkJw87UsTMg5s8jrekrHmUPUJaMcu6ELiVhQrNw=
6669
github.com/hashicorp/terraform-registry-address v0.0.0-20210412075316-9b2996cce896/go.mod h1:bzBPnUIkI0RxauU8Dqo+2KrZZ28Cf48s8V6IHt3p4co=
70+
github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 h1:HKLsbzeOsfXmKNpr3GiT18XAblV0BjCbzL8KQAMZGa0=
6771
github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg=
72+
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
6873
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
74+
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
6975
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
7076
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
7177
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -83,6 +89,7 @@ github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJ
8389
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
8490
github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce h1:RPclfga2SEJmgMmz2k+Mg7cowZ8yv4Trqw9UsJby758=
8591
github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce/go.mod h1:uFMI8w+ref4v2r9jz+c9i1IfIttS/OkmLfrk1jne5hs=
92+
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
8693
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
8794
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
8895
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -140,6 +147,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
140147
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
141148
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
142149
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
150+
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
143151
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
144152
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
145153
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -157,6 +165,7 @@ google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoA
157165
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
158166
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
159167
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
168+
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
160169
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
161170
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
162171
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@@ -166,6 +175,7 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
166175
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
167176
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
168177
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
178+
google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM=
169179
google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
170180
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
171181
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=

internal/logging/context.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66

77
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
8+
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
89
"github.com/hashicorp/terraform-plugin-log/tflog"
910
"github.com/hashicorp/terraform-plugin-log/tfsdklog"
1011
)
@@ -16,6 +17,15 @@ func InitContext(ctx context.Context) context.Context {
1617
return ctx
1718
}
1819

20+
// RpcContext injects the RPC name into logger contexts.
21+
func RpcContext(ctx context.Context, rpc string) context.Context {
22+
ctx = tflog.With(ctx, KeyTfRpc, rpc)
23+
ctx = tfsdklog.With(ctx, KeyTfRpc, rpc)
24+
ctx = tfsdklog.SubsystemWith(ctx, SubsystemMux, KeyTfRpc, rpc)
25+
26+
return ctx
27+
}
28+
1929
// Tfprotov5ProviderServerContext injects the chosen provider Go type
2030
func Tfprotov5ProviderServerContext(ctx context.Context, p tfprotov5.ProviderServer) context.Context {
2131
providerType := fmt.Sprintf("%T", p)
@@ -25,3 +35,13 @@ func Tfprotov5ProviderServerContext(ctx context.Context, p tfprotov5.ProviderSer
2535

2636
return ctx
2737
}
38+
39+
// Tfprotov6ProviderServerContext injects the chosen provider Go type
40+
func Tfprotov6ProviderServerContext(ctx context.Context, p tfprotov6.ProviderServer) context.Context {
41+
providerType := fmt.Sprintf("%T", p)
42+
ctx = tflog.With(ctx, KeyTfMuxProvider, providerType)
43+
ctx = tfsdklog.With(ctx, KeyTfMuxProvider, providerType)
44+
ctx = tfsdklog.SubsystemWith(ctx, SubsystemMux, KeyTfMuxProvider, providerType)
45+
46+
return ctx
47+
}

internal/logging/keys.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,7 @@ package logging
77
const (
88
// Go type of the provider selected by mux.
99
KeyTfMuxProvider = "tf_mux_provider"
10+
11+
// The RPC being run, such as "ApplyResourceChange"
12+
KeyTfRpc = "tf_rpc"
1013
)
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package tf6testserver
2+
3+
import (
4+
"context"
5+
6+
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
7+
)
8+
9+
var _ tfprotov6.ProviderServer = &TestServer{}
10+
11+
type TestServer struct {
12+
DataSourceSchemas map[string]*tfprotov6.Schema
13+
ProviderMetaSchema *tfprotov6.Schema
14+
ProviderSchema *tfprotov6.Schema
15+
ResourceSchemas map[string]*tfprotov6.Schema
16+
17+
ApplyResourceChangeCalled map[string]bool
18+
19+
ConfigureProviderCalled bool
20+
21+
ImportResourceStateCalled map[string]bool
22+
23+
PlanResourceChangeCalled map[string]bool
24+
25+
ReadDataSourceCalled map[string]bool
26+
27+
ReadResourceCalled map[string]bool
28+
29+
StopProviderCalled bool
30+
StopProviderError string
31+
32+
UpgradeResourceStateCalled map[string]bool
33+
34+
ValidateDataResourceConfigCalled map[string]bool
35+
36+
ValidateProviderConfigCalled bool
37+
ValidateProviderConfigResponse *tfprotov6.ValidateProviderConfigResponse
38+
39+
ValidateResourceConfigCalled map[string]bool
40+
}
41+
42+
func (s *TestServer) ProviderServer() tfprotov6.ProviderServer {
43+
return s
44+
}
45+
46+
func (s *TestServer) ApplyResourceChange(_ context.Context, req *tfprotov6.ApplyResourceChangeRequest) (*tfprotov6.ApplyResourceChangeResponse, error) {
47+
if s.ApplyResourceChangeCalled == nil {
48+
s.ApplyResourceChangeCalled = make(map[string]bool)
49+
}
50+
51+
s.ApplyResourceChangeCalled[req.TypeName] = true
52+
return nil, nil
53+
}
54+
55+
func (s *TestServer) ConfigureProvider(_ context.Context, _ *tfprotov6.ConfigureProviderRequest) (*tfprotov6.ConfigureProviderResponse, error) {
56+
s.ConfigureProviderCalled = true
57+
return &tfprotov6.ConfigureProviderResponse{}, nil
58+
}
59+
60+
func (s *TestServer) GetProviderSchema(_ context.Context, _ *tfprotov6.GetProviderSchemaRequest) (*tfprotov6.GetProviderSchemaResponse, error) {
61+
if s.DataSourceSchemas == nil {
62+
s.DataSourceSchemas = make(map[string]*tfprotov6.Schema)
63+
}
64+
65+
if s.ResourceSchemas == nil {
66+
s.ResourceSchemas = make(map[string]*tfprotov6.Schema)
67+
}
68+
69+
return &tfprotov6.GetProviderSchemaResponse{
70+
Provider: s.ProviderSchema,
71+
ProviderMeta: s.ProviderMetaSchema,
72+
ResourceSchemas: s.ResourceSchemas,
73+
DataSourceSchemas: s.DataSourceSchemas,
74+
}, nil
75+
}
76+
77+
func (s *TestServer) ImportResourceState(_ context.Context, req *tfprotov6.ImportResourceStateRequest) (*tfprotov6.ImportResourceStateResponse, error) {
78+
if s.ImportResourceStateCalled == nil {
79+
s.ImportResourceStateCalled = make(map[string]bool)
80+
}
81+
82+
s.ImportResourceStateCalled[req.TypeName] = true
83+
return nil, nil
84+
}
85+
86+
func (s *TestServer) PlanResourceChange(_ context.Context, req *tfprotov6.PlanResourceChangeRequest) (*tfprotov6.PlanResourceChangeResponse, error) {
87+
if s.PlanResourceChangeCalled == nil {
88+
s.PlanResourceChangeCalled = make(map[string]bool)
89+
}
90+
91+
s.PlanResourceChangeCalled[req.TypeName] = true
92+
return nil, nil
93+
}
94+
95+
func (s *TestServer) ReadDataSource(_ context.Context, req *tfprotov6.ReadDataSourceRequest) (*tfprotov6.ReadDataSourceResponse, error) {
96+
if s.ReadDataSourceCalled == nil {
97+
s.ReadDataSourceCalled = make(map[string]bool)
98+
}
99+
100+
s.ReadDataSourceCalled[req.TypeName] = true
101+
return nil, nil
102+
}
103+
104+
func (s *TestServer) ReadResource(_ context.Context, req *tfprotov6.ReadResourceRequest) (*tfprotov6.ReadResourceResponse, error) {
105+
if s.ReadResourceCalled == nil {
106+
s.ReadResourceCalled = make(map[string]bool)
107+
}
108+
109+
s.ReadResourceCalled[req.TypeName] = true
110+
return nil, nil
111+
}
112+
113+
func (s *TestServer) StopProvider(_ context.Context, _ *tfprotov6.StopProviderRequest) (*tfprotov6.StopProviderResponse, error) {
114+
s.StopProviderCalled = true
115+
116+
if s.StopProviderError != "" {
117+
return &tfprotov6.StopProviderResponse{
118+
Error: s.StopProviderError,
119+
}, nil
120+
}
121+
122+
return &tfprotov6.StopProviderResponse{}, nil
123+
}
124+
125+
func (s *TestServer) UpgradeResourceState(_ context.Context, req *tfprotov6.UpgradeResourceStateRequest) (*tfprotov6.UpgradeResourceStateResponse, error) {
126+
if s.UpgradeResourceStateCalled == nil {
127+
s.UpgradeResourceStateCalled = make(map[string]bool)
128+
}
129+
130+
s.UpgradeResourceStateCalled[req.TypeName] = true
131+
return nil, nil
132+
}
133+
134+
func (s *TestServer) ValidateDataResourceConfig(_ context.Context, req *tfprotov6.ValidateDataResourceConfigRequest) (*tfprotov6.ValidateDataResourceConfigResponse, error) {
135+
if s.ValidateDataResourceConfigCalled == nil {
136+
s.ValidateDataResourceConfigCalled = make(map[string]bool)
137+
}
138+
139+
s.ValidateDataResourceConfigCalled[req.TypeName] = true
140+
return nil, nil
141+
}
142+
143+
func (s *TestServer) ValidateResourceConfig(_ context.Context, req *tfprotov6.ValidateResourceConfigRequest) (*tfprotov6.ValidateResourceConfigResponse, error) {
144+
if s.ValidateResourceConfigCalled == nil {
145+
s.ValidateResourceConfigCalled = make(map[string]bool)
146+
}
147+
148+
s.ValidateResourceConfigCalled[req.TypeName] = true
149+
return nil, nil
150+
}
151+
152+
func (s *TestServer) ValidateProviderConfig(_ context.Context, req *tfprotov6.ValidateProviderConfigRequest) (*tfprotov6.ValidateProviderConfigResponse, error) {
153+
s.ValidateProviderConfigCalled = true
154+
return s.ValidateProviderConfigResponse, nil
155+
}

0 commit comments

Comments
 (0)