Merge pull request #29 from stefanprodan/swagger

Implement Swagger support
This commit is contained in:
Stefan Prodan
2019-08-07 16:01:15 +03:00
committed by GitHub
28 changed files with 1774 additions and 30 deletions

View File

@@ -34,9 +34,13 @@ test-container:
curl -sH "Authorization: Bearer $${TOKEN}" localhost:9898/token/validate | grep test
push-container:
docker tag $(DOCKER_IMAGE_NAME):$(VERSION) $(DOCKER_IMAGE_NAME):latest
docker push $(DOCKER_IMAGE_NAME):$(VERSION)
docker push $(DOCKER_IMAGE_NAME):latest
docker tag $(DOCKER_IMAGE_NAME):$(VERSION) quay.io/$(DOCKER_IMAGE_NAME):$(VERSION)
docker tag $(DOCKER_IMAGE_NAME):$(VERSION) quay.io/$(DOCKER_IMAGE_NAME):latest
docker push quay.io/$(DOCKER_IMAGE_NAME):$(VERSION)
docker push quay.io/$(DOCKER_IMAGE_NAME):latest
version-set:
@next="$(TAG)" && \
@@ -51,3 +55,7 @@ version-set:
release:
git tag $(VERSION)
git push origin $(VERSION)
swagger:
GO111MODULE=on go get github.com/swaggo/swag/cmd/swag
cd pkg/api && $$(go env GOPATH)/bin/swag init -g server.go

View File

