From ff8c625da642d93bd2b2b9f9ce247176364198b2 Mon Sep 17 00:00:00 2001 From: andrewhoff Date: Sun, 12 Apr 2020 01:22:11 -0700 Subject: [PATCH] documentation and a working container image --- .env.example | 2 -- Dockerfile | 12 ++++++++++++ Makefile | 10 ++++++++-- README.md | 26 ++++++++++++++++++++------ coap/router.go | 20 +++++++++----------- coap/table.go | 8 ++++---- go.mod | 3 +-- go.sum | 4 ++++ hooks/api.go | 5 ++--- hooks/hooks.go | 22 +++++++++++++++------- main.go | 3 +-- 11 files changed, 76 insertions(+), 39 deletions(-) delete mode 100755 .env.example create mode 100644 Dockerfile diff --git a/.env.example b/.env.example deleted file mode 100755 index 1af82a4..0000000 --- a/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -DB_FILENAME=hooks.db -ADMIN_BEARER=`openssl rand -base64 27` diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9aed847 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM golang:1.13 as builder +COPY . /coap-hooks-router +WORKDIR /coap-hooks-router +ENV CGO_ENABLED 0 +RUN go build -o server . + +FROM scratch as release +ARG ADMIN_BEARER +WORKDIR / +VOLUME /dbdata +COPY --from=builder /coap-hooks-router/server /server +ENTRYPOINT ["/server"] diff --git a/Makefile b/Makefile index 09d3cbf..636e25a 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,8 @@ -run: - go run main.go \ No newline at end of file +dev: + go run main.go + +docker-build: + docker build -t coap-hooks-router . + +docker-run: + docker run -v container-db:/dbdata -e ADMIN_BEARER=${ADMIN_BEARER} -p 8081:8081 -p 5683:5683 coap-hooks-router \ No newline at end of file diff --git a/README.md b/README.md index 739e80c..2543e0d 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,25 @@ -# To Run -- `cp .env.example .env` -- generate your ADMIN_BEARER -- `make run` +# CoAP Webhooks Router -# To create a new hook +## To Run +- generate your ADMIN_BEARER (`openssl rand -base64 27` can do it) and export ADMIN_BEARER="generatedbearertokenvalue" in your shell +- `make dev` for local run +- `make docker-run` for local docker run (functionally the same as the previous) +- `make docker-build` to build the docker image +- the Admin Hooks API runs on 8081, and the CoAP server runs on 5683 + +## Using the Hooks API + +### Create a new hook - `curl -v -d '{"owner":"{OWNER_NAME}","name":"{HOOK_NAME}","destination":"{HTTPS_ADDRESS}"}' -H "Content-Type:application/json" -H "Authorization: Bearer {BEARER}" http://localhost:8081/api/hooks/` -# To list all hooks for an owner +### List all hooks for an owner - `curl -v -H "Accept:application/json" -H "Authorization: Bearer {BEARER}" http://localhost:8081/api/hooks/{OWNER_NAME}` +### List all hooks existing in the system +- `curl -v -H "Accept:application/json" -H "Authorization: Bearer {BEARER}" http://localhost:8081/api/hooks` + +### Delete an existing hook by name +- `curl -v -X DELETE -H "Content-Type:application/json" -H "Authorization: Bearer {BEARER}" http://localhost:8081/api/hooks/{OWNER_NAME}/{HOOK_NAME}` + +### Delete all hooks for an owner +- `curl -v -X DELETE -H "Content-Type:application/json" -H "Authorization: Bearer {BEARER}" http://localhost:8081/api/hooks/{OWNER_NAME}` diff --git a/coap/router.go b/coap/router.go index d82c1c4..23d92b4 100644 --- a/coap/router.go +++ b/coap/router.go @@ -9,6 +9,7 @@ import ( "github.com/coapcloud/coap-hooks-router/hooks" "github.com/coapcloud/go-coap" + "github.com/coapcloud/go-coap/codes" ) func ListenAndServe(hooksRepo hooks.Repository, port int) { @@ -21,18 +22,15 @@ func ListenAndServe(hooksRepo hooks.Repository, port int) { // start listening for hook API events to register and deregister routes go func() { for e := range hooksRepo.Events() { + changedHooks := e.Hooks + switch e.EventType { case hooks.EventTypeCreate: - r.HotAddRoute(*e.Hook) + r.HotAddRoute(*changedHooks[0]) case hooks.EventTypeDelete: - r.HotRemoveRoute(*e.Hook) + r.HotRemoveRoute(*changedHooks[0]) case hooks.EventTypeDeleteForOwner: - ownerHooks, err := hooksRepo.ListHooksForOwner(e.Hook.Owner) - if err != nil { - log.Fatal("couldn't list all hooks") - } - - for _, v := range ownerHooks { + for _, v := range changedHooks { err := r.HotRemoveRoute(*v) if err != nil { log.Printf("couldn't remove hook: %v with error: %v", *v, err) @@ -60,7 +58,7 @@ func (r routeTable) ServeCOAP(w coap.ResponseWriter, req *coap.Request) { dest, ok := r.match(req.Msg.Code(), req.Msg.Path()) if !ok { log.Println("could not match route") - w.SetCode(coap.NotFound) + w.SetCode(codes.NotFound) respBdy = []byte("not found") } @@ -70,7 +68,7 @@ func (r routeTable) ServeCOAP(w coap.ResponseWriter, req *coap.Request) { respBdy, err = dest.Fire(buf) if err != nil { log.Printf("Error while trying to invoke webhook %v\n", err) - w.SetCode(coap.InternalServerError) + w.SetCode(codes.InternalServerError) respBdy = []byte("could not run callback for request") } @@ -84,7 +82,7 @@ func (r routeTable) ServeCOAP(w coap.ResponseWriter, req *coap.Request) { } } -func (r *routeTable) match(verb coap.COAPCode, pathComponents []string) (hooks.Hook, bool) { +func (r *routeTable) match(verb codes.Code, pathComponents []string) (hooks.Hook, bool) { r.RLock() defer r.RUnlock() diff --git a/coap/table.go b/coap/table.go index dfd20d1..788dc5d 100644 --- a/coap/table.go +++ b/coap/table.go @@ -7,7 +7,7 @@ import ( "sync" "github.com/coapcloud/coap-hooks-router/hooks" - "github.com/coapcloud/go-coap" + "github.com/coapcloud/go-coap/codes" "github.com/derekparker/trie" ) @@ -57,7 +57,7 @@ func (r *routeTable) HotRemoveRoute(h hooks.Hook) error { return r.deregisterRouteForHook(h) } -func routeKey(verb coap.COAPCode, owner, hookName string) string { +func routeKey(verb codes.Code, owner, hookName string) string { key := fmt.Sprintf("%d-%s/%s", verb, owner, hookName) fmt.Printf("route key generated: %s\n", key) @@ -68,7 +68,7 @@ func (r *routeTable) registerRouteForHook(h hooks.Hook) error { r.Lock() defer r.Unlock() - verb := coap.POST // this part can later be extended to allow the registration of different verbs + verb := codes.POST // this part can later be extended to allow the registration of different verbs key := routeKey(verb, h.Owner, h.Name) @@ -85,7 +85,7 @@ func (r *routeTable) deregisterRouteForHook(h hooks.Hook) error { r.Lock() defer r.Unlock() - verb := coap.POST // this part can later be extended to allow the registration of different verbs + verb := codes.POST // this part can later be extended to allow the registration of different verbs key := routeKey(verb, h.Owner, h.Name) diff --git a/go.mod b/go.mod index 788cbef..22b4e6d 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,8 @@ module github.com/coapcloud/coap-hooks-router go 1.13 require ( - github.com/coapcloud/go-coap v0.0.0-20190923040847-c7ef78cd84df + github.com/coapcloud/go-coap v0.1.1 github.com/derekparker/trie v0.0.0-20200317170641-1fdf38b7b0e9 - github.com/go-ocf/go-coap v0.0.0-20200406073902-cf923db524db // indirect github.com/joho/godotenv v1.3.0 github.com/labstack/echo/v4 v4.1.16 github.com/pion/dtls v1.5.4 // indirect diff --git a/go.sum b/go.sum index fba2d23..46e85f4 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,8 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYU github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/coapcloud/go-coap v0.0.0-20190923040847-c7ef78cd84df h1:HBYTFGnoYOhg1a365JTZfqIPI49mpSGfR/ib/58lQdQ= github.com/coapcloud/go-coap v0.0.0-20190923040847-c7ef78cd84df/go.mod h1:mp7wB/7I0+hzZx6QgoUZyjMErTWVv8Kjrwk+ZnNXri8= +github.com/coapcloud/go-coap v0.1.1 h1:xmAFX3d0BcPv/Qjm3jHmeoaUOdocQW2z+/WbBP1ZXNg= +github.com/coapcloud/go-coap v0.1.1/go.mod h1:LDN/oanDUGZ8gRORYbxoSzXvzgnPjFdrh03Q54P0Koo= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -16,6 +18,8 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-ocf/go-coap v0.0.0-20200406073902-cf923db524db h1:zYs9RIr1Ghp2POmmwWl1nmgCSrInPRqRhM1fPFqG0UY= github.com/go-ocf/go-coap v0.0.0-20200406073902-cf923db524db/go.mod h1:QoCnsMBbmLW/XqYJj5reW4Mm7QxIz8QTycLGqWczpZA= +github.com/go-ocf/go-coap v0.0.0-20200410132222-418332224ecf h1:8YTWo9pmtJSf8R0DePBYXb9Fk1fMJOFslQsLvlgg3hA= +github.com/go-ocf/go-coap v0.0.0-20200410132222-418332224ecf/go.mod h1:QoCnsMBbmLW/XqYJj5reW4Mm7QxIz8QTycLGqWczpZA= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= diff --git a/hooks/api.go b/hooks/api.go index 256bb21..d5dc73e 100644 --- a/hooks/api.go +++ b/hooks/api.go @@ -38,14 +38,13 @@ func ListenAndServe(hooksRepo Repository, port int) { // Middleware e.Use(middleware.Logger()) e.Use(middleware.Recover()) - e.Use(middleware.AddTrailingSlash()) hooksGroup := e.Group("/api/hooks") hooksGroup.Use(middleware.KeyAuth(func(key string, c echo.Context) (bool, error) { return subtle.ConstantTimeCompare([]byte(key), config.AdminBearer) == 1, nil })) - hooksGroup.POST("/", func(c echo.Context) error { + hooksGroup.POST("", func(c echo.Context) error { var reqHook Hook err := c.Bind(&reqHook) if err != nil { @@ -60,7 +59,7 @@ func ListenAndServe(hooksRepo Repository, port int) { return c.JSON(http.StatusOK, reqHook) }) - hooksGroup.GET("/", func(c echo.Context) error { + hooksGroup.GET("", func(c echo.Context) error { hooks, err := s.store.ListAllHooks() if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()}) diff --git a/hooks/hooks.go b/hooks/hooks.go index 8dfac58..f6a94c7 100644 --- a/hooks/hooks.go +++ b/hooks/hooks.go @@ -58,7 +58,7 @@ const ( type HookAPIEvent struct { EventType EventType - Hook *Hook + Hooks []*Hook } type repo struct { @@ -92,8 +92,8 @@ func (r *repo) CreateHook(h Hook) (err error) { } r.events <- HookAPIEvent{ - EventTypeCreate, - &h, + EventType: EventTypeCreate, + Hooks: []*Hook{&h}, } } else { e := tx.Rollback() @@ -163,6 +163,8 @@ func (r *repo) DeleteHooksForOwner(owner string) (err error) { return err } + var hooks []*Hook + defer func() { if err == nil { e := tx.Commit() @@ -172,6 +174,7 @@ func (r *repo) DeleteHooksForOwner(owner string) (err error) { r.events <- HookAPIEvent{ EventType: EventTypeDeleteForOwner, + Hooks: hooks, } } else { e := tx.Rollback() @@ -181,6 +184,11 @@ func (r *repo) DeleteHooksForOwner(owner string) (err error) { } }() + hooks, err = r.ListHooksForOwner(owner) + if err != nil { + return err + } + err = tx.DeleteBucket([]byte(owner)) if err != nil { return err @@ -197,7 +205,7 @@ func (r *repo) DeleteHookByOwnerAndName(owner, name string) (err error) { return err } - var h *Hook + var h Hook defer func() { if err == nil { @@ -207,8 +215,8 @@ func (r *repo) DeleteHookByOwnerAndName(owner, name string) (err error) { } r.events <- HookAPIEvent{ - EventTypeDelete, - h, + EventType: EventTypeDelete, + Hooks: []*Hook{&h}, } } else { e := tx.Rollback() @@ -230,7 +238,7 @@ func (r *repo) DeleteHookByOwnerAndName(owner, name string) (err error) { return fmt.Errorf("could not find hook: %s to delete for owner: %s", name, owner) } - h = &Hook{ + h = Hook{ Owner: owner, Name: name, Destination: string(v), diff --git a/main.go b/main.go index 5316510..059bed6 100644 --- a/main.go +++ b/main.go @@ -4,7 +4,6 @@ import ( "log" "github.com/coapcloud/coap-hooks-router/coap" - "github.com/coapcloud/coap-hooks-router/config" hooksAPI "github.com/coapcloud/coap-hooks-router/hooks" ) @@ -14,7 +13,7 @@ const ( ) func main() { - hooksRepo, err := hooksAPI.NewHooksRepository(config.DBFilename) + hooksRepo, err := hooksAPI.NewHooksRepository("dbdata/hooks.db") if err != nil { log.Fatalf("Error trying to create HooksRepository: %v\n", err) }