Skip to content

Commit

Permalink
Plugin restapi: New plugin to provide a REST API.
Browse files Browse the repository at this point in the history
  • Loading branch information
octo committed May 15, 2020
1 parent 79ab972 commit 7b20f6f
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 0 deletions.
36 changes: 36 additions & 0 deletions restapi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# restapi

Plugin **restapi** provides a REST API to communicate with the *collectd*
daemon.

## Description

The *restapi plugin* starts a webserver and waits for incoming REST API
requests.

## Building

To build this plugin, the collectd header files are required.

On Debian and Ubuntu, the collectd headers are available from the
`collectd-dev` package. Once installed, add the import paths to the
`CGI_CPPFLAGS`:

```bash
export CGO_CPPFLAGS="-I/usr/include/collectd/core/daemon \
-I/usr/include/collectd/core -I/usr/include/collectd"
```

Alternatively, you can grab the collectd sources, run the `configure` script,
and reference the header files from there:

```bash
TOP_SRCDIR="${HOME}/collectd"
export CGO_CPPFLAGS="-I${TOP_SRCDIR}/src -I${TOP_SRCDIR}/src/daemon"
```

Then build the plugin with the "c-shared" buildmode:

```bash
go build -buildmode=c-shared -o restapi.so
```
76 changes: 76 additions & 0 deletions restapi/restapi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package main

import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"time"

"collectd.org/api"
"collectd.org/plugin"
"go.uber.org/multierr"
)

const pluginName = "restapi"

type restapi struct {
srv *http.Server
}

func init() {
mux := http.NewServeMux()
mux.HandleFunc("/valueList", valueListHandler)

api := restapi{
srv: &http.Server{
Addr: ":8080",
Handler: mux,
},
}

go func() {
if err := api.srv.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
plugin.Errorf("%s plugin: ListenAndServe(): %v", pluginName, err)
}
}()

plugin.RegisterShutdown(pluginName, api)
}

func valueListHandler(w http.ResponseWriter, req *http.Request) {
if req.Method != http.MethodPost {
w.WriteHeader(http.StatusNotImplemented)
fmt.Fprintln(w, "Only POST is currently supported.")
return
}

var vls []api.ValueList
if err := json.NewDecoder(req.Body).Decode(&vls); err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintln(w, "parsing JSON failed:", err)
return
}

var errs error
for _, vl := range vls {
errs = multierr.Append(errs,
plugin.Write(req.Context(), &vl))
}

if errs != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintln(w, "plugin.Write():", errs)
return
}
}

func (api restapi) Shutdown(ctx context.Context) error {
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()

return api.srv.Shutdown(ctx)
}

func main() {} // ignored

0 comments on commit 7b20f6f

Please sign in to comment.