@@ -16,6 +16,7 @@ Specifications:
* Structured logging with zap
* 12-factor app with viper
* Fault injection (random errors and latency)
* Swagger docs
* Helm and Kustomize installers
* End-to-End testing with Kubernetes Kind and Helm
@@ -41,11 +42,14 @@ Web API:
* `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`
* `GET /chunked/{seconds}` uses `transfer-encoding` type `chunked` to give a partial response and then waits for the specified period
* `GET /swagger.json` returns the API Swagger docs, used for Linkerd service profiling and Gloo routes discovery
Web UI:
![podinfo-ui](https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/screens/podinfo-ui.png)
To access the Swagger UI open `<podinfo-host>/swagger/index.html` in a browser.
### Guides
* [Automated canary deployments with Flagger and Istio](https://medium.com/google-cloud/automated-canary-deployments-with-flagger-and-istio-ac747827f9d1)
@@ -79,3 +83,9 @@ Kustomize:
```bash
kubectl apply -k github.com/stefanprodan/podinfo//kustomize
```
Docker:
```bash
docker run -dp 9898:9898 stefanprodan/podinfo
```

View File

@@ -1,6 +1,6 @@
apiVersion: v1
version: 2.0.2
appVersion: 2.0.2
version: 2.1.0
appVersion: 2.1.0
name: podinfo
engine: gotpl
description: Podinfo Helm chart for Kubernetes

View File

@@ -12,7 +12,7 @@ faults:
image:
repository: quay.io/stefanprodan/podinfo
tag: 2.0.2
tag: 2.1.0
pullPolicy: IfNotPresent
service:

9
go.mod
View File

@@ -4,13 +4,13 @@ go 1.12
require (
github.com/BurntSushi/toml v0.3.1 // indirect
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
github.com/aws/aws-sdk-go v1.15.63 // indirect
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/chzyer/logex v1.1.10 // indirect
github.com/chzyer/readline v0.0.0-20160726135117-62c6fe619375
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/fatih/color v1.7.0
github.com/fsnotify/fsnotify v1.4.7
@@ -31,7 +31,6 @@ require (
github.com/mitchellh/go-testing-interface v1.0.0 // indirect
github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699 // indirect
github.com/pelletier/go-toml v1.2.0 // indirect
github.com/pkg/errors v0.8.1 // indirect
github.com/prometheus/client_golang v0.8.0
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 // indirect
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e // indirect
@@ -42,12 +41,12 @@ require (
github.com/spf13/jwalterweatherman v0.0.0-20180814060501-14d3d4c51834 // indirect
github.com/spf13/pflag v1.0.2
github.com/spf13/viper v1.1.0
github.com/stretchr/testify v1.3.0 // indirect
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 // indirect
github.com/swaggo/http-swagger v0.0.0-20190614090009-c2865af9083e
github.com/swaggo/swag v1.6.2
github.com/ulikunitz/xz v0.5.4 // indirect
go.uber.org/atomic v1.3.2 // indirect
go.uber.org/multierr v1.1.0 // indirect
go.uber.org/zap v1.9.1
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 // indirect
golang.org/x/sync v0.0.0-20190423024810-112230192c58 // indirect
gopkg.in/yaml.v2 v2.2.1 // indirect
)

34
go.sum
View File

@@ -1,5 +1,13 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/aws/aws-sdk-go v1.15.63 h1:rPr7eEm/FSK23DoDKhbd9oLMYGT7JU9pkyfBUVOHXUo=
github.com/aws/aws-sdk-go v1.15.63/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
@@ -21,6 +29,17 @@ github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0=
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/jsonreference v0.19.0 h1:BqWKpV1dFd+AuiKlgtddwVIFQsuMpxfBDBHGfM2yNpk=
github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k4=
github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880=
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
@@ -43,6 +62,8 @@ github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Ao
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
@@ -82,10 +103,19 @@ github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/viper v1.1.0 h1:V7OZpY8i3C1x/pDmU0zNNlfVoDz112fSYvtWMjjS3f4=
github.com/spf13/viper v1.1.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 h1:PyYN9JH5jY9j6av01SpfRMb+1DWg/i3MbGOKPxJ2wjM=
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=
github.com/swaggo/http-swagger v0.0.0-20190614090009-c2865af9083e h1:m5sYJ43teIUlESuKRFQRRm7kqi6ExiYwVKfoXNuRgHU=
github.com/swaggo/http-swagger v0.0.0-20190614090009-c2865af9083e/go.mod h1:eycbshptIv+tqTMlLEaGC2noPNcetbrcYEelLafrIDI=
github.com/swaggo/swag v1.6.2 h1:WQMAtT/FmMBb7g0rAuHDhG3vvdtHKJ3WZ+Ssb0p4Y6E=
github.com/swaggo/swag v1.6.2/go.mod h1:YyZstMc22WYm6GEDx/CYWxq+faBbjQ5EqwQcrjREDBo=
github.com/ulikunitz/xz v0.5.4 h1:zATC2OoZ8H1TZll3FpbX+ikwmadbO699PE06cIkm9oU=
github.com/ulikunitz/xz v0.5.4/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=
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
@@ -93,6 +123,8 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
@@ -101,6 +133,8 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468 h1:fTfk6GjmihJbK0mSUFgPPgYpsdmApQ86Mcd4GuKax9U=
golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=

View File

@@ -24,7 +24,7 @@ spec:
spec:
containers:
- name: podinfod
image: quay.io/stefanprodan/podinfo:2.0.2
image: quay.io/stefanprodan/podinfo:2.1.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9898

View File

@@ -9,6 +9,14 @@ import (
"github.com/gorilla/mux"
)
// Chunked godoc
// @Summary Chunked transfer encoding
// @Description uses transfer-encoding type chunked to give a partial response and then waits for the specified period
// @Tags HTTP API
// @Accept json
// @Produce json
// @Router /chunked/{seconds} [get]
// @Success 200 {object} api.MapResponse
func (s *Server) chunkedHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)

View File

@@ -8,6 +8,14 @@ import (
"time"
)
// Delay godoc
// @Summary Delay
// @Description waits for the specified period
// @Tags HTTP API
// @Accept json
// @Produce json
// @Router /delay/{seconds} [get]
// @Success 200 {object} api.MapResponse
func (s *Server) delayHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)

594
pkg/api/docs/docs.go Normal file
View File

@@ -0,0 +1,594 @@
// 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
package docs
import (
"bytes"
"encoding/json"
"github.com/alecthomas/template"
"github.com/swaggo/swag"
)
var doc = `{
"schemes": {{ marshal .Schemes }},
"swagger": "2.0",
"info": {
"description": "Go microservice template for Kubernetes.",
"title": "Podinfo API",
"contact": {
"name": "Source Code",
"url": "https://github.com/stefanprodan/podinfo"
},
"license": {
"name": "MIT License",
"url": "https://github.com/stefanprodan/podinfo/blob/master/LICENSE"
},
"version": "2.0"
},
"host": "localhost:9898",
"basePath": "/",
"paths": {
"/": {
"get": {
"description": "renders podinfo UI",
"produces": [
"text/html"
],
"tags": [
"HTTP API"
],
"summary": "Index",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
}
},
"/api/echo": {
"post": {
"description": "forwards the call to the backend service and echos the posted content",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Echo",
"responses": {
"202": {
"description": "Accepted",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
}
}
},
"/api/info": {
"get": {
"description": "returns the runtime information",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Runtime information",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.RuntimeResponse"
}
}
}
}
},
"/chunked/{seconds}": {
"get": {
"description": "uses transfer-encoding type chunked to give a partial response and then waits for the specified period",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Chunked transfer encoding",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
}
}
},
"/delay/{seconds}": {
"get": {
"description": "waits for the specified period",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Delay",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
}
}
},
"/env": {
"get": {
"description": "returns the environment variables as a JSON array",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Environment",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.ArrayResponse"
}
}
}
}
},
"/headers": {
"get": {
"description": "returns a JSON array with the request HTTP headers",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Headers",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.ArrayResponse"
}
}
}
}
},
"/healthz": {
"get": {
"description": "used by Kubernetes liveness probe",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Kubernetes"
],
"summary": "Liveness check",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
}
},
"/metrics": {
"get": {
"description": "returns HTTP requests duration and Go runtime metrics",
"produces": [
"text/plain"
],
"tags": [
"Kubernetes"
],
"summary": "Prometheus metrics",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
}
},
"/panic": {
"get": {
"description": "crashes the process with exit code 255",
"tags": [
"HTTP API"
],
"summary": "Panic"
}
},
"/readyz": {
"get": {
"description": "used by Kubernetes readiness probe",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Kubernetes"
],
"summary": "Readiness check",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
}
},
"/readyz/disable": {
"post": {
"description": "signals the Kubernetes LB to stop sending requests to this instance",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Kubernetes"
],
"summary": "Disable ready state",
"responses": {
"202": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
}
},
"/readyz/enable": {
"post": {
"description": "signals the Kubernetes LB that this instance is ready to receive traffic",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Kubernetes"
],
"summary": "Enable ready state",
"responses": {
"202": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
}
},
"/status/{code}": {
"get": {
"description": "sets the response status code to the specified code",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Status code",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
}
}
},
"/store": {
"post": {
"description": "writes the posted content to disk at /data/hash and returns the SHA1 hash of the content",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Upload file",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
}
}
},
"/store/{hash}": {
"get": {
"description": "returns the content of the file /data/hash if exists",
"consumes": [
"application/json"
],
"produces": [
"text/plain"
],
"tags": [
"HTTP API"
],
"summary": "Download file",
"responses": {
"200": {
"description": "file",
"schema": {
"type": "string"
}
}
}
}
},
"/token": {
"post": {
"description": "issues a JWT token valid for one minute",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Generate JWT token",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.TokenResponse"
}
}
}
}
},
"/token/validate": {
"post": {
"description": "validates the JWT token",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Validate JWT token",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.TokenValidationResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"type": "string"
}
}
}
}
},
"/version": {
"get": {
"description": "returns podinfo version and git commit hash",
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Version",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
}
}
},
"/ws/echo": {
"post": {
"description": "echos content via websockets",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Echo over websockets",
"responses": {
"202": {
"description": "Accepted",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
}
}
}
},
"definitions": {
"api.ArrayResponse": {
"type": "array",
"items": {}
},
"api.MapResponse": {
"type": "object",
"additionalProperties": {}
},
"api.RuntimeResponse": {
"type": "object",
"properties": {
"color": {
"type": "string"
},
"goarch": {
"type": "string"
},
"goos": {
"type": "string"
},
"hostname": {
"type": "string"
},
"message": {
"type": "string"
},
"num_cpu": {
"type": "string"
},
"num_goroutine": {
"type": "string"
},
"revision": {
"type": "string"
},
"runtime": {
"type": "string"
},
"version": {
"type": "string"
}
}
},
"api.TokenResponse": {
"type": "object",
"properties": {
"expires_at": {
"type": "string"
},
"token": {
"type": "string"
}
}
},
"api.TokenValidationResponse": {
"type": "object",
"properties": {
"expires_at": {
"type": "string"
},
"token_name": {
"type": "string"
}
}
}
}
}`
type swaggerInfo struct {
Version string
Host string
BasePath string
Schemes []string
Title string
Description string
}
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = swaggerInfo{Schemes: []string{"http", "https"}}
type s struct{}
func (s *s) ReadDoc() string {
t, err := template.New("swagger_info").Funcs(template.FuncMap{
"marshal": func(v interface{}) string {
a, _ := json.Marshal(v)
return string(a)
},
}).Parse(doc)
if err != nil {
return doc
}
var tpl bytes.Buffer
if err := t.Execute(&tpl, SwaggerInfo); err != nil {
return doc
}
return tpl.String()
}
func init() {
swag.Register(swag.Name, &s{})
}

542
pkg/api/docs/swagger.json Normal file
View File

@@ -0,0 +1,542 @@
{
"swagger": "2.0",
"info": {
"description": "Go microservice template for Kubernetes.",
"title": "Podinfo API",
"contact": {
"name": "Source Code",
"url": "https://github.com/stefanprodan/podinfo"
},
"license": {
"name": "MIT License",
"url": "https://github.com/stefanprodan/podinfo/blob/master/LICENSE"
},
"version": "2.0"
},
"host": "localhost:9898",
"basePath": "/",
"paths": {
"/": {
"get": {
"description": "renders podinfo UI",
"produces": [
"text/html"
],
"tags": [
"HTTP API"
],
"summary": "Index",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
}
},
"/api/echo": {
"post": {
"description": "forwards the call to the backend service and echos the posted content",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Echo",
"responses": {
"202": {
"description": "Accepted",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
}
}
},
"/api/info": {
"get": {
"description": "returns the runtime information",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Runtime information",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.RuntimeResponse"
}
}
}
}
},
"/chunked/{seconds}": {
"get": {
"description": "uses transfer-encoding type chunked to give a partial response and then waits for the specified period",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Chunked transfer encoding",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
}
}
},
"/delay/{seconds}": {
"get": {
"description": "waits for the specified period",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Delay",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
}
}
},
"/env": {
"get": {
"description": "returns the environment variables as a JSON array",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Environment",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.ArrayResponse"
}
}
}
}
},
"/headers": {
"get": {
"description": "returns a JSON array with the request HTTP headers",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Headers",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.ArrayResponse"
}
}
}
}
},
"/healthz": {
"get": {
"description": "used by Kubernetes liveness probe",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Kubernetes"
],
"summary": "Liveness check",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
}
},
"/metrics": {
"get": {
"description": "returns HTTP requests duration and Go runtime metrics",
"produces": [
"text/plain"
],
"tags": [
"Kubernetes"
],
"summary": "Prometheus metrics",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
}
},
"/panic": {
"get": {
"description": "crashes the process with exit code 255",
"tags": [
"HTTP API"
],
"summary": "Panic"
}
},
"/readyz": {
"get": {
"description": "used by Kubernetes readiness probe",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Kubernetes"
],
"summary": "Readiness check",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
}
},
"/readyz/disable": {
"post": {
"description": "signals the Kubernetes LB to stop sending requests to this instance",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Kubernetes"
],
"summary": "Disable ready state",
"responses": {
"202": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
}
},
"/readyz/enable": {
"post": {
"description": "signals the Kubernetes LB that this instance is ready to receive traffic",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Kubernetes"
],
"summary": "Enable ready state",
"responses": {
"202": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
}
},
"/status/{code}": {
"get": {
"description": "sets the response status code to the specified code",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Status code",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
}
}
},
"/store": {
"post": {
"description": "writes the posted content to disk at /data/hash and returns the SHA1 hash of the content",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Upload file",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
}
}
},
"/store/{hash}": {
"get": {
"description": "returns the content of the file /data/hash if exists",
"consumes": [
"application/json"
],
"produces": [
"text/plain"
],
"tags": [
"HTTP API"
],
"summary": "Download file",
"responses": {
"200": {
"description": "file",
"schema": {
"type": "string"
}
}
}
}
},
"/token": {
"post": {
"description": "issues a JWT token valid for one minute",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Generate JWT token",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.TokenResponse"
}
}
}
}
},
"/token/validate": {
"post": {
"description": "validates the JWT token",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Validate JWT token",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.TokenValidationResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"type": "string"
}
}
}
}
},
"/version": {
"get": {
"description": "returns podinfo version and git commit hash",
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Version",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
}
}
},
"/ws/echo": {
"post": {
"description": "echos content via websockets",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"HTTP API"
],
"summary": "Echo over websockets",
"responses": {
"202": {
"description": "Accepted",
"schema": {
"type": "object",
"$ref": "#/definitions/api.MapResponse"
}
}
}
}
}
},
"definitions": {
"api.ArrayResponse": {
"type": "array",
"items": {}
},
"api.MapResponse": {
"type": "object",
"additionalProperties": {}
},
"api.RuntimeResponse": {
"type": "object",
"properties": {
"color": {
"type": "string"
},
"goarch": {
"type": "string"
},
"goos": {
"type": "string"
},
"hostname": {
"type": "string"
},
"message": {
"type": "string"
},
"num_cpu": {
"type": "string"
},
"num_goroutine": {
"type": "string"
},
"revision": {
"type": "string"
},
"runtime": {
"type": "string"
},
"version": {
"type": "string"
}
}
},
"api.TokenResponse": {
"type": "object",
"properties": {
"expires_at": {
"type": "string"
},
"token": {
"type": "string"
}
}
},
"api.TokenValidationResponse": {
"type": "object",
"properties": {
"expires_at": {
"type": "string"
},
"token_name": {
"type": "string"
}
}
}
}
}

