Skip to content

Commit 7b20f6f

Browse files
committed
Plugin restapi: New plugin to provide a REST API.
1 parent 79ab972 commit 7b20f6f

File tree

2 files changed

+112
-0
lines changed

2 files changed

+112
-0
lines changed

restapi/README.md

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# restapi
2+
3+
Plugin **restapi** provides a REST API to communicate with the *collectd*
4+
daemon.
5+
6+
## Description
7+
8+
The *restapi plugin* starts a webserver and waits for incoming REST API
9+
requests.
10+
11+
## Building
12+
13+
To build this plugin, the collectd header files are required.
14+
15+
On Debian and Ubuntu, the collectd headers are available from the
16+
`collectd-dev` package. Once installed, add the import paths to the
17+
`CGI_CPPFLAGS`:
18+
19+
```bash
20+
export CGO_CPPFLAGS="-I/usr/include/collectd/core/daemon \
21+
-I/usr/include/collectd/core -I/usr/include/collectd"
22+
```
23+
24+
Alternatively, you can grab the collectd sources, run the `configure` script,
25+
and reference the header files from there:
26+
27+
```bash
28+
TOP_SRCDIR="${HOME}/collectd"
29+
export CGO_CPPFLAGS="-I${TOP_SRCDIR}/src -I${TOP_SRCDIR}/src/daemon"
30+
```
31+
32+
Then build the plugin with the "c-shared" buildmode:
33+
34+
```bash
35+
go build -buildmode=c-shared -o restapi.so
36+
```

restapi/restapi.go

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"errors"
7+
"fmt"
8+
"net/http"
9+
"time"
10+
11+
"collectd.org/api"
12+
"collectd.org/plugin"
13+
"go.uber.org/multierr"
14+
)
15+
16+
const pluginName = "restapi"
17+
18+
type restapi struct {
19+
srv *http.Server
20+
}
21+
22+
func init() {
23+
mux := http.NewServeMux()
24+
mux.HandleFunc("/valueList", valueListHandler)
25+
26+
api := restapi{
27+
srv: &http.Server{
28+
Addr: ":8080",
29+
Handler: mux,
30+
},
31+
}
32+
33+
go func() {
34+
if err := api.srv.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
35+
plugin.Errorf("%s plugin: ListenAndServe(): %v", pluginName, err)
36+
}
37+
}()
38+
39+
plugin.RegisterShutdown(pluginName, api)
40+
}
41+
42+
func valueListHandler(w http.ResponseWriter, req *http.Request) {
43+
if req.Method != http.MethodPost {
44+
w.WriteHeader(http.StatusNotImplemented)
45+
fmt.Fprintln(w, "Only POST is currently supported.")
46+
return
47+
}
48+
49+
var vls []api.ValueList
50+
if err := json.NewDecoder(req.Body).Decode(&vls); err != nil {
51+
w.WriteHeader(http.StatusBadRequest)
52+
fmt.Fprintln(w, "parsing JSON failed:", err)
53+
return
54+
}
55+
56+
var errs error
57+
for _, vl := range vls {
58+
errs = multierr.Append(errs,
59+
plugin.Write(req.Context(), &vl))
60+
}
61+
62+
if errs != nil {
63+
w.WriteHeader(http.StatusInternalServerError)
64+
fmt.Fprintln(w, "plugin.Write():", errs)
65+
return
66+
}
67+
}
68+
69+
func (api restapi) Shutdown(ctx context.Context) error {
70+
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
71+
defer cancel()
72+
73+
return api.srv.Shutdown(ctx)
74+
}
75+
76+
func main() {} // ignored

0 commit comments

Comments
 (0)