11package plugin
22
33import (
4+ "errors"
45 "log"
56
67 hclog "github.com/hashicorp/go-hclog"
78 "github.com/hashicorp/go-plugin"
89 testing "github.com/mitchellh/go-testing-interface"
9- "google.golang.org/grpc"
1010
1111 "github.com/hashicorp/terraform-plugin-go/tfprotov5"
1212 "github.com/hashicorp/terraform-plugin-go/tfprotov5/tf5server"
@@ -18,10 +18,16 @@ import (
1818const (
1919 // The constants below are the names of the plugins that can be dispensed
2020 // from the plugin server.
21+ //
22+ // Deprecated: This is no longer used, but left for backwards compatibility
23+ // since it is exported. It will be removed in the next major version.
2124 ProviderPluginName = "provider"
2225)
2326
2427// Handshake is the HandshakeConfig used to configure clients and servers.
28+ //
29+ // Deprecated: This is no longer used, but left for backwards compatibility
30+ // since it is exported. It will be removed in the next major version.
2531var Handshake = plugin.HandshakeConfig {
2632 // The magic cookie values should NEVER be changed.
2733 MagicCookieKey : "TF_PLUGIN_MAGIC_COOKIE" ,
@@ -45,11 +51,20 @@ type ServeOpts struct {
4551 // Logger is the logger that go-plugin will use.
4652 Logger hclog.Logger
4753
54+ // Debug starts a debug server and controls its lifecycle, printing the
55+ // information needed for Terraform to connect to the provider to stdout.
56+ // os.Interrupt will be captured and used to stop the server.
57+ //
58+ // This option cannot be combined with TestConfig.
59+ Debug bool
60+
4861 // TestConfig should only be set when the provider is being tested; it
4962 // will opt out of go-plugin's lifecycle management and other features,
5063 // and will use the supplied configuration options to control the
5164 // plugin's lifecycle and communicate connection information. See the
5265 // go-plugin GoDoc for more information.
66+ //
67+ // This option cannot be combined with Debug.
5368 TestConfig * plugin.ServeTestConfig
5469
5570 // Set NoLogOutputOverride to not override the log output with an hclog
@@ -69,6 +84,11 @@ type ServeOpts struct {
6984// Serve serves a plugin. This function never returns and should be the final
7085// function called in the main function of the plugin.
7186func Serve (opts * ServeOpts ) {
87+ if opts .Debug && opts .TestConfig != nil {
88+ log .Printf ("[ERROR] Error starting provider: cannot set both Debug and TestConfig" )
89+ return
90+ }
91+
7292 if ! opts .NoLogOutputOverride {
7393 // In order to allow go-plugin to correctly pass log-levels through to
7494 // terraform, we need to use an hclog.Logger with JSON output. We can
@@ -84,65 +104,126 @@ func Serve(opts *ServeOpts) {
84104 log .SetOutput (logger .StandardWriter (& hclog.StandardLoggerOptions {InferLevels : true }))
85105 }
86106
87- // since the plugins may not yet be aware of the new protocol, we
88- // automatically wrap the plugins in the grpc shims.
89- if opts .GRPCProviderFunc == nil && opts .ProviderFunc != nil {
107+ if opts .ProviderAddr == "" {
108+ opts .ProviderAddr = "provider"
109+ }
110+
111+ var err error
112+
113+ switch {
114+ case opts .ProviderFunc != nil && opts .GRPCProviderFunc == nil :
90115 opts .GRPCProviderFunc = func () tfprotov5.ProviderServer {
91116 return schema .NewGRPCProviderServer (opts .ProviderFunc ())
92117 }
118+ err = tf5serverServe (opts )
119+ case opts .GRPCProviderFunc != nil :
120+ err = tf5serverServe (opts )
121+ case opts .GRPCProviderV6Func != nil :
122+ err = tf6serverServe (opts )
123+ default :
124+ err = errors .New ("no provider server defined in ServeOpts" )
93125 }
94126
95- serveConfig := plugin.ServeConfig {
96- HandshakeConfig : Handshake ,
97- GRPCServer : func (opts []grpc.ServerOption ) * grpc.Server {
98- return grpc .NewServer (opts ... )
99- },
100- Logger : opts .Logger ,
101- Test : opts .TestConfig ,
127+ if err != nil {
128+ log .Printf ("[ERROR] Error starting provider: %s" , err )
102129 }
130+ }
103131
104- // assume we have either a v5 or a v6 provider
105- if opts .GRPCProviderFunc != nil {
106- provider := opts .GRPCProviderFunc ()
107- addr := opts .ProviderAddr
108- if addr == "" {
109- addr = "provider"
110- }
111- serveConfig .VersionedPlugins = map [int ]plugin.PluginSet {
112- 5 : {
113- ProviderPluginName : & tf5server.GRPCProviderPlugin {
114- GRPCProvider : func () tfprotov5.ProviderServer {
115- return provider
116- },
117- Name : addr ,
118- },
119- },
120- }
121- if opts .UseTFLogSink != nil {
122- serveConfig .VersionedPlugins [5 ][ProviderPluginName ].(* tf5server.GRPCProviderPlugin ).Opts = append (serveConfig .VersionedPlugins [5 ][ProviderPluginName ].(* tf5server.GRPCProviderPlugin ).Opts , tf5server .WithLoggingSink (opts .UseTFLogSink ))
123- }
132+ func tf5serverServe (opts * ServeOpts ) error {
133+ var tf5serveOpts []tf5server.ServeOpt
124134
125- } else if opts .GRPCProviderV6Func != nil {
126- provider := opts .GRPCProviderV6Func ()
127- addr := opts .ProviderAddr
128- if addr == "" {
129- addr = "provider"
130- }
131- serveConfig .VersionedPlugins = map [int ]plugin.PluginSet {
132- 6 : {
133- ProviderPluginName : & tf6server.GRPCProviderPlugin {
134- GRPCProvider : func () tfprotov6.ProviderServer {
135- return provider
136- },
137- Name : addr ,
138- },
139- },
140- }
141- if opts .UseTFLogSink != nil {
142- serveConfig .VersionedPlugins [6 ][ProviderPluginName ].(* tf6server.GRPCProviderPlugin ).Opts = append (serveConfig .VersionedPlugins [6 ][ProviderPluginName ].(* tf6server.GRPCProviderPlugin ).Opts , tf6server .WithLoggingSink (opts .UseTFLogSink ))
143- }
135+ if opts .Debug {
136+ tf5serveOpts = append (tf5serveOpts , tf5server .WithManagedDebug ())
137+ }
138+
139+ if opts .Logger != nil {
140+ tf5serveOpts = append (tf5serveOpts , tf5server .WithGoPluginLogger (opts .Logger ))
141+ }
142+
143+ if opts .TestConfig != nil {
144+ // Convert send-only channels to bi-directional channels to appease
145+ // the compiler. WithDebug is errantly defined to require
146+ // bi-directional when send-only is actually needed, which may be
147+ // fixed in the future so the opts.TestConfig channels can be passed
148+ // through directly.
149+ closeCh := make (chan struct {})
150+ reattachConfigCh := make (chan * plugin.ReattachConfig )
151+
152+ go func () {
153+ // Always forward close channel receive, since its signaling that
154+ // the channel is closed.
155+ val := <- closeCh
156+ opts .TestConfig .CloseCh <- val
157+ }()
158+
159+ go func () {
160+ val , ok := <- reattachConfigCh
161+
162+ if ok {
163+ opts .TestConfig .ReattachConfigCh <- val
164+ }
165+ }()
166+
167+ tf5serveOpts = append (tf5serveOpts , tf5server .WithDebug (
168+ opts .TestConfig .Context ,
169+ reattachConfigCh ,
170+ closeCh ),
171+ )
172+ }
173+
174+ if opts .UseTFLogSink != nil {
175+ tf5serveOpts = append (tf5serveOpts , tf5server .WithLoggingSink (opts .UseTFLogSink ))
176+ }
177+
178+ return tf5server .Serve (opts .ProviderAddr , opts .GRPCProviderFunc , tf5serveOpts ... )
179+ }
180+
181+ func tf6serverServe (opts * ServeOpts ) error {
182+ var tf6serveOpts []tf6server.ServeOpt
183+
184+ if opts .Debug {
185+ tf6serveOpts = append (tf6serveOpts , tf6server .WithManagedDebug ())
186+ }
187+
188+ if opts .Logger != nil {
189+ tf6serveOpts = append (tf6serveOpts , tf6server .WithGoPluginLogger (opts .Logger ))
190+ }
191+
192+ if opts .TestConfig != nil {
193+ // Convert send-only channels to bi-directional channels to appease
194+ // the compiler. WithDebug is errantly defined to require
195+ // bi-directional when send-only is actually needed, which may be
196+ // fixed in the future so the opts.TestConfig channels can be passed
197+ // through directly.
198+ closeCh := make (chan struct {})
199+ reattachConfigCh := make (chan * plugin.ReattachConfig )
200+
201+ go func () {
202+ val , ok := <- closeCh
203+
204+ if ok {
205+ opts .TestConfig .CloseCh <- val
206+ }
207+ }()
208+
209+ go func () {
210+ val , ok := <- reattachConfigCh
211+
212+ if ok {
213+ opts .TestConfig .ReattachConfigCh <- val
214+ }
215+ }()
216+
217+ tf6serveOpts = append (tf6serveOpts , tf6server .WithDebug (
218+ opts .TestConfig .Context ,
219+ reattachConfigCh ,
220+ closeCh ),
221+ )
222+ }
144223
224+ if opts .UseTFLogSink != nil {
225+ tf6serveOpts = append (tf6serveOpts , tf6server .WithLoggingSink (opts .UseTFLogSink ))
145226 }
146227
147- plugin .Serve (& serveConfig )
228+ return tf6server .Serve (opts . ProviderAddr , opts . GRPCProviderV6Func , tf6serveOpts ... )
148229}
0 commit comments