File: guide-plugin-write-non-go.md

package info (click to toggle)
golang-github-hashicorp-go-plugin 1.0.1-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 552 kB
  • sloc: python: 38; makefile: 4
file content (150 lines) | stat: -rw-r--r-- 4,793 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# Writing Plugins Without Go

This guide explains how to write a go-plugin compatible plugin using
a programming language other than Go. go-plugin supports plugins using
[gRPC](http://www.grpc.io). This makes it relatively simple to write plugins
using other languages!

Minimal knowledge about gRPC is assumed. We recommend reading the
[gRPC Go Tutorial](http://www.grpc.io/docs/tutorials/basic/go.html). This
alone is enough gRPC knowledge to continue.

This guide will implement the kv example in Python.
Full source code for the examples present in this guide
[is available in the examples/grpc folder](https://github.com/hashicorp/go-plugin/tree/master/examples/grpc).

## 1. Implement the Service

The first step is to implement the gRPC server for the protocol buffers
service that your plugin defines. This is a standard gRPC server.
For the KV service, the service looks like this:

```proto
service KV {
    rpc Get(GetRequest) returns (GetResponse);
    rpc Put(PutRequest) returns (Empty);
}
```

We can implement that using Python as easily as:

```python
class KVServicer(kv_pb2_grpc.KVServicer):
    """Implementation of KV service."""

    def Get(self, request, context):
        filename = "kv_"+request.key
        with open(filename, 'r') as f:
            result = kv_pb2.GetResponse()
            result.value = f.read()
            return result

    def Put(self, request, context):
        filename = "kv_"+request.key
        value = "{0}\n\nWritten from plugin-python".format(request.value)
        with open(filename, 'w') as f:
            f.write(value)

        return kv_pb2.Empty()

```

Great! With that, we have a fully functioning implementation of the service.
You can test this using standard gRPC testing mechanisms.

## 2. Serve the Service

Next, we need to create a gRPC server and serve the service we just made.

In Python:

```python
# Make the server
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))

# Add our service
kv_pb2_grpc.add_KVServicer_to_server(KVServicer(), server)

# Listen on a port
server.add_insecure_port(':1234')

# Start
server.start()
```

You can listen on any TCP address or Unix domain socket. go-plugin does
assume that connections are reliable (local), so you should not serve
your plugin across the network.

## 3. Add the gRPC Health Checking Service

go-plugin requires the
[gRPC Health Checking Service](https://github.com/grpc/grpc/blob/master/doc/health-checking.md)
to be registered on your server. You must register the status of "plugin" to be SERVING.

The health checking service is used by go-plugin to determine if everything
is healthy with the connection. If you don't implement this service, your
process may be abruptly restarted and your plugins are likely to be unreliable.

```
health = HealthServicer()
health.set("plugin", health_pb2.HealthCheckResponse.ServingStatus.Value('SERVING'))
health_pb2_grpc.add_HealthServicer_to_server(health, server)
```

## 4. Output Handshake Information

The final step is to output the handshake information to stdout. go-plugin
reads a single line from stdout to determine how to connect to your plugin,
what protocol it is using, etc.


The structure is:

```
CORE-PROTOCOL-VERSION | APP-PROTOCOL-VERSION | NETWORK-TYPE | NETWORK-ADDR | PROTOCOL
```

Where:

  * `CORE-PROTOCOL-VERSION` is the protocol version for go-plugin itself.
    The current value is `1`. Please use this value. Any other value will
    cause your plugin to not load.

  * `APP-PROTOCOL-VERSION` is the protocol version for the application data.
    This is determined by the application. You must reference the documentation
    for your application to determine the desired value.

  * `NETWORK-TYPE` and `NETWORK-ADDR` are the networking information for
    connecting to this plugin. The type must be "unix" or "tcp". The address
    is a path to the Unix socket for "unix" and an IP address for "tcp".

  * `PROTOCOL` is the named protocol that the connection will use. If this
    is omitted (older versions), this is "netrpc" for Go net/rpc. This can
    also be "grpc". This is the protocol that the plugin wants to speak to
    the host process with.

For our example that is:

```
1|1|tcp|127.0.0.1:1234|grpc
```

The only element you'll have to be careful about is the second one (the
`APP-PROTOCOL-VERISON`). This will depend on the application you're
building a plugin for. Please reference their documentation for more
information.

## 5. Done!

And we're done!

Configure the host application (the application you're writing a plugin
for) to execute your Python application. Configuring plugins is specific
to the host application.

For our example, we used an environmental variable, and it looks like this:

```sh
$ export KV_PLUGIN="python plugin.py"
```