]>
Commit | Line | Data |
---|---|---|
15c0b25d AP |
1 | package plugin |
2 | ||
3 | import ( | |
4 | "crypto/tls" | |
5 | "fmt" | |
6 | "net" | |
7 | "time" | |
8 | ||
107c1cdb | 9 | "github.com/hashicorp/go-plugin/internal/plugin" |
15c0b25d AP |
10 | "golang.org/x/net/context" |
11 | "google.golang.org/grpc" | |
12 | "google.golang.org/grpc/credentials" | |
13 | "google.golang.org/grpc/health/grpc_health_v1" | |
14 | ) | |
15 | ||
16 | func dialGRPCConn(tls *tls.Config, dialer func(string, time.Duration) (net.Conn, error)) (*grpc.ClientConn, error) { | |
17 | // Build dialing options. | |
18 | opts := make([]grpc.DialOption, 0, 5) | |
19 | ||
107c1cdb | 20 | // We use a custom dialer so that we can connect over unix domain sockets. |
15c0b25d AP |
21 | opts = append(opts, grpc.WithDialer(dialer)) |
22 | ||
15c0b25d AP |
23 | // Fail right away |
24 | opts = append(opts, grpc.FailOnNonTempDialError(true)) | |
25 | ||
26 | // If we have no TLS configuration set, we need to explicitly tell grpc | |
27 | // that we're connecting with an insecure connection. | |
28 | if tls == nil { | |
29 | opts = append(opts, grpc.WithInsecure()) | |
30 | } else { | |
31 | opts = append(opts, grpc.WithTransportCredentials( | |
32 | credentials.NewTLS(tls))) | |
33 | } | |
34 | ||
35 | // Connect. Note the first parameter is unused because we use a custom | |
36 | // dialer that has the state to see the address. | |
37 | conn, err := grpc.Dial("unused", opts...) | |
38 | if err != nil { | |
39 | return nil, err | |
40 | } | |
41 | ||
42 | return conn, nil | |
43 | } | |
44 | ||
45 | // newGRPCClient creates a new GRPCClient. The Client argument is expected | |
46 | // to be successfully started already with a lock held. | |
47 | func newGRPCClient(doneCtx context.Context, c *Client) (*GRPCClient, error) { | |
48 | conn, err := dialGRPCConn(c.config.TLSConfig, c.dialer) | |
49 | if err != nil { | |
50 | return nil, err | |
51 | } | |
52 | ||
53 | // Start the broker. | |
54 | brokerGRPCClient := newGRPCBrokerClient(conn) | |
55 | broker := newGRPCBroker(brokerGRPCClient, c.config.TLSConfig) | |
56 | go broker.Run() | |
57 | go brokerGRPCClient.StartStream() | |
58 | ||
107c1cdb ND |
59 | cl := &GRPCClient{ |
60 | Conn: conn, | |
61 | Plugins: c.config.Plugins, | |
62 | doneCtx: doneCtx, | |
63 | broker: broker, | |
64 | controller: plugin.NewGRPCControllerClient(conn), | |
65 | } | |
66 | ||
67 | return cl, nil | |
15c0b25d AP |
68 | } |
69 | ||
70 | // GRPCClient connects to a GRPCServer over gRPC to dispense plugin types. | |
71 | type GRPCClient struct { | |
72 | Conn *grpc.ClientConn | |
73 | Plugins map[string]Plugin | |
74 | ||
75 | doneCtx context.Context | |
76 | broker *GRPCBroker | |
107c1cdb ND |
77 | |
78 | controller plugin.GRPCControllerClient | |
15c0b25d AP |
79 | } |
80 | ||
81 | // ClientProtocol impl. | |
82 | func (c *GRPCClient) Close() error { | |
83 | c.broker.Close() | |
107c1cdb | 84 | c.controller.Shutdown(c.doneCtx, &plugin.Empty{}) |
15c0b25d AP |
85 | return c.Conn.Close() |
86 | } | |
87 | ||
88 | // ClientProtocol impl. | |
89 | func (c *GRPCClient) Dispense(name string) (interface{}, error) { | |
90 | raw, ok := c.Plugins[name] | |
91 | if !ok { | |
92 | return nil, fmt.Errorf("unknown plugin type: %s", name) | |
93 | } | |
94 | ||
95 | p, ok := raw.(GRPCPlugin) | |
96 | if !ok { | |
97 | return nil, fmt.Errorf("plugin %q doesn't support gRPC", name) | |
98 | } | |
99 | ||
100 | return p.GRPCClient(c.doneCtx, c.broker, c.Conn) | |
101 | } | |
102 | ||
103 | // ClientProtocol impl. | |
104 | func (c *GRPCClient) Ping() error { | |
105 | client := grpc_health_v1.NewHealthClient(c.Conn) | |
106 | _, err := client.Check(context.Background(), &grpc_health_v1.HealthCheckRequest{ | |
107 | Service: GRPCServiceName, | |
108 | }) | |
109 | ||
110 | return err | |
111 | } |