Skip to content

Commit 18184aa

Browse files
committed
fea: grpc.Plugin & grpc.Serve
1 parent ac6cc71 commit 18184aa

8 files changed

Lines changed: 215 additions & 51 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.16
55
require (
66
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2
77
github.com/fatih/color v1.12.0
8-
github.com/hashicorp/go-hclog v0.16.2 // indirect
8+
github.com/hashicorp/go-hclog v0.16.2
99
github.com/hashicorp/go-plugin v1.4.2
1010
github.com/hashicorp/yamux v0.0.0-20210707203944-259a57b3608c // indirect
1111
github.com/hokaccha/go-prettyjson v0.0.0-20210113012101-fb4e108d2519 // indirect

grpc/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# GRPC Plugin
2+
3+
## Updating the Protocol
4+
5+
If you update the protocol buffers file, you can regenerate the file using the following command from this directory. You do not need to run this if you're just trying the example.
6+
7+
```bash
8+
protoc -I proto/ proto/model.proto --go_out=plugins=grpc:proto/
9+
```
10+
11+
## More
12+
13+
https://github.com/hashicorp/go-plugin/tree/master/examples/grpc

grpc/grpc.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func (m *ClientGRPC) Exec(name string, args ...interface{}) (*Response, error) {
2626
return nil, err
2727
}
2828

29-
return &Response{Bytes: res.Response}, nil
29+
return &Response{Bytes: res.Response, Type: res.Type}, nil
3030
}
3131

3232
// ServerGRPC Here is the gRPC server that ClientGRPC talks to.
@@ -43,5 +43,5 @@ func (m *ServerGRPC) Exec(ctx context.Context, req *proto.Request) (*proto.Respo
4343
return nil, err
4444
}
4545
v, err := m.Impl.Exec(req.Name, args...)
46-
return &proto.Response{Response: v.Bytes}, err
46+
return &proto.Response{Response: v.Bytes, Type: v.Type}, err
4747
}

grpc/intefaces.go

Lines changed: 44 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,60 +2,43 @@ package grpc
22

33
import (
44
"context"
5-
"encoding/json"
65

76
"github.com/hashicorp/go-plugin"
87
"github.com/yaoapp/kun/grpc/proto"
9-
"github.com/yaoapp/kun/maps"
108
"google.golang.org/grpc"
119
)
1210

13-
// Handshake is a common handshake that is shared by plugin and host.
14-
var Handshake = plugin.HandshakeConfig{
15-
// This isn't required when using VersionedPlugins
16-
ProtocolVersion: 1,
17-
MagicCookieKey: "MODEL_PLUGIN",
18-
MagicCookieValue: "hello",
19-
}
11+
// Level represents a log level.
12+
type Level int32
2013

