From 73b658d7118e8995dcc621d9a55000c6ff038fa9 Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Sat, 16 May 2020 09:53:17 +0300 Subject: [PATCH] Add cache API - implement cache with Redis - add cache-server to args and config - add Redis deployment to webapp overlays --- README.md | 2 + charts/podinfo/README.md | 1 + charts/podinfo/templates/deployment.yaml | 3 + charts/podinfo/values.yaml | 1 + cmd/podinfo/main.go | 1 + deploy/bases/backend/deployment.yaml | 1 + deploy/bases/cache/deployment.yaml | 57 +++++++++++ deploy/bases/cache/kustomization.yaml | 9 ++ deploy/bases/cache/redis.conf | 4 + deploy/bases/cache/service.yaml | 13 +++ deploy/bases/frontend/deployment.yaml | 1 + deploy/overlays/dev/kustomization.yaml | 1 + deploy/overlays/production/kustomization.yaml | 1 + deploy/overlays/staging/kustomization.yaml | 1 + go.mod | 7 +- go.sum | 38 +++++++- pkg/api/cache.go | 93 ++++++++++++++++++ pkg/api/docs/docs.go | 96 ++++++++++++++----- pkg/api/docs/swagger.json | 73 +++++++++++--- pkg/api/docs/swagger.yaml | 54 ++++++++--- pkg/api/server.go | 41 ++++++-- 21 files changed, 435 insertions(+), 63 deletions(-) create mode 100644 deploy/bases/cache/deployment.yaml create mode 100644 deploy/bases/cache/kustomization.yaml create mode 100644 deploy/bases/cache/redis.conf create mode 100644 deploy/bases/cache/service.yaml create mode 100644 pkg/api/cache.go diff --git a/README.md b/README.md index 0a26abb..8cdd9de 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ 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 /store/{hash}` returns the content from Redis if the key 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` diff --git a/charts/podinfo/README.md b/charts/podinfo/README.md index 6e6090d..674e5f3 100644 --- a/charts/podinfo/README.md +++ b/charts/podinfo/README.md @@ -35,6 +35,7 @@ Parameter | Default | Description `logLevel` | `info` | Log level: `debug`, `info`, `warn`, `error`, `flat` or `panic` `backend` | `None` | Echo backend URL `backends` | `[]` | Array of echo backend URLs +`cache` | `None` | Redis address in the format `:` `ui.color` | `#34577c` | UI color `ui.message` | `None` | UI greetings message `ui.logo` | `None` | UI logo diff --git a/charts/podinfo/templates/deployment.yaml b/charts/podinfo/templates/deployment.yaml index f529198..37dd23a 100644 --- a/charts/podinfo/templates/deployment.yaml +++ b/charts/podinfo/templates/deployment.yaml @@ -49,6 +49,9 @@ spec: {{- range .Values.backends }} - --backend-url={{ . }} {{- end }} + {{- if .Values.cache }} + - --cache-server={{ .Values.cache }} + {{- end }} - --level={{ .Values.logLevel }} - --random-delay={{ .Values.faults.delay }} - --random-error={{ .Values.faults.error }} diff --git a/charts/podinfo/values.yaml b/charts/podinfo/values.yaml index e3af618..fea8389 100644 --- a/charts/podinfo/values.yaml +++ b/charts/podinfo/values.yaml @@ -4,6 +4,7 @@ replicaCount: 1 logLevel: info backend: #http://backend-podinfo:9898/echo backends: [] +cache: "" ui: color: "#34577c" diff --git a/cmd/podinfo/main.go b/cmd/podinfo/main.go index 5ed5c6e..4ee920e 100644 --- a/cmd/podinfo/main.go +++ b/cmd/podinfo/main.go @@ -46,6 +46,7 @@ func main() { fs.Bool("unready", false, "when set, ready state is never reached") fs.Int("stress-cpu", 0, "number of CPU cores with 100 load") fs.Int("stress-memory", 0, "MB of data to load into memory") + fs.String("cache-server", "", "Redis address in the format :") versionFlag := fs.BoolP("version", "v", false, "get version number") diff --git a/deploy/bases/backend/deployment.yaml b/deploy/bases/backend/deployment.yaml index 7bc89e3..48340fd 100644 --- a/deploy/bases/backend/deployment.yaml +++ b/deploy/bases/backend/deployment.yaml @@ -42,6 +42,7 @@ spec: - --grpc-port=9999 - --grpc-service-name=backend - --level=info + - --cache-server=cache:6379 env: - name: PODINFO_UI_COLOR value: "#34577c" diff --git a/deploy/bases/cache/deployment.yaml b/deploy/bases/cache/deployment.yaml new file mode 100644 index 0000000..3ab5429 --- /dev/null +++ b/deploy/bases/cache/deployment.yaml @@ -0,0 +1,57 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: cache +spec: + selector: + matchLabels: + app: cache + template: + metadata: + labels: + app: cache + spec: + containers: + - name: redis + image: redis:6.0.1 + imagePullPolicy: IfNotPresent + command: + - redis-server + - "/redis-master/redis.conf" + ports: + - name: redis + containerPort: 6379 + protocol: TCP + livenessProbe: + tcpSocket: + port: redis + initialDelaySeconds: 5 + timeoutSeconds: 5 + readinessProbe: + exec: + command: + - redis-cli + - ping + initialDelaySeconds: 5 + timeoutSeconds: 5 + resources: + limits: + cpu: 1000m + memory: 128Mi + requests: + cpu: 100m + memory: 32Mi + volumeMounts: + - mountPath: /var/lib/redis + name: data + - mountPath: /redis-master + name: config + volumes: + - name: data + emptyDir: {} + - name: config + configMap: + name: redis-config + items: + - key: redis.conf + path: redis.conf \ No newline at end of file diff --git a/deploy/bases/cache/kustomization.yaml b/deploy/bases/cache/kustomization.yaml new file mode 100644 index 0000000..27169de --- /dev/null +++ b/deploy/bases/cache/kustomization.yaml @@ -0,0 +1,9 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - service.yaml + - deployment.yaml +configMapGenerator: + - name: redis-config + files: + - redis.conf diff --git a/deploy/bases/cache/redis.conf b/deploy/bases/cache/redis.conf new file mode 100644 index 0000000..4de7850 --- /dev/null +++ b/deploy/bases/cache/redis.conf @@ -0,0 +1,4 @@ +maxmemory 64mb +maxmemory-policy allkeys-lru +save "" +appendonly no diff --git a/deploy/bases/cache/service.yaml b/deploy/bases/cache/service.yaml new file mode 100644 index 0000000..6f9f67d --- /dev/null +++ b/deploy/bases/cache/service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: cache +spec: + type: ClusterIP + selector: + app: cache + ports: + - name: redis + port: 6379 + protocol: TCP + targetPort: redis diff --git a/deploy/bases/frontend/deployment.yaml b/deploy/bases/frontend/deployment.yaml index ac6d765..55a7900 100644 --- a/deploy/bases/frontend/deployment.yaml +++ b/deploy/bases/frontend/deployment.yaml @@ -41,6 +41,7 @@ spec: - --port-metrics=9797 - --level=info - --backend-url=http://backend:9898/echo + - --cache-server=cache:6379 env: - name: PODINFO_UI_COLOR value: "#34577c" diff --git a/deploy/overlays/dev/kustomization.yaml b/deploy/overlays/dev/kustomization.yaml index 901c948..4298d7c 100644 --- a/deploy/overlays/dev/kustomization.yaml +++ b/deploy/overlays/dev/kustomization.yaml @@ -4,6 +4,7 @@ namespace: dev resources: - ../../bases/backend - ../../bases/frontend + - ../../bases/cache - namespace.yaml transformers: - labels.yaml diff --git a/deploy/overlays/production/kustomization.yaml b/deploy/overlays/production/kustomization.yaml index 0f60ecd..0097d07 100644 --- a/deploy/overlays/production/kustomization.yaml +++ b/deploy/overlays/production/kustomization.yaml @@ -4,6 +4,7 @@ namespace: production resources: - ../../bases/backend - ../../bases/frontend + - ../../bases/cache - namespace.yaml transformers: - labels.yaml diff --git a/deploy/overlays/staging/kustomization.yaml b/deploy/overlays/staging/kustomization.yaml index f0d489e..3627a51 100644 --- a/deploy/overlays/staging/kustomization.yaml +++ b/deploy/overlays/staging/kustomization.yaml @@ -4,6 +4,7 @@ namespace: staging resources: - ../../bases/backend - ../../bases/frontend + - ../../bases/cache - namespace.yaml transformers: - labels.yaml diff --git a/go.mod b/go.mod index 0f1a7e3..afe0d8a 100644 --- a/go.mod +++ b/go.mod @@ -10,17 +10,18 @@ require ( github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/fatih/color v1.7.0 github.com/fsnotify/fsnotify v1.4.7 - github.com/go-chi/chi v4.0.3+incompatible // indirect + github.com/go-chi/chi v4.1.1+incompatible // indirect + github.com/gomodule/redigo v1.8.1 github.com/gorilla/mux v1.7.4 github.com/gorilla/websocket v1.4.2 github.com/hashicorp/go-getter v1.4.1 github.com/prometheus/client_golang v1.5.1 - github.com/spf13/cobra v0.0.6 + github.com/spf13/cobra v1.0.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.6.2 github.com/swaggo/http-swagger v0.0.0-20190614090009-c2865af9083e github.com/swaggo/swag v1.6.5 - go.uber.org/zap v1.10.0 + go.uber.org/zap v1.15.0 golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 google.golang.org/grpc v1.23.0 ) diff --git a/go.sum b/go.sum index f2db41d..e994966 100644 --- a/go.sum +++ b/go.sum @@ -52,6 +52,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 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= @@ -71,8 +72,8 @@ github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NB github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= -github.com/go-chi/chi v4.0.3+incompatible h1:gakN3pDJnzZN5jqFV2TEdF66rTfKeITyR8qu6ekICEY= -github.com/go-chi/chi v4.0.3+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/go-chi/chi v4.1.1+incompatible h1:MmTgB0R8Bt/jccxp+t6S/1VGIKdJw5J74CK/c9tTfA4= +github.com/go-chi/chi v4.1.1+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -111,6 +112,8 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/gomodule/redigo v1.8.1 h1:Abmo0bI7Xf0IhdIPc7HZQzZcShdnmxeoVuDDtIQp8N8= +github.com/gomodule/redigo v1.8.1/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -123,6 +126,7 @@ github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPg github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -233,8 +237,11 @@ github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLk github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -248,8 +255,8 @@ github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs= -github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= @@ -268,6 +275,8 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 h1:PyYN9JH5jY9j6av01SpfRMb+1DWg/i3MbGOKPxJ2wjM= @@ -287,6 +296,7 @@ github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= @@ -296,12 +306,21 @@ go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -312,7 +331,10 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -385,8 +407,13 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59 h1:QjA/9ArTfVTLfEhClDCG7SGrZkZixxWpwNCDiwJfh88= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0 h1:Dh6fw+p6FyRl5x/FvNswO1ji0lIGzm3KP8Y9VkS9PTE= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -421,6 +448,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= @@ -437,4 +465,6 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/pkg/api/cache.go b/pkg/api/cache.go new file mode 100644 index 0000000..298c604 --- /dev/null +++ b/pkg/api/cache.go @@ -0,0 +1,93 @@ +package api + +import ( + "github.com/gomodule/redigo/redis" + "github.com/gorilla/mux" + "go.uber.org/zap" + "io/ioutil" + "net/http" + "time" +) + +// Cache godoc +// @Summary Save payload in cache +// @Description writes the posted content in cache and returns the SHA1 hash of the content +// @Tags HTTP API +// @Accept json +// @Produce json +// @Router /cache [post] +// @Success 200 {object} api.MapResponse +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 + } + + 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)) + 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) +} + +// Cache godoc +// @Summary Get payload from cache +// @Description returns the content from cache if key exists +// @Tags HTTP API +// @Accept json +// @Produce json +// @Router /cache/{hash} [get] +// @Success 200 {string} api.MapResponse +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"] + conn := s.pool.Get() + defer conn.Close() + + ok, err := redis.Bool(conn.Do("EXISTS", hash)) + if err != nil || !ok { + s.ErrorResponse(w, r, "key not found in cache", http.StatusNotFound) + return + } + + data, err := redis.String(conn.Do("GET", hash)) + if err != nil { + s.logger.Warn("cache get failed", zap.Error(err)) + s.ErrorResponse(w, r, "cache get failed", http.StatusInternalServerError) + return + } + + s.JSONResponseCode(w, r, map[string]string{"data": data}, http.StatusAccepted) +} + +func (s *Server) startCachePool() { + if s.config.CacheServer != "" { + s.pool = &redis.Pool{ + MaxIdle: 3, + IdleTimeout: 240 * time.Second, + Dial: func() (redis.Conn, error) { + return redis.Dial("tcp", s.config.CacheServer) + }, + TestOnBorrow: func(c redis.Conn, t time.Time) error { + _, err := c.Do("PING") + return err + }, + } + } +} diff --git a/pkg/api/docs/docs.go b/pkg/api/docs/docs.go index 457f423..e27dbcf 100644 --- a/pkg/api/docs/docs.go +++ b/pkg/api/docs/docs.go @@ -1,12 +1,13 @@ // GENERATED BY THE COMMAND ABOVE; DO NOT EDIT // This file was generated by swaggo/swag at -// 2019-08-07 15:52:23.918713 +0300 EEST m=+0.023438272 +// 2020-05-16 09:49:23.920068 +0300 EEST m=+0.052088436 package docs import ( "bytes" "encoding/json" + "strings" "github.com/alecthomas/template" "github.com/swaggo/swag" @@ -16,8 +17,8 @@ var doc = `{ "schemes": {{ marshal .Schemes }}, "swagger": "2.0", "info": { - "description": "Go microservice template for Kubernetes.", - "title": "Podinfo API", + "description": "{{.Description}}", + "title": "{{.Title}}", "contact": { "name": "Source Code", "url": "https://github.com/stefanprodan/podinfo" @@ -26,10 +27,10 @@ var doc = `{ "name": "MIT License", "url": "https://github.com/stefanprodan/podinfo/blob/master/LICENSE" }, - "version": "2.0" + "version": "{{.Version}}" }, - "host": "localhost:9898", - "basePath": "/", + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", "paths": { "/": { "get": { @@ -68,7 +69,6 @@ var doc = `{ "202": { "description": "Accepted", "schema": { - "type": "object", "$ref": "#/definitions/api.MapResponse" } } @@ -92,13 +92,58 @@ var doc = `{ "200": { "description": "OK", "schema": { - "type": "object", "$ref": "#/definitions/api.RuntimeResponse" } } } } }, + "/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}": { + "get": { + "description": "returns the content from cache if key exists", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "HTTP API" + ], + "summary": "Get payload from cache", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, "/chunked/{seconds}": { "get": { "description": "uses transfer-encoding type chunked to give a partial response and then waits for the specified period", @@ -116,7 +161,6 @@ var doc = `{ "200": { "description": "OK", "schema": { - "type": "object", "$ref": "#/definitions/api.MapResponse" } } @@ -140,7 +184,6 @@ var doc = `{ "200": { "description": "OK", "schema": { - "type": "object", "$ref": "#/definitions/api.MapResponse" } } @@ -164,7 +207,6 @@ var doc = `{ "200": { "description": "OK", "schema": { - "type": "object", "$ref": "#/definitions/api.ArrayResponse" } } @@ -188,7 +230,6 @@ var doc = `{ "200": { "description": "OK", "schema": { - "type": "object", "$ref": "#/definitions/api.ArrayResponse" } } @@ -333,7 +374,6 @@ var doc = `{ "200": { "description": "OK", "schema": { - "type": "object", "$ref": "#/definitions/api.MapResponse" } } @@ -357,7 +397,6 @@ var doc = `{ "200": { "description": "OK", "schema": { - "type": "object", "$ref": "#/definitions/api.MapResponse" } } @@ -404,7 +443,6 @@ var doc = `{ "200": { "description": "OK", "schema": { - "type": "object", "$ref": "#/definitions/api.TokenResponse" } } @@ -428,7 +466,6 @@ var doc = `{ "200": { "description": "OK", "schema": { - "type": "object", "$ref": "#/definitions/api.TokenValidationResponse" } }, @@ -455,7 +492,6 @@ var doc = `{ "200": { "description": "OK", "schema": { - "type": "object", "$ref": "#/definitions/api.MapResponse" } } @@ -479,7 +515,6 @@ var doc = `{ "202": { "description": "Accepted", "schema": { - "type": "object", "$ref": "#/definitions/api.MapResponse" } } @@ -490,11 +525,15 @@ var doc = `{ "definitions": { "api.ArrayResponse": { "type": "array", - "items": {} + "items": { + "type": "string" + } }, "api.MapResponse": { "type": "object", - "additionalProperties": {} + "additionalProperties": { + "type": "string" + } }, "api.RuntimeResponse": { "type": "object", @@ -511,6 +550,9 @@ var doc = `{ "hostname": { "type": "string" }, + "logo": { + "type": "string" + }, "message": { "type": "string" }, @@ -566,11 +608,21 @@ type swaggerInfo struct { } // SwaggerInfo holds exported Swagger Info so clients can modify it -var SwaggerInfo = swaggerInfo{Schemes: []string{"http", "https"}} +var SwaggerInfo = swaggerInfo{ + Version: "2.0", + Host: "localhost:9898", + BasePath: "/", + Schemes: []string{"http", "https"}, + Title: "Podinfo API", + Description: "Go microservice template for Kubernetes.", +} type s struct{} func (s *s) ReadDoc() string { + sInfo := SwaggerInfo + sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1) + t, err := template.New("swagger_info").Funcs(template.FuncMap{ "marshal": func(v interface{}) string { a, _ := json.Marshal(v) @@ -582,7 +634,7 @@ func (s *s) ReadDoc() string { } var tpl bytes.Buffer - if err := t.Execute(&tpl, SwaggerInfo); err != nil { + if err := t.Execute(&tpl, sInfo); err != nil { return doc } diff --git a/pkg/api/docs/swagger.json b/pkg/api/docs/swagger.json index 56f23ed..dd444d0 100644 --- a/pkg/api/docs/swagger.json +++ b/pkg/api/docs/swagger.json @@ -1,4 +1,8 @@ { + "schemes": [ + "http", + "https" + ], "swagger": "2.0", "info": { "description": "Go microservice template for Kubernetes.", @@ -53,7 +57,6 @@ "202": { "description": "Accepted", "schema": { - "type": "object", "$ref": "#/definitions/api.MapResponse" } } @@ -77,13 +80,58 @@ "200": { "description": "OK", "schema": { - "type": "object", "$ref": "#/definitions/api.RuntimeResponse" } } } } }, + "/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}": { + "get": { + "description": "returns the content from cache if key exists", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "HTTP API" + ], + "summary": "Get payload from cache", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, "/chunked/{seconds}": { "get": { "description": "uses transfer-encoding type chunked to give a partial response and then waits for the specified period", @@ -101,7 +149,6 @@ "200": { "description": "OK", "schema": { - "type": "object", "$ref": "#/definitions/api.MapResponse" } } @@ -125,7 +172,6 @@ "200": { "description": "OK", "schema": { - "type": "object", "$ref": "#/definitions/api.MapResponse" } } @@ -149,7 +195,6 @@ "200": { "description": "OK", "schema": { - "type": "object", "$ref": "#/definitions/api.ArrayResponse" } } @@ -173,7 +218,6 @@ "200": { "description": "OK", "schema": { - "type": "object", "$ref": "#/definitions/api.ArrayResponse" } } @@ -318,7 +362,6 @@ "200": { "description": "OK", "schema": { - "type": "object", "$ref": "#/definitions/api.MapResponse" } } @@ -342,7 +385,6 @@ "200": { "description": "OK", "schema": { - "type": "object", "$ref": "#/definitions/api.MapResponse" } } @@ -389,7 +431,6 @@ "200": { "description": "OK", "schema": { - "type": "object", "$ref": "#/definitions/api.TokenResponse" } } @@ -413,7 +454,6 @@ "200": { "description": "OK", "schema": { - "type": "object", "$ref": "#/definitions/api.TokenValidationResponse" } }, @@ -440,7 +480,6 @@ "200": { "description": "OK", "schema": { - "type": "object", "$ref": "#/definitions/api.MapResponse" } } @@ -464,7 +503,6 @@ "202": { "description": "Accepted", "schema": { - "type": "object", "$ref": "#/definitions/api.MapResponse" } } @@ -475,11 +513,15 @@ "definitions": { "api.ArrayResponse": { "type": "array", - "items": {} + "items": { + "type": "string" + } }, "api.MapResponse": { "type": "object", - "additionalProperties": {} + "additionalProperties": { + "type": "string" + } }, "api.RuntimeResponse": { "type": "object", @@ -496,6 +538,9 @@ "hostname": { "type": "string" }, + "logo": { + "type": "string" + }, "message": { "type": "string" }, diff --git a/pkg/api/docs/swagger.yaml b/pkg/api/docs/swagger.yaml index 68cd5a9..f55e742 100644 --- a/pkg/api/docs/swagger.yaml +++ b/pkg/api/docs/swagger.yaml @@ -1,10 +1,12 @@ basePath: / definitions: api.ArrayResponse: - items: {} + items: + type: string type: array api.MapResponse: - additionalProperties: {} + additionalProperties: + type: string type: object api.RuntimeResponse: properties: @@ -16,6 +18,8 @@ definitions: type: string hostname: type: string + logo: + type: string message: type: string num_cpu: @@ -80,7 +84,6 @@ paths: description: Accepted schema: $ref: '#/definitions/api.MapResponse' - type: object summary: Echo tags: - HTTP API @@ -96,10 +99,40 @@ paths: description: OK schema: $ref: '#/definitions/api.RuntimeResponse' - type: object summary: Runtime information tags: - HTTP API + /cache: + post: + consumes: + - application/json + description: writes the posted content in cache and returns the SHA1 hash of + the content + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api.MapResponse' + summary: Save payload in cache + tags: + - HTTP API + /cache/{hash}: + get: + consumes: + - application/json + description: returns the content from cache if key exists + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + summary: Get payload from cache + tags: + - HTTP API /chunked/{seconds}: get: consumes: @@ -113,7 +146,6 @@ paths: description: OK schema: $ref: '#/definitions/api.MapResponse' - type: object summary: Chunked transfer encoding tags: - HTTP API @@ -129,7 +161,6 @@ paths: description: OK schema: $ref: '#/definitions/api.MapResponse' - type: object summary: Delay tags: - HTTP API @@ -145,7 +176,6 @@ paths: description: OK schema: $ref: '#/definitions/api.ArrayResponse' - type: object summary: Environment tags: - HTTP API @@ -161,7 +191,6 @@ paths: description: OK schema: $ref: '#/definitions/api.ArrayResponse' - type: object summary: Headers tags: - HTTP API @@ -257,7 +286,6 @@ paths: description: OK schema: $ref: '#/definitions/api.MapResponse' - type: object summary: Status code tags: - HTTP API @@ -274,7 +302,6 @@ paths: description: OK schema: $ref: '#/definitions/api.MapResponse' - type: object summary: Upload file tags: - HTTP API @@ -305,7 +332,6 @@ paths: description: OK schema: $ref: '#/definitions/api.TokenResponse' - type: object summary: Generate JWT token tags: - HTTP API @@ -321,7 +347,6 @@ paths: description: OK schema: $ref: '#/definitions/api.TokenValidationResponse' - type: object "401": description: Unauthorized schema: @@ -339,7 +364,6 @@ paths: description: OK schema: $ref: '#/definitions/api.MapResponse' - type: object summary: Version tags: - HTTP API @@ -355,8 +379,10 @@ paths: description: Accepted schema: $ref: '#/definitions/api.MapResponse' - type: object summary: Echo over websockets tags: - HTTP API +schemes: +- http +- https swagger: "2.0" diff --git a/pkg/api/server.go b/pkg/api/server.go index 5466946..d43d68e 100644 --- a/pkg/api/server.go +++ b/pkg/api/server.go @@ -3,11 +3,6 @@ package api import ( "context" "fmt" - - "github.com/swaggo/swag" - "golang.org/x/net/http2" - "golang.org/x/net/http2/h2c" - "net/http" _ "net/http/pprof" "os" @@ -15,13 +10,17 @@ import ( "sync/atomic" "time" + "github.com/gomodule/redigo/redis" "github.com/gorilla/mux" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/spf13/viper" _ "github.com/stefanprodan/podinfo/pkg/api/docs" "github.com/stefanprodan/podinfo/pkg/fscache" httpSwagger "github.com/swaggo/http-swagger" + "github.com/swaggo/swag" "go.uber.org/zap" + "golang.org/x/net/http2" + "golang.org/x/net/http2/h2c" ) // @title Podinfo API @@ -69,12 +68,14 @@ type Config struct { Unhealthy bool `mapstructure:"unhealthy"` Unready bool `mapstructure:"unready"` JWTSecret string `mapstructure:"jwt-secret"` + CacheServer string `mapstructure:"cache-server"` } type Server struct { router *mux.Router logger *zap.Logger config *Config + pool *redis.Pool } func NewServer(config *Config, logger *zap.Logger) (*Server, error) { @@ -103,8 +104,10 @@ func (s *Server) registerHandlers() { s.router.HandleFunc("/readyz/disable", s.disableReadyHandler).Methods("POST") s.router.HandleFunc("/panic", s.panicHandler).Methods("GET") s.router.HandleFunc("/status/{code:[0-9]+}", s.statusHandler).Methods("GET", "POST", "PUT").Name("status") - s.router.HandleFunc("/store", s.storeWriteHandler).Methods("POST") + 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("/configs", s.configReadHandler).Methods("GET") s.router.HandleFunc("/token", s.tokenGenerateHandler).Methods("POST") s.router.HandleFunc("/token/validate", s.tokenValidateHandler).Methods("GET") @@ -176,6 +179,27 @@ func (s *Server) ListenAndServe(stopCh <-chan struct{}) { } } + // start redis connection pool + s.startCachePool() + if s.pool != nil { + ticker := time.NewTicker(30 * time.Second) + go func() { + for { + select { + case <-stopCh: + return + case <-ticker.C: + conn := s.pool.Get() + _, err := redis.String(conn.Do("PING")) + if err != nil { + s.logger.Warn("cache server is offline", zap.Error(err), zap.String("server", s.config.CacheServer)) + } + _ = conn.Close() + } + } + }() + } + // run server in background go func() { if err := srv.ListenAndServe(); err != http.ErrServerClosed { @@ -200,6 +224,11 @@ func (s *Server) ListenAndServe(stopCh <-chan struct{}) { atomic.StoreInt32(&healthy, 0) atomic.StoreInt32(&ready, 0) + // close cache pool + if s.pool != nil { + _ = s.pool.Close() + } + s.logger.Info("Shutting down HTTP server", zap.Duration("timeout", s.config.HttpServerShutdownTimeout)) // wait for Kubernetes readiness probe to remove this instance from the load balancer