From 2b5273c5242b0795955a1ea1926d14d1af7139a8 Mon Sep 17 00:00:00 2001 From: Denis Zheleztsov Date: Thu, 1 Aug 2019 11:00:44 +0300 Subject: [PATCH] [#2]: Force data marshaling into JSON object New path: */v1/get/path/to/the/node+json* Method: *GET* Fixes #2 --- .travis.yml | 6 +-- Dockerfile | 14 ++++++- README.md | 83 +++++++++++++++++++++++++++++++++++------ build.sh | 32 +++++----------- rest/api.go | 104 ++++++++++++++++++++++++++++++++++++++++------------ 5 files changed, 177 insertions(+), 62 deletions(-) diff --git a/.travis.yml b/.travis.yml index ddfcba6..c2e2b96 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: go go: - - 1.6 - - 1.7 - - 1.8 + - 1.9 + - 1.11 + - 1.12 # tests script script: diff --git a/Dockerfile b/Dockerfile index cd5264b..d79bc31 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,18 @@ -FROM alpine +FROM golang:1.12 AS builder + +COPY . /go/src/github.com/Difrex/zoorest +WORKDIR /go/src/github.com/Difrex/zoorest + +ENV CGO_ENABLED=0 + +RUN go get -t -v ./... +RUN go build -o /zoorest . + +FROM alpine AS final MAINTAINER Denis Zheleztsov -ADD out/zoorest /bin/ +COPY --from=builder /zoorest /bin/zoorest RUN echo -ne "zoorest\nzoorest\n" | adduser zoorest diff --git a/README.md b/README.md index 8add2d0..63be116 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# Zoorest +# Zoorest [![Build Status](https://travis-ci.org/Difrex/zoorest.svg?branch=master)](https://travis-ci.org/Difrex/zoorest) -Zookeeper HTTP rest API - +Zookeeper REST API + **Table of Contents** - [Zoorest](#zoorest) @@ -11,14 +11,16 @@ Zookeeper HTTP rest API - [List node childrens](#list-node-childrens) - [Errors](#errors) - [Get node data](#get-node-data) - - [Errors](#errors) + - [Errors](#errors-1) + - [Get node data as JSON](#get-node-data-as-json) + - [Errors](#errors-2) - [Create node recursive](#create-node-recursive) - [Create node children](#create-node-children) - - [Errors](#errors) + - [Errors](#errors-3) - [Update node](#update-node) - - [Errors](#errors) + - [Errors](#errors-4) - [Delete node recursive](#delete-node-recursive) - - [Errors](#errors) + - [Errors](#errors-5) - [Build](#build) - [Binary](#binary) - [Docker build](#docker-build) @@ -124,6 +126,65 @@ echo eyJzb21lIjogImpzb24ifQ== | base64 -d } ``` +### Get node data as JSON + +Method: **GET** + +Location: **/v1/get/path/to/node+json** + +Simple add to the end of the path `+json` +Return JSON +``` +curl -s -XGET http://127.0.0.1:8889/v1/json/one/data+json | jq +{ + "path": "/json/one/data", + "state": "OK", + "error": "", + "zkstat": { + "Czxid": 45, + "Mzxid": 55, + "Ctime": 1564645641612, + "Mtime": 1564646317882, + "Version": 6, + "Cversion": 0, + "Aversion": 0, + "EphemeralOwner": 0, + "DataLength": 28, + "NumChildren": 0, + "Pzxid": 45 + }, + "data": { + "ok": true, + "some": "data" + } +} +``` + +#### Errors + +```json +curl -s -XGET http://127.0.0.1:8889/v1/get/invalid/json+json | jq +{ + "path": "/invalid/json", + "state": "OK", + "error": "JSON parsing failure: invalid character 'i' looking for beginning of value", + "zkstat": { + "Czxid": 45, + "Mzxid": 56, + "Ctime": 1564645641612, + "Mtime": 1564646350753, + "Version": 7, + "Cversion": 0, + "Aversion": 0, + "EphemeralOwner": 0, + "DataLength": 17, + "NumChildren": 0, + "Pzxid": 45 + }, + "data": null +} +``` + ### Create node recursive Method: **PUT** @@ -230,11 +291,11 @@ go build -compile gccgo #### Binary file -Build binary file +Build binary with the Docker ``` git clone https://github.com/Difrex/zoorest.git cd zoorest -./build.sh +./build.sh docker-binary ``` Result binary file will be placed in out/ dir @@ -244,7 +305,7 @@ Build Alpine based docker image ``` git clone https://github.com/Difrex/zoorest.git cd zoorest -./build.sh alpine +./build.sh docker ``` Image will be tagged as zoorest:latest @@ -265,7 +326,7 @@ And run it Denis Zheleztsov -# LICENSE +# LICENSE GPLv3 see [LICENSE](LICENSE) diff --git a/build.sh b/build.sh index 2692acd..728cc43 100755 --- a/build.sh +++ b/build.sh @@ -1,29 +1,15 @@ #!/bin/bash -cat > Dockerfile.builder < - -ENV GOPATH /usr - -RUN go get github.com/Difrex/zoorest/rest -RUN cd /usr/src/github.com/Difrex/zoorest && go get -t -v ./... - -WORKDIR /usr/src/github.com/Difrex/zoorest - -ENTRYPOINT go build -ldflags "-linkmode external -extldflags -static" && mv zoorest /out -EOF - -# Build builder -docker build --no-cache -t zoorest_builder -f Dockerfile.builder . -# Build bin -docker run -v $(pwd)/out:/out zoorest_builder - -case $1 in alpine) +case $1 in docker) docker build -t zoorest -f Dockerfile . ;; + binary) + go build -o zoorest . + ;; + docker-binary) + docker build --rm -t zoorest -f Dockerfile . + docker run --rm -u root -v $(pwd)/out:/tmp/out --entrypoint '/bin/cp' zoorest /bin/zoorest /tmp/out + ;; *) - ;; + ;; esac - diff --git a/rest/api.go b/rest/api.go index 7e6db21..e08e9e7 100644 --- a/rest/api.go +++ b/rest/api.go @@ -2,13 +2,14 @@ package rest import ( "encoding/json" - "github.com/gorilla/mux" - "github.com/samuel/go-zookeeper/zk" "io/ioutil" "log" "net/http" "strings" "time" + + "github.com/gorilla/mux" + "github.com/samuel/go-zookeeper/zk" ) // Ls ... @@ -29,14 +30,16 @@ type Get struct { Data []byte `json:"data"` } +type GetJSON struct { + Path string `json:"path"` + State string `json:"state"` + Error string `json:"error"` + ZkStat *zk.Stat `json:"zkstat"` + Data interface{} `json:"data"` +} + // LS ... func (zk ZooNode) LS(w http.ResponseWriter, r *http.Request) { - if r.Method != "GET" { - e := strings.Join([]string{"Method", r.Method, "not alowed"}, " ") - w.WriteHeader(500) - w.Write([]byte(e)) - return - } vars := mux.Vars(r) path := vars["path"] @@ -108,12 +111,6 @@ func (zk ZooNode) UP(w http.ResponseWriter, r *http.Request) { // RM ... func (zk ZooNode) RM(w http.ResponseWriter, r *http.Request) { - if r.Method != "DELETE" { - e := strings.Join([]string{"Method", r.Method, "not alowed"}, " ") - w.WriteHeader(500) - w.Write([]byte(e)) - return - } vars := mux.Vars(r) path := vars["path"] @@ -139,12 +136,33 @@ func (zk ZooNode) RM(w http.ResponseWriter, r *http.Request) { // GET ... func (zk ZooNode) GET(w http.ResponseWriter, r *http.Request) { - if r.Method != "GET" { - e := strings.Join([]string{"Method", r.Method, "not alowed"}, " ") + vars := mux.Vars(r) + path := vars["path"] + + ch := make(chan Get) + + go func() { ch <- zk.GetNode(path) }() + + childrens := <-ch + data, err := json.Marshal(childrens) + if err != nil { w.WriteHeader(500) - w.Write([]byte(e)) + w.Write([]byte("JSON parsing failure")) return } + + if childrens.Error != "" { + w.WriteHeader(500) + w.Write(data) + return + } + + w.WriteHeader(200) + w.Write(data) +} + +// GetJSON marshals the node data to the JSON +func (zk ZooNode) GetJSON(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) path := vars["path"] @@ -166,18 +184,39 @@ func (zk ZooNode) GET(w http.ResponseWriter, r *http.Request) { return } - w.WriteHeader(200) - w.Write(data) + dataJson, err := unmarhalNodeData(childrens.Data) + if err != nil { + w.WriteHeader(500) + childrens.Error = "JSON parsing failure: " + err.Error() + w.Write(errorUnmarshal(childrens)) + return + } + + getJson := GetJSON{ + Path: childrens.Path, + Error: childrens.Error, + State: childrens.State, + ZkStat: childrens.ZkStat, + Data: dataJson, + } + if data, err := json.Marshal(getJson); err != nil { + w.WriteHeader(500) + w.Write([]byte(err.Error())) + } else { + w.WriteHeader(200) + w.Write(data) + } } // Serve ... func Serve(listen string, zk ZooNode) { r := mux.NewRouter() - r.HandleFunc("/v1/ls{path:[A-Za-z0-9-_/.:]+}", zk.LS) - r.HandleFunc("/v1/get{path:[A-Za-z0-9-_/.:]+}", zk.GET) - r.HandleFunc("/v1/rmr{path:[A-Za-z0-9-_/.:]+}", zk.RM) - r.HandleFunc("/v1/up{path:[A-Za-z0-9-_/.:]+}", zk.UP) + r.HandleFunc("/v1/ls{path:[A-Za-z0-9-_/.:]+}", zk.LS).Methods("GET") + r.HandleFunc("/v1/get{path:[A-Za-z0-9-_/.:]+}", zk.GET).Methods("GET") + r.HandleFunc("/v1/get{path:[A-Za-z0-9-_/.:]+}+json", zk.GetJSON).Methods("GET") + r.HandleFunc("/v1/rmr{path:[A-Za-z0-9-_/.:]+}", zk.RM).Methods("DELETE") + r.HandleFunc("/v1/up{path:[A-Za-z0-9-_/.:]+}", zk.UP).Methods("PUT", "POST", "PATCH") http.Handle("/", r) @@ -191,3 +230,22 @@ func Serve(listen string, zk ZooNode) { log.Print("Listening API on ", listen) log.Fatal(srv.ListenAndServe()) } + +func unmarhalNodeData(data []byte) (interface{}, error) { + var content interface{} + err := json.Unmarshal(data, &content) + if err != nil { + return nil, err + } + + return content, nil +} + +func errorUnmarshal(get Get) []byte { + get.Data = nil + data, err := json.Marshal(get) + if err != nil { + return nil + } + return data +}