Merge pull request #70 from stefanprodan/redis-cache-api

Add cache CRUD API
This commit is contained in:
Stefan Prodan
2020-05-20 13:15:03 +03:00
committed by GitHub
7 changed files with 171 additions and 81 deletions
+3 -2
View File
@@ -41,8 +41,9 @@ Web API:
* `POST /token` issues a JWT token valid for one minute `JWT=$(curl -sd 'anon' podinfo:9898/token | jq -r .token)`
* `GET /token/validate` validates the JWT token `curl -H "Authorization: Bearer $JWT" podinfo:9898/token/validate`
* `GET /configs` returns a JSON with configmaps and/or secrets mounted in the `config` volume
* `POST /cache` saves the posted content to Redis and returns the SHA1 hash of the content
* `GET /cache/{hash}` returns the content from Redis if the key exists
* `POST/PUT /cache/{key}` saves the posted content to Redis
* `GET /cache/{key}` returns the content from Redis if the key exists
* `DELETE /cache/{key}` deletes the key from Redis if exists
* `POST /store` writes the posted content to disk at /data/hash and returns the SHA1 hash of the content
* `GET /store/{hash}` returns the content of the file /data/hash if exists
* `GET /ws/echo` echos content via websockets `podcli ws ws://localhost:9898/ws/echo`
+32
View File
@@ -0,0 +1,32 @@
{{- if .Values.cache }}
apiVersion: v1
kind: Pod
metadata:
name: {{ template "podinfo.fullname" . }}-cache-test-{{ randAlphaNum 5 | lower }}
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "podinfo.name" . }}
annotations:
"helm.sh/hook": test
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
sidecar.istio.io/inject: "false"
linkerd.io/inject: disabled
appmesh.k8s.aws/sidecarInjectorWebhook: disabled
spec:
containers:
- name: curl
image: curlimages/curl:7.69.0
command:
- sh
- -c
- |
curl -sd 'data' ${PODINFO_SVC}/cache/test &&
curl -s ${PODINFO_SVC}/cache/test | grep data &&
curl -s -XDELETE ${PODINFO_SVC}/cache/test
env:
- name: PODINFO_SVC
value: "{{ template "podinfo.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.service.externalPort }}"
restartPolicy: Never
{{- end }}
+50 -18
View File
@@ -1,45 +1,74 @@
package api
import (
"github.com/gomodule/redigo/redis"
"github.com/gorilla/mux"
"go.uber.org/zap"
"io/ioutil"
"net/http"
"time"
"github.com/gomodule/redigo/redis"
"github.com/gorilla/mux"
"go.uber.org/zap"
)
// Cache godoc
// @Summary Save payload in cache
// @Description writes the posted content in cache and returns the SHA1 hash of the content
// @Description writes the posted content in cache
// @Tags HTTP API
// @Accept json
// @Produce json
// @Router /cache [post]
// @Success 200 {object} api.MapResponse
// @Router /cache/{key} [post]
// @Success 202
func (s *Server) cacheWriteHandler(w http.ResponseWriter, r *http.Request) {
if s.pool == nil {
s.ErrorResponse(w, r, "cache server is offline", http.StatusBadRequest)
return
}
key := mux.Vars(r)["key"]
body, err := ioutil.ReadAll(r.Body)
if err != nil {
s.ErrorResponse(w, r, "reading the request body failed", http.StatusBadRequest)
return
}
hash := hash(string(body))
conn := s.pool.Get()
defer conn.Close()
_, err = conn.Do("SET", hash, string(body))
_, err = conn.Do("SET", key, string(body))
if err != nil {
s.logger.Warn("cache set failed", zap.Error(err))
s.ErrorResponse(w, r, "cache set failed", http.StatusInternalServerError)
return
}
s.JSONResponseCode(w, r, map[string]string{"hash": hash}, http.StatusAccepted)
w.WriteHeader(http.StatusAccepted)
}
// Cache godoc
// @Summary Delete payload from cache
// @Description deletes the key and its value from cache
// @Tags HTTP API
// @Accept json
// @Produce json
// @Router /cache/{key} [delete]
// @Success 202
func (s *Server) cacheDeleteHandler(w http.ResponseWriter, r *http.Request) {
if s.pool == nil {
s.ErrorResponse(w, r, "cache server is offline", http.StatusBadRequest)
return
}
key := mux.Vars(r)["key"]
conn := s.pool.Get()
defer conn.Close()
_, err := conn.Do("DEL", key)
if err != nil {
s.logger.Warn("cache delete failed", zap.Error(err))
w.WriteHeader(http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusAccepted)
}
// Cache godoc
@@ -48,32 +77,35 @@ func (s *Server) cacheWriteHandler(w http.ResponseWriter, r *http.Request) {
// @Tags HTTP API
// @Accept json
// @Produce json
// @Router /cache/{hash} [get]
// @Success 200 {string} api.MapResponse
// @Router /cache/{key} [get]
// @Success 200 {string} string value
func (s *Server) cacheReadHandler(w http.ResponseWriter, r *http.Request) {
if s.pool == nil {
s.ErrorResponse(w, r, "cache server is offline", http.StatusBadRequest)
return
}
hash := mux.Vars(r)["hash"]
key := mux.Vars(r)["key"]
conn := s.pool.Get()
defer conn.Close()
ok, err := redis.Bool(conn.Do("EXISTS", hash))
ok, err := redis.Bool(conn.Do("EXISTS", key))
if err != nil || !ok {
s.ErrorResponse(w, r, "key not found in cache", http.StatusNotFound)
s.logger.Warn("cache key not found", zap.String("key", key))
w.WriteHeader(http.StatusNotFound)
return
}
data, err := redis.String(conn.Do("GET", hash))
data, err := redis.String(conn.Do("GET", key))
if err != nil {
s.logger.Warn("cache get failed", zap.Error(err))
s.ErrorResponse(w, r, "cache get failed", http.StatusInternalServerError)
w.WriteHeader(http.StatusInternalServerError)
return
}
s.JSONResponseCode(w, r, map[string]string{"data": data}, http.StatusAccepted)
w.WriteHeader(http.StatusOK)
w.Write([]byte(data))
}
func (s *Server) startCachePool() {
+34 -25
View File
@@ -1,6 +1,6 @@
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
// This file was generated by swaggo/swag at
// 2020-05-16 09:49:23.920068 +0300 EEST m=+0.052088436
// 2020-05-20 12:48:10.564627 +0300 EEST m=+0.030136350
package docs
@@ -98,30 +98,7 @@ var doc = `{
}
}
},
"/cache": {
"post": {
"description": "writes the posted content in cache and returns the SHA1 hash of the content",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Save payload in cache",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.MapResponse"
}
}
}
}
},
"/cache/{hash}": {
"/cache/{key}": {
"get": {
"description": "returns the content from cache if key exists",
"consumes": [
@@ -142,6 +119,38 @@ var doc = `{
}
}
}
},
"post": {
"description": "writes the posted content in cache",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Save payload in cache",
"responses": {
"202": {}
}
},
"delete": {
"description": "deletes the key and its value from cache",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Delete payload from cache",
"responses": {
"202": {}
}
}
},
"/chunked/{seconds}": {
+33 -24
View File
@@ -86,30 +86,7 @@
}
}
},
"/cache": {
"post": {
"description": "writes the posted content in cache and returns the SHA1 hash of the content",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Save payload in cache",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.MapResponse"
}
}
}
}
},
"/cache/{hash}": {
"/cache/{key}": {
"get": {
"description": "returns the content from cache if key exists",
"consumes": [
@@ -130,6 +107,38 @@
}
}
}
},
"post": {
"description": "writes the posted content in cache",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Save payload in cache",
"responses": {
"202": {}
}
},
"delete": {
"description": "deletes the key and its value from cache",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Delete payload from cache",
"responses": {
"202": {}
}
}
},
"/chunked/{seconds}": {
+16 -10
View File
@@ -102,23 +102,18 @@ paths:
summary: Runtime information
tags:
- HTTP API
/cache:
post:
/cache/{key}:
delete:
consumes:
- application/json
description: writes the posted content in cache and returns the SHA1 hash of
the content
description: deletes the key and its value from cache
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.MapResponse'
summary: Save payload in cache
"202": {}
summary: Delete payload from cache
tags:
- HTTP API
/cache/{hash}:
get:
consumes:
- application/json
@@ -133,6 +128,17 @@ paths:
summary: Get payload from cache
tags:
- HTTP API
post:
consumes:
- application/json
description: writes the posted content in cache
produces:
- application/json
responses:
"202": {}
summary: Save payload in cache
tags:
- HTTP API
/chunked/{seconds}:
get:
consumes:
+3 -2
View File
@@ -106,8 +106,9 @@ func (s *Server) registerHandlers() {
s.router.HandleFunc("/status/{code:[0-9]+}", s.statusHandler).Methods("GET", "POST", "PUT").Name("status")
s.router.HandleFunc("/store", s.storeWriteHandler).Methods("POST", "PUT")
s.router.HandleFunc("/store/{hash}", s.storeReadHandler).Methods("GET").Name("store")
s.router.HandleFunc("/cache", s.cacheWriteHandler).Methods("POST", "PUT")
s.router.HandleFunc("/cache/{hash}", s.cacheReadHandler).Methods("GET").Name("cache")
s.router.HandleFunc("/cache/{key}", s.cacheWriteHandler).Methods("POST", "PUT")
s.router.HandleFunc("/cache/{key}", s.cacheDeleteHandler).Methods("DELETE")
s.router.HandleFunc("/cache/{key}", s.cacheReadHandler).Methods("GET").Name("cache")
s.router.HandleFunc("/configs", s.configReadHandler).Methods("GET")
s.router.HandleFunc("/token", s.tokenGenerateHandler).Methods("POST")
s.router.HandleFunc("/token/validate", s.tokenValidateHandler).Methods("GET")