mirror of
https://github.com/hashicorp/terraform.git
synced 2026-06-09 00:42:48 -04:00
rpcapi: Initial stubbing of the plugin server setup
This commit is contained in:
parent
8f9b085501
commit
38d66ea74d
4 changed files with 136 additions and 0 deletions
9
internal/rpcapi/dependencies.go
Normal file
9
internal/rpcapi/dependencies.go
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package rpcapi
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/internal/rpcapi/terraform1"
|
||||
)
|
||||
|
||||
type dependenciesServer struct {
|
||||
terraform1.UnimplementedDependenciesServer
|
||||
}
|
||||
42
internal/rpcapi/plugin.go
Normal file
42
internal/rpcapi/plugin.go
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
package rpcapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/terraform/internal/rpcapi/terraform1"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type corePlugin struct {
|
||||
plugin.Plugin
|
||||
}
|
||||
|
||||
func (p *corePlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
|
||||
// This codebase only provides a server implementation of this plugin.
|
||||
// Clients must live elsewhere.
|
||||
return nil, fmt.Errorf("there is no client implementation in this codebase")
|
||||
}
|
||||
|
||||
func (p *corePlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
|
||||
// We initially only register the setup server, because the registration
|
||||
// of other services can vary depending on the capabilities negotiated
|
||||
// during handshake.
|
||||
setup := newSetupServer(p.handshakeFunc(s))
|
||||
terraform1.RegisterSetupServer(s, setup)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *corePlugin) handshakeFunc(s *grpc.Server) func(context.Context, *terraform1.ClientCapabilities) (*terraform1.ServerCapabilities, error) {
|
||||
return func(ctx context.Context, clientCaps *terraform1.ClientCapabilities) (*terraform1.ServerCapabilities, error) {
|
||||
|
||||
// If handshaking is successful (which it currently always is, because
|
||||
// we don't have any special capabilities to negotiate yet) then we
|
||||
// will register all of the other services so the client can being
|
||||
// doing real work. (In future the details of what we register here
|
||||
// might vary based on the negotiated capabilities.)
|
||||
terraform1.RegisterDependenciesServer(s, &dependenciesServer{})
|
||||
return &terraform1.ServerCapabilities{}, nil
|
||||
}
|
||||
}
|
||||
35
internal/rpcapi/server.go
Normal file
35
internal/rpcapi/server.go
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
package rpcapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/go-plugin"
|
||||
)
|
||||
|
||||
// ServePlugin attempts to complete the go-plugin protocol handshake, and then
|
||||
// if successful starts the plugin server and blocks until externally
|
||||
// terminated.
|
||||
func ServePlugin(ctx context.Context) error {
|
||||
plugin.Serve(&plugin.ServeConfig{
|
||||
HandshakeConfig: handshake,
|
||||
VersionedPlugins: map[int]plugin.PluginSet{
|
||||
1: plugin.PluginSet{
|
||||
"tfcore": &corePlugin{},
|
||||
},
|
||||
},
|
||||
GRPCServer: plugin.DefaultGRPCServer,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// handshake is the HandshakeConfig used to begin negotiation between client
|
||||
// and server.
|
||||
var handshake = plugin.HandshakeConfig{
|
||||
// The ProtocolVersion is the version that must match between TF core
|
||||
// and TF plugins.
|
||||
ProtocolVersion: 1,
|
||||
|
||||
// The magic cookie values should NEVER be changed.
|
||||
MagicCookieKey: "TERRAFORM_RPCAPI_COOKIE",
|
||||
MagicCookieValue: "fba0991c9bcd453982f0d88e2da95940",
|
||||
}
|
||||
50
internal/rpcapi/setup.go
Normal file
50
internal/rpcapi/setup.go
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
package rpcapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/rpcapi/terraform1"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// setupServer is an implementation of the "Setup" service defined in our
|
||||
// terraform1 package.
|
||||
//
|
||||
// This service is here only to offer the "Handshake" function, which clients
|
||||
// must call to negotiate access to any other services. This is really just
|
||||
// an adapter around a handshake function implemented on [corePlugin].
|
||||
type setupServer struct {
|
||||
terraform1.UnimplementedSetupServer
|
||||
|
||||
// initOthers is the callback used to perform the capability negotiation
|
||||
// step and initialize all of the other API services based on what was
|
||||
// negotiated.
|
||||
initOthers func(context.Context, *terraform1.ClientCapabilities) (*terraform1.ServerCapabilities, error)
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func newSetupServer(initOthers func(context.Context, *terraform1.ClientCapabilities) (*terraform1.ServerCapabilities, error)) terraform1.SetupServer {
|
||||
return &setupServer{
|
||||
initOthers: initOthers,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *setupServer) Handshake(ctx context.Context, req *terraform1.Handshake_Request) (*terraform1.Handshake_Response, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if s.initOthers == nil {
|
||||
return nil, status.Error(codes.FailedPrecondition, "handshake already completed")
|
||||
}
|
||||
|
||||
serverCaps, err := s.initOthers(ctx, req.Capabilities)
|
||||
s.initOthers = nil // cannot handshake again
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &terraform1.Handshake_Response{
|
||||
Capabilities: serverCaps,
|
||||
}, nil
|
||||
}
|
||||
Loading…
Reference in a new issue