2017-11-07 19:29:51 +00:00
|
|
|
package plugin
|
|
|
|
|
|
|
|
import (
|
2018-12-20 21:43:52 +00:00
|
|
|
"crypto/tls"
|
2017-11-07 19:29:51 +00:00
|
|
|
"fmt"
|
2018-12-20 21:43:52 +00:00
|
|
|
"net"
|
|
|
|
"time"
|
2017-11-07 19:29:51 +00:00
|
|
|
|
2018-12-20 21:43:52 +00:00
|
|
|
"github.com/hashicorp/go-plugin/internal/proto"
|
2017-11-07 19:29:51 +00:00
|
|
|
"golang.org/x/net/context"
|
|
|
|
"google.golang.org/grpc"
|
|
|
|
"google.golang.org/grpc/credentials"
|
|
|
|
"google.golang.org/grpc/health/grpc_health_v1"
|
|
|
|
)
|
|
|
|
|
2018-12-20 21:43:52 +00:00
|
|
|
func dialGRPCConn(tls *tls.Config, dialer func(string, time.Duration) (net.Conn, error)) (*grpc.ClientConn, error) {
|
2017-11-07 19:29:51 +00:00
|
|
|
// Build dialing options.
|
|
|
|
opts := make([]grpc.DialOption, 0, 5)
|
|
|
|
|
2018-12-20 21:43:52 +00:00
|
|
|
// We use a custom dialer so that we can connect over unix domain sockets.
|
|
|
|
opts = append(opts, grpc.WithDialer(dialer))
|
2017-11-07 19:29:51 +00:00
|
|
|
|
|
|
|
// Fail right away
|
|
|
|
opts = append(opts, grpc.FailOnNonTempDialError(true))
|
|
|
|
|
|
|
|
// If we have no TLS configuration set, we need to explicitly tell grpc
|
|
|
|
// that we're connecting with an insecure connection.
|
2018-12-20 21:43:52 +00:00
|
|
|
if tls == nil {
|
2017-11-07 19:29:51 +00:00
|
|
|
opts = append(opts, grpc.WithInsecure())
|
|
|
|
} else {
|
|
|
|
opts = append(opts, grpc.WithTransportCredentials(
|
2018-12-20 21:43:52 +00:00
|
|
|
credentials.NewTLS(tls)))
|
2017-11-07 19:29:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Connect. Note the first parameter is unused because we use a custom
|
|
|
|
// dialer that has the state to see the address.
|
|
|
|
conn, err := grpc.Dial("unused", opts...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-12-20 21:43:52 +00:00
|
|
|
return conn, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// newGRPCClient creates a new GRPCClient. The Client argument is expected
|
|
|
|
// to be successfully started already with a lock held.
|
|
|
|
func newGRPCClient(doneCtx context.Context, c *Client) (*GRPCClient, error) {
|
|
|
|
conn, err := dialGRPCConn(c.config.TLSConfig, c.dialer)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start the broker.
|
|
|
|
brokerGRPCClient := newGRPCBrokerClient(conn)
|
|
|
|
broker := newGRPCBroker(brokerGRPCClient, c.config.TLSConfig)
|
|
|
|
go broker.Run()
|
|
|
|
go brokerGRPCClient.StartStream()
|
|
|
|
|
|
|
|
cl := &GRPCClient{
|
|
|
|
Conn: conn,
|
|
|
|
Plugins: c.config.Plugins,
|
|
|
|
doneCtx: doneCtx,
|
|
|
|
broker: broker,
|
|
|
|
controller: proto.NewGRPCControllerClient(conn),
|
|
|
|
}
|
|
|
|
|
|
|
|
return cl, nil
|
2017-11-07 19:29:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GRPCClient connects to a GRPCServer over gRPC to dispense plugin types.
|
|
|
|
type GRPCClient struct {
|
|
|
|
Conn *grpc.ClientConn
|
|
|
|
Plugins map[string]Plugin
|
2018-12-20 21:43:52 +00:00
|
|
|
|
|
|
|
doneCtx context.Context
|
|
|
|
broker *GRPCBroker
|
|
|
|
|
|
|
|
controller proto.GRPCControllerClient
|
2017-11-07 19:29:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ClientProtocol impl.
|
|
|
|
func (c *GRPCClient) Close() error {
|
2018-12-20 21:43:52 +00:00
|
|
|
c.broker.Close()
|
|
|
|
c.controller.Shutdown(c.doneCtx, &proto.Empty{})
|
2017-11-07 19:29:51 +00:00
|
|
|
return c.Conn.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
// ClientProtocol impl.
|
|
|
|
func (c *GRPCClient) Dispense(name string) (interface{}, error) {
|
|
|
|
raw, ok := c.Plugins[name]
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("unknown plugin type: %s", name)
|
|
|
|
}
|
|
|
|
|
|
|
|
p, ok := raw.(GRPCPlugin)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("plugin %q doesn't support gRPC", name)
|
|
|
|
}
|
|
|
|
|
2018-12-20 21:43:52 +00:00
|
|
|
return p.GRPCClient(c.doneCtx, c.broker, c.Conn)
|
2017-11-07 19:29:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ClientProtocol impl.
|
|
|
|
func (c *GRPCClient) Ping() error {
|
|
|
|
client := grpc_health_v1.NewHealthClient(c.Conn)
|
|
|
|
_, err := client.Check(context.Background(), &grpc_health_v1.HealthCheckRequest{
|
|
|
|
Service: GRPCServiceName,
|
|
|
|
})
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|