21-
// PluginMap is the map of plugins we can dispense.
22-
var PluginMap = map[string]plugin.Plugin{
23-
"model": &ModelGRPCPlugin{},
24-
}
14+
const (
15+
// NoLevel is a special level used to indicate that no level has been
16+
// set and allow for a default to be used.
17+
NoLevel Level = 0
2518

26-
// Model is the interface that we're exposing as a plugin.
27-
type Model interface {
28-
Exec(name string, args ...interface{}) (*Response, error)
29-
}
19+
// Trace is the most verbose level. Intended to be used for the tracing
20+
// of actions in code, such as function enters/exits, etc.
21+
Trace Level = 1
3022

31-
// Response GRPC Response
32-
type Response struct {
33-
Bytes []byte
34-
}
23+
// Debug information for programmer lowlevel analysis.
24+
Debug Level = 2
3525

36-
// Bind bind struct
37-
func (res Response) Bind(v interface{}) error {
38-
return json.Unmarshal(res.Bytes, &v)
39-
}
26+
// Info information about steady state operations.
27+
Info Level = 3
4028

41-
// Map cast to map
42-
func (res Response) Map() (maps.MapStrAny, error) {
43-
v := maps.MakeMapStrAny()
44-
err := json.Unmarshal(res.Bytes, &v)
45-
if err != nil {
46-
return nil, err
47-
}
48-
return v, nil
49-
}
29+
// Warn information about rare but handled events.
30+
Warn Level = 4
5031

51-
// MustMap cast to map
52-
func (res Response) MustMap() maps.MapStrAny {
53-
v := maps.MakeMapStrAny()
54-
err := json.Unmarshal(res.Bytes, &v)
55-
if err != nil {
56-
panic(err)
57-
}
58-
return v
32+
// Error information about unrecoverable events.
33+
Error Level = 5
34+
35+
// Off disables all logging output.
36+
Off Level = 6
37+
)
38+
39+
// Model is the interface that we're exposing as a plugin.
40+
type Model interface {
41+
Exec(name string, args ...interface{}) (*Response, error)
5942
}
6043

6144
// ModelGRPCPlugin This is the implementation of plugin.Plugin so we can serve/consume this.
@@ -67,6 +50,25 @@ type ModelGRPCPlugin struct {
6750
Impl Model
6851
}
6952

53+
// Handshake is a common handshake that is shared by plugin and host.
54+
var Handshake = plugin.HandshakeConfig{
55+
// This isn't required when using VersionedPlugins
56+
ProtocolVersion: 1,
57+
MagicCookieKey: "GOU_MODEL_PLUGIN",
58+
MagicCookieValue: "GOU VER0.6.0",
59+
}
60+
61+
// PluginMap is the map of plugins we can dispense.
62+
var PluginMap = map[string]plugin.Plugin{
63+
"model": &ModelGRPCPlugin{},
64+
}
65+
66+
// Response GRPC Response
67+
type Response struct {
68+
Bytes []byte
69+
Type string
70+
}
71+
7072
// GRPCServer the GRPC Server
7173
func (p *ModelGRPCPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
7274
proto.RegisterModelServer(s, &ServerGRPC{Impl: p.Impl})

grpc/proto/model.pb.go

Lines changed: 15 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

grpc/proto/model.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ message Request {
1010

1111
message Response {
1212
bytes response = 1;
13+
string type = 2;
1314
}
1415

1516
service Model {

grpc/response.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package grpc
2+
3+
import (
4+
jsoniter "github.com/json-iterator/go"
5+
"github.com/yaoapp/kun/any"
6+
"github.com/yaoapp/kun/maps"
7+
)
8+
9+
// Bind bind struct
10+
func (res Response) Bind(v interface{}) error {
11+
return jsoniter.Unmarshal(res.Bytes, &v)
12+
}
13+
14+
// MustBind bind struct
15+
func (res Response) MustBind(v interface{}) {
16+
err := res.Bind(&v)
17+
if err != nil {
18+
panic(err)
19+
}
20+
}
21+
22+
// Interface bind struct
23+
func (res Response) Interface() (interface{}, error) {
24+
var v interface{}
25+
err := jsoniter.Unmarshal(res.Bytes, &v)
26+
return v, err
27+
}
28+
29+
// MustInterface bind struct
30+
func (res Response) MustInterface() interface{} {
31+
v, err := res.Interface()
32+
if err != nil {
33+
panic(err)
34+
}
35+
return v
36+
}
37+
38+
// Map cast to map
39+
func (res Response) Map() (maps.MapStrAny, error) {
40+
v := maps.Map{}
41+
err := jsoniter.Unmarshal(res.Bytes, &v)
42+
if err != nil {
43+
return nil, err
44+
}
45+
return v, nil
46+
}
47+
48+
// MustMap cast to map
49+
func (res Response) MustMap() maps.MapStrAny {
50+
v, err := res.Map()
51+
if err != nil {
52+
panic(err)
53+
}
54+
return v
55+
}
56+
57+
// Array cast to array | slice
58+
func (res Response) Array() ([]interface{}, error) {
59+
v := []interface{}{}
60+
err := jsoniter.Unmarshal(res.Bytes, &v)
61+
if err != nil {
62+
return nil, err
63+
}
64+
return v, nil
65+
}
66+
67+
// MustArray cast to array | slice
68+
func (res Response) MustArray() []interface{} {
69+
v, err := res.Array()
70+
if err != nil {
71+
panic(err)
72+
}
73+
return v
74+
}
75+
76+
// Value get the response value
77+
func (res Response) Value() (interface{}, error) {
78+
switch res.Type {
79+
case "interface":
80+
return res.Interface()
81+
case "string":
82+
return string(res.Bytes), nil
83+
case "integer", "int":
84+
return any.Of(string(res.Bytes)).CInt(), nil
85+
case "float", "double":
86+
return any.Of(string(res.Bytes)).CFloat(), nil
87+
case "map":
88+
return res.Map()
89+
case "array", "slice":
90+
return res.Array()
91+
default:
92+
return res.Bytes, nil
93+
}
94+
}
95+
96+
// MustValue get the response value
97+
func (res Response) MustValue() interface{} {
98+
v, err := res.Value()
99+
if err != nil {
100+
panic(err)
101+
}
102+
return v
103+
}

grpc/serve.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package grpc
2+
3+
import (
4+
"io"
5+
6+
"github.com/hashicorp/go-hclog"
7+
"github.com/hashicorp/go-plugin"
8+
)
9+
10+
// Plugin Here is a real implementation of Pluine that writes to a local file with
11+
// the key name and the contents are the value of the key.
12+
type Plugin struct {
13+
Logger hclog.Logger
14+
}
15+
16+
// SetLogger set logger output
17+
func (plugin *Plugin) SetLogger(output io.Writer, level Level) {
18+
logger := hclog.New(&hclog.LoggerOptions{
19+
Level: hclog.Level(level),
20+
Output: output,
21+
JSONFormat: true,
22+
})
23+
plugin.Logger = logger
24+
}
25+
26+
// Serve GRPC Server
27+
func Serve(model Model) {
28+
pluginMap := map[string]plugin.Plugin{
29+
"model": &ModelGRPCPlugin{Impl: model},
30+
}
31+
plugin.Serve(&plugin.ServeConfig{
32+
HandshakeConfig: Handshake,
33+
Plugins: pluginMap,
34+
GRPCServer: plugin.DefaultGRPCServer,
35+
})
36+
}

0 commit comments

Comments
 (0)