362
pkg/api/docs/swagger.yaml Normal file
View File

@@ -0,0 +1,362 @@
basePath: /
definitions:
api.ArrayResponse:
items: {}
type: array
api.MapResponse:
additionalProperties: {}
type: object
api.RuntimeResponse:
properties:
color:
type: string
goarch:
type: string
goos:
type: string
hostname:
type: string
message:
type: string
num_cpu:
type: string
num_goroutine:
type: string
revision:
type: string
runtime:
type: string
version:
type: string
type: object
api.TokenResponse:
properties:
expires_at:
type: string
token:
type: string
type: object
api.TokenValidationResponse:
properties:
expires_at:
type: string
token_name:
type: string
type: object
host: localhost:9898
info:
contact:
name: Source Code
url: https://github.com/stefanprodan/podinfo
description: Go microservice template for Kubernetes.
license:
name: MIT License
url: https://github.com/stefanprodan/podinfo/blob/master/LICENSE
title: Podinfo API
version: "2.0"
paths:
/:
get:
description: renders podinfo UI
produces:
- text/html
responses:
"200":
description: OK
schema:
type: string
summary: Index
tags:
- HTTP API
/api/echo:
post:
consumes:
- application/json
description: forwards the call to the backend service and echos the posted content
produces:
- application/json
responses:
"202":
description: Accepted
schema:
$ref: '#/definitions/api.MapResponse'
type: object
summary: Echo
tags:
- HTTP API
/api/info:
get:
consumes:
- application/json
description: returns the runtime information
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.RuntimeResponse'
type: object
summary: Runtime information
tags:
- HTTP API
/chunked/{seconds}:
get:
consumes:
- application/json
description: uses transfer-encoding type chunked to give a partial response
and then waits for the specified period
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.MapResponse'
type: object
summary: Chunked transfer encoding
tags:
- HTTP API
/delay/{seconds}:
get:
consumes:
- application/json
description: waits for the specified period
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.MapResponse'
type: object
summary: Delay
tags:
- HTTP API
/env:
get:
consumes:
- application/json
description: returns the environment variables as a JSON array
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.ArrayResponse'
type: object
summary: Environment
tags:
- HTTP API
/headers:
get:
consumes:
- application/json
description: returns a JSON array with the request HTTP headers
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.ArrayResponse'
type: object
summary: Headers
tags:
- HTTP API
/healthz:
get:
consumes:
- application/json
description: used by Kubernetes liveness probe
produces:
- application/json
responses:
"200":
description: OK
schema:
type: string
summary: Liveness check
tags:
- Kubernetes
/metrics:
get:
description: returns HTTP requests duration and Go runtime metrics
produces:
- text/plain
responses:
"200":
description: OK
schema:
type: string
summary: Prometheus metrics
tags:
- Kubernetes
/panic:
get:
description: crashes the process with exit code 255
summary: Panic
tags:
- HTTP API
/readyz:
get:
consumes:
- application/json
description: used by Kubernetes readiness probe
produces:
- application/json
responses:
"200":
description: OK
schema:
type: string
summary: Readiness check
tags:
- Kubernetes
/readyz/disable:
post:
consumes:
- application/json
description: signals the Kubernetes LB to stop sending requests to this instance
produces:
- application/json
responses:
"202":
description: OK
schema:
type: string
summary: Disable ready state
tags:
- Kubernetes
/readyz/enable:
post:
consumes:
- application/json
description: signals the Kubernetes LB that this instance is ready to receive
traffic
produces:
- application/json
responses:
"202":
description: OK
schema:
type: string
summary: Enable ready state
tags:
- Kubernetes
/status/{code}:
get:
consumes:
- application/json
description: sets the response status code to the specified code
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.MapResponse'
type: object
summary: Status code
tags:
- HTTP API
/store:
post:
consumes:
- application/json
description: writes the posted content to disk at /data/hash and returns the
SHA1 hash of the content
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.MapResponse'
type: object
summary: Upload file
tags:
- HTTP API
/store/{hash}:
get:
consumes:
- application/json
description: returns the content of the file /data/hash if exists
produces:
- text/plain
responses:
"200":
description: file
schema:
type: string
summary: Download file
tags:
- HTTP API
/token:
post:
consumes:
- application/json
description: issues a JWT token valid for one minute
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.TokenResponse'
type: object
summary: Generate JWT token
tags:
- HTTP API
/token/validate:
post:
consumes:
- application/json
description: validates the JWT token
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.TokenValidationResponse'
type: object
"401":
description: Unauthorized
schema:
type: string
summary: Validate JWT token
tags:
- HTTP API
/version:
get:
description: returns podinfo version and git commit hash
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.MapResponse'
type: object
summary: Version
tags:
- HTTP API
/ws/echo:
post:
consumes:
- application/json
description: echos content via websockets
produces:
- application/json
responses:
"202":
description: Accepted
schema:
$ref: '#/definitions/api.MapResponse'
type: object
summary: Echo over websockets
tags:
- HTTP API
swagger: "2.0"

View File

@@ -10,6 +10,14 @@ import (
"go.uber.org/zap"
)
// Echo godoc
// @Summary Echo
// @Description forwards the call to the backend service and echos the posted content
// @Tags HTTP API
// @Accept json
// @Produce json
// @Router /api/echo [post]
// @Success 202 {object} api.MapResponse
func (s *Server) echoHandler(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {

View File

@@ -11,6 +11,14 @@ import (
var wsCon = websocket.Upgrader{}
// EchoWS godoc
// @Summary Echo over websockets
// @Description echos content via websockets
// @Tags HTTP API
// @Accept json
// @Produce json
// @Router /ws/echo [post]
// @Success 202 {object} api.MapResponse
// Test: go run ./cmd/podcli/* ws localhost:9898/ws/echo
func (s *Server) echoWsHandler(w http.ResponseWriter, r *http.Request) {
c, err := wsCon.Upgrade(w, r, nil)

View File

@@ -6,6 +6,14 @@ import (
"os"
)
// Env godoc
// @Summary Environment
// @Description returns the environment variables as a JSON array
// @Tags HTTP API
// @Accept json
// @Produce json
// @Router /env [get]
// @Success 200 {object} api.ArrayResponse
func (s *Server) envHandler(w http.ResponseWriter, r *http.Request) {
s.JSONResponse(w, r, os.Environ())
}

View File

@@ -4,6 +4,14 @@ import (
"net/http"
)
// Headers godoc
// @Summary Headers
// @Description returns a JSON array with the request HTTP headers
// @Tags HTTP API
// @Accept json
// @Produce json
// @Router /headers [get]
// @Success 200 {object} api.ArrayResponse
func (s *Server) echoHeadersHandler(w http.ResponseWriter, r *http.Request) {
s.JSONResponse(w, r, r.Header)
}

View File

@@ -5,6 +5,14 @@ import (
"sync/atomic"
)
// Healthz godoc
// @Summary Liveness check
// @Description used by Kubernetes liveness probe
// @Tags Kubernetes
// @Accept json
// @Produce json
// @Router /healthz [get]
// @Success 200 {string} string "OK"
func (s *Server) healthzHandler(w http.ResponseWriter, r *http.Request) {
if atomic.LoadInt32(&healthy) == 1 {
s.JSONResponse(w, r, map[string]string{"status": "OK"})
@@ -13,6 +21,14 @@ func (s *Server) healthzHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusServiceUnavailable)
}
// Readyz godoc
// @Summary Readiness check
// @Description used by Kubernetes readiness probe
// @Tags Kubernetes
// @Accept json
// @Produce json
// @Router /readyz [get]
// @Success 200 {string} string "OK"
func (s *Server) readyzHandler(w http.ResponseWriter, r *http.Request) {
if atomic.LoadInt32(&ready) == 1 {
s.JSONResponse(w, r, map[string]string{"status": "OK"})
@@ -21,11 +37,27 @@ func (s *Server) readyzHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusServiceUnavailable)
}
// EnableReady godoc
// @Summary Enable ready state
// @Description signals the Kubernetes LB that this instance is ready to receive traffic
// @Tags Kubernetes
// @Accept json
// @Produce json
// @Router /readyz/enable [post]
// @Success 202 {string} string "OK"
func (s *Server) enableReadyHandler(w http.ResponseWriter, r *http.Request) {
atomic.StoreInt32(&ready, 1)
w.WriteHeader(http.StatusAccepted)
}
// DisableReady godoc
// @Summary Disable ready state
// @Description signals the Kubernetes LB to stop sending requests to this instance
// @Tags Kubernetes
// @Accept json
// @Produce json
// @Router /readyz/disable [post]
// @Success 202 {string} string "OK"
func (s *Server) disableReadyHandler(w http.ResponseWriter, r *http.Request) {
atomic.StoreInt32(&ready, 0)
w.WriteHeader(http.StatusAccepted)

View File

@@ -6,6 +6,13 @@ import (
"path"
)
// Index godoc
// @Summary Index
// @Description renders podinfo UI
// @Tags HTTP API
// @Produce html
// @Router / [get]
// @Success 200 {string} string "OK"
func (s *Server) indexHandler(w http.ResponseWriter, r *http.Request) {
tmpl, err := template.New("vue.html").ParseFiles(path.Join(s.config.UIPath, "vue.html"))
if err != nil {

View File

@@ -9,19 +9,16 @@ import (
"github.com/stefanprodan/podinfo/pkg/version"
)
// Info godoc
// @Summary Runtime information
// @Description returns the runtime information
// @Tags HTTP API
// @Accept json
// @Produce json
// @Success 200 {object} api.RuntimeResponse
// @Router /api/info [get]
func (s *Server) infoHandler(w http.ResponseWriter, r *http.Request) {
data := struct {
Hostname string `json:"hostname"`
Version string `json:"version"`
Revision string `json:"revision"`
Color string `json:"color"`
Message string `json:"message"`
GOOS string `json:"goos"`
GOARCH string `json:"goarch"`
Runtime string `json:"runtime"`
NumGoroutine string `json:"num_goroutine"`
NumCPU string `json:"num_cpu"`
}{
data := RuntimeResponse{
Hostname: s.config.Hostname,
Version: version.VERSION,
Revision: version.REVISION,
@@ -36,3 +33,16 @@ func (s *Server) infoHandler(w http.ResponseWriter, r *http.Request) {
s.JSONResponse(w, r, data)
}
type RuntimeResponse struct {
Hostname string `json:"hostname"`
Version string `json:"version"`
Revision string `json:"revision"`
Color string `json:"color"`
Message string `json:"message"`
GOOS string `json:"goos"`
GOARCH string `json:"goarch"`
Runtime string `json:"runtime"`
NumGoroutine string `json:"num_goroutine"`
NumCPU string `json:"num_cpu"`
}

View File

@@ -47,6 +47,13 @@ func NewPrometheusMiddleware() *PrometheusMiddleware {
}
}
// Metrics godoc
// @Summary Prometheus metrics
// @Description returns HTTP requests duration and Go runtime metrics
// @Tags Kubernetes
// @Produce plain
// @Router /metrics [get]
// @Success 200 {string} string "OK"
func (p *PrometheusMiddleware) Handler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
begin := time.Now()

View File

@@ -4,6 +4,11 @@ import (
"net/http"
)
// Panic godoc
// @Summary Panic
// @Description crashes the process with exit code 255
// @Tags HTTP API
// @Router /panic [get]
func (s *Server) panicHandler(w http.ResponseWriter, r *http.Request) {
s.logger.Panic("Panic command received")
}

View File

@@ -3,6 +3,7 @@ package api
import (
"context"
"fmt"
"github.com/swaggo/swag"
"net/http"
_ "net/http/pprof"
"os"
@@ -13,10 +14,26 @@ import (
"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"
"github.com/swaggo/http-swagger"
"go.uber.org/zap"
)
// @title Podinfo API
// @version 2.0
// @description Go microservice template for Kubernetes.
// @contact.name Source Code
// @contact.url https://github.com/stefanprodan/podinfo
// @license.name MIT License
// @license.url https://github.com/stefanprodan/podinfo/blob/master/LICENSE
// @host localhost:9898
// @BasePath /
// @schemes http https
var (
healthy int32
ready int32
@@ -83,6 +100,19 @@ func (s *Server) registerHandlers() {
s.router.HandleFunc("/ws/echo", s.echoWsHandler)
s.router.HandleFunc("/chunked", s.chunkedHandler)
s.router.HandleFunc("/chunked/{wait:[0-9]+}", s.chunkedHandler)
s.router.PathPrefix("/swagger/").Handler(httpSwagger.Handler(
httpSwagger.URL("/swagger/doc.json"),
))
s.router.PathPrefix("/swagger/").Handler(httpSwagger.Handler(
httpSwagger.URL("/swagger/doc.json"),
))
s.router.HandleFunc("/swagger.json", func(w http.ResponseWriter, r *http.Request) {
doc, err := swag.ReadDoc()
if err != nil {
s.logger.Error("swagger error", zap.Error(err), zap.String("path", "/swagger.json"))
}
w.Write([]byte(doc))
})
}
func (s *Server) registerMiddlewares() {
@@ -206,3 +236,6 @@ func (s *Server) printRoutes() {
return nil
})
}
type ArrayResponse []string
type MapResponse map[string]string

View File

@@ -7,6 +7,14 @@ import (
"strconv"
)
// Status godoc
// @Summary Status code
// @Description sets the response status code to the specified code
// @Tags HTTP API
// @Accept json
// @Produce json
// @Router /status/{code} [get]
// @Success 200 {object} api.MapResponse
func (s *Server) statusHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)

View File

@@ -11,6 +11,14 @@ import (
"go.uber.org/zap"
)
// Store godoc
// @Summary Upload file
// @Description writes the posted content to disk at /data/hash and returns the SHA1 hash of the content
// @Tags HTTP API
// @Accept json
// @Produce json
// @Router /store [post]
// @Success 200 {object} api.MapResponse
func (s *Server) storeWriteHandler(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
@@ -29,6 +37,14 @@ func (s *Server) storeWriteHandler(w http.ResponseWriter, r *http.Request) {
s.JSONResponseCode(w, r, map[string]string{"hash": hash}, http.StatusAccepted)
}
// Store godoc
// @Summary Download file
// @Description returns the content of the file /data/hash if exists
// @Tags HTTP API
// @Accept json
// @Produce plain
// @Router /store/{hash} [get]
// @Success 200 {string} string "file"
func (s *Server) storeReadHandler(w http.ResponseWriter, r *http.Request) {
hash := mux.Vars(r)["hash"]
content, err := ioutil.ReadFile(path.Join(s.config.DataPath, hash))

View File

@@ -17,6 +17,14 @@ type jwtCustomClaims struct {
jwt.StandardClaims
}
// Token godoc
// @Summary Generate JWT token
// @Description issues a JWT token valid for one minute
// @Tags HTTP API
// @Accept json
// @Produce json
// @Router /token [post]
// @Success 200 {object} api.TokenResponse
func (s *Server) tokenGenerateHandler(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
@@ -46,10 +54,7 @@ func (s *Server) tokenGenerateHandler(w http.ResponseWriter, r *http.Request) {
return
}
var result = struct {
Token string `json:"token"`
ExpiresAt time.Time `json:"expires_at"`
}{
var result = TokenResponse{
Token: t,
ExpiresAt: time.Unix(claims.StandardClaims.ExpiresAt, 0),
}
@@ -57,6 +62,15 @@ func (s *Server) tokenGenerateHandler(w http.ResponseWriter, r *http.Request) {
s.JSONResponse(w, r, result)
}
// TokenValidate godoc
// @Summary Validate JWT token
// @Description validates the JWT token
// @Tags HTTP API
// @Accept json
// @Produce json
// @Router /token/validate [post]
// @Success 200 {object} api.TokenValidationResponse
// @Failure 401 {string} string "Unauthorized"
// Get: JWT=$(curl -s -d 'test' localhost:9898/token | jq -r .token)
// Post: curl -H "Authorization: Bearer ${JWT}" localhost:9898/token/validate
func (s *Server) tokenValidateHandler(w http.ResponseWriter, r *http.Request) {
@@ -87,10 +101,7 @@ func (s *Server) tokenValidateHandler(w http.ResponseWriter, r *http.Request) {
if claims.StandardClaims.Issuer != "podinfo" {
s.ErrorResponse(w, r, "invalid issuer", http.StatusUnauthorized)
} else {
var result = struct {
TokenName string `json:"token_name"`
ExpiresAt time.Time `json:"expires_at"`
}{
var result = TokenValidationResponse{
TokenName: claims.Name,
ExpiresAt: time.Unix(claims.StandardClaims.ExpiresAt, 0),
}
@@ -100,3 +111,13 @@ func (s *Server) tokenValidateHandler(w http.ResponseWriter, r *http.Request) {
s.ErrorResponse(w, r, "Invalid authorization token", http.StatusUnauthorized)
}
}
type TokenResponse struct {
Token string `json:"token"`
ExpiresAt time.Time `json:"expires_at"`
}
type TokenValidationResponse struct {
TokenName string `json:"token_name"`
ExpiresAt time.Time `json:"expires_at"`
}

View File

@@ -6,6 +6,13 @@ import (
"github.com/stefanprodan/podinfo/pkg/version"
)
// Version godoc
// @Summary Version
// @Description returns podinfo version and git commit hash
// @Tags HTTP API
// @Produce json
// @Router /version [get]
// @Success 200 {object} api.MapResponse
func (s *Server) versionHandler(w http.ResponseWriter, r *http.Request) {
result := map[string]string{
"version": version.VERSION,

View File

@@ -1,4 +1,4 @@
package version
var VERSION = "2.0.2"
var VERSION = "2.1.0"
var REVISION = "unknown"

View File

@@ -59,6 +59,7 @@
Powered
by <a class="white--text" href="https://github.com/stefanprodan/podinfo" target="_blank">podinfo</a>
version ${ info.version } revision ${ info.revision }
Swagger <a class="white--text" href="swagger/">docs</a>
</div>
</v-flex>
</v-layout>