mirror of
https://github.com/stefanprodan/podinfo.git
synced 2026-04-08 03:56:52 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c8a85a5ab | ||
|
|
c9dc78f29c | ||
|
|
6a9b0253ac | ||
|
|
198211e20b | ||
|
|
e1ca9e227d | ||
|
|
4fc593f42c |
@@ -38,6 +38,7 @@ Web API:
|
||||
* `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`
|
||||
* `GET /chunked/{seconds}` uses `transfer-encoding` type `chunked` to give a partial response and then waits for the specified period
|
||||
|
||||
### Guides
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
apiVersion: v1
|
||||
version: 1.6.0
|
||||
appVersion: 1.6.1
|
||||
version: 1.7.0
|
||||
appVersion: 1.7.1
|
||||
name: podinfo
|
||||
engine: gotpl
|
||||
description: Podinfo Helm chart for Kubernetes
|
||||
|
||||
@@ -12,7 +12,7 @@ faults:
|
||||
|
||||
image:
|
||||
repository: quay.io/stefanprodan/podinfo
|
||||
tag: 1.6.1
|
||||
tag: 1.7.1
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
service:
|
||||
|
||||
36
pkg/api/chunked.go
Normal file
36
pkg/api/chunked.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func (s *Server) chunkedHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
|
||||
delay, err := strconv.Atoi(vars["wait"])
|
||||
if err != nil {
|
||||
delay = rand.Intn(int(s.config.HttpServerTimeout*time.Second)-10) + 10
|
||||
}
|
||||
|
||||
flusher, ok := w.(http.Flusher)
|
||||
if !ok {
|
||||
s.ErrorResponse(w, r, "Streaming unsupported!", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Connection", "Keep-Alive")
|
||||
w.Header().Set("Transfer-Encoding", "chunked")
|
||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
|
||||
flusher.Flush()
|
||||
|
||||
time.Sleep(time.Duration(delay) * time.Second)
|
||||
s.JSONResponse(w, r, map[string]int{"delay": delay})
|
||||
|
||||
flusher.Flush()
|
||||
}
|
||||
35
pkg/api/chunked_test.go
Normal file
35
pkg/api/chunked_test.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestChunkedHandler(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", "/chunked/0", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
srv := NewMockServer()
|
||||
|
||||
srv.router.HandleFunc("/chunked/{wait}", srv.chunkedHandler)
|
||||
srv.router.ServeHTTP(rr, req)
|
||||
|
||||
// Check the status code is what we expect.
|
||||
if status := rr.Code; status != http.StatusOK {
|
||||
t.Errorf("handler returned wrong status code: got %v want %v",
|
||||
status, http.StatusOK)
|
||||
}
|
||||
|
||||
// Check the response body is what we expect.
|
||||
expected := ".*delay.*0.*"
|
||||
r := regexp.MustCompile(expected)
|
||||
if !r.MatchString(rr.Body.String()) {
|
||||
t.Fatalf("handler returned unexpected body:\ngot \n%v \nwant \n%s",
|
||||
rr.Body.String(), expected)
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package api
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"regexp"
|
||||
@@ -51,7 +52,7 @@ func (p *PrometheusMiddleware) Handler(next http.Handler) http.Handler {
|
||||
begin := time.Now()
|
||||
interceptor := &interceptor{ResponseWriter: w, statusCode: http.StatusOK}
|
||||
path := p.getRouteName(r)
|
||||
next.ServeHTTP(interceptor, r)
|
||||
next.ServeHTTP(interceptor.wrappedResponseWriter(), r)
|
||||
var (
|
||||
status = strconv.Itoa(interceptor.statusCode)
|
||||
took = time.Since(begin)
|
||||
@@ -94,6 +95,14 @@ type interceptor struct {
|
||||
recorded bool
|
||||
}
|
||||
|
||||
func (i *interceptor) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
hj, ok := i.ResponseWriter.(http.Hijacker)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("interceptor: can't cast parent ResponseWriter to Hijacker")
|
||||
}
|
||||
return hj.Hijack()
|
||||
}
|
||||
|
||||
func (i *interceptor) WriteHeader(code int) {
|
||||
if !i.recorded {
|
||||
i.statusCode = code
|
||||
@@ -102,10 +111,262 @@ func (i *interceptor) WriteHeader(code int) {
|
||||
i.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
func (i *interceptor) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
hj, ok := i.ResponseWriter.(http.Hijacker)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("interceptor: can't cast parent ResponseWriter to Hijacker")
|
||||
// Returns a wrapped http.ResponseWriter that implements the same optional interfaces
|
||||
// that the underlying ResponseWriter has.
|
||||
// Handle every possible combination so that code that checks for the existence of each
|
||||
// optional interface functions properly.
|
||||
// Based on https://github.com/felixge/httpsnoop/blob/eadd4fad6aac69ae62379194fe0219f3dbc80fd3/wrap_generated_gteq_1.8.go#L66
|
||||
func (i *interceptor) wrappedResponseWriter() http.ResponseWriter {
|
||||
closeNotifier, isCloseNotifier := i.ResponseWriter.(http.CloseNotifier)
|
||||
flush, isFlusher := i.ResponseWriter.(http.Flusher)
|
||||
hijack, isHijacker := i.ResponseWriter.(http.Hijacker)
|
||||
push, isPusher := i.ResponseWriter.(http.Pusher)
|
||||
readFrom, isReaderFrom := i.ResponseWriter.(io.ReaderFrom)
|
||||
|
||||
switch {
|
||||
case !isCloseNotifier && !isFlusher && !isHijacker && !isPusher && !isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
}{i}
|
||||
|
||||
case isCloseNotifier && !isFlusher && !isHijacker && !isPusher && !isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
}{i, closeNotifier}
|
||||
|
||||
case !isCloseNotifier && isFlusher && !isHijacker && !isPusher && !isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Flusher
|
||||
}{i, flush}
|
||||
|
||||
case !isCloseNotifier && !isFlusher && isHijacker && !isPusher && !isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
}{i, hijack}
|
||||
|
||||
case !isCloseNotifier && !isFlusher && !isHijacker && isPusher && !isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Pusher
|
||||
}{i, push}
|
||||
|
||||
case !isCloseNotifier && !isFlusher && !isHijacker && !isPusher && isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
io.ReaderFrom
|
||||
}{i, readFrom}
|
||||
|
||||
case isCloseNotifier && isFlusher && !isHijacker && !isPusher && !isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Flusher
|
||||
}{i, closeNotifier, flush}
|
||||
|
||||
case isCloseNotifier && !isFlusher && isHijacker && !isPusher && !isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Hijacker
|
||||
}{i, closeNotifier, hijack}
|
||||
|
||||
case isCloseNotifier && !isFlusher && !isHijacker && isPusher && !isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Pusher
|
||||
}{i, closeNotifier, push}
|
||||
|
||||
case isCloseNotifier && !isFlusher && !isHijacker && !isPusher && isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
io.ReaderFrom
|
||||
}{i, closeNotifier, readFrom}
|
||||
|
||||
case !isCloseNotifier && isFlusher && isHijacker && !isPusher && !isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Flusher
|
||||
http.Hijacker
|
||||
}{i, flush, hijack}
|
||||
|
||||
case !isCloseNotifier && isFlusher && !isHijacker && isPusher && !isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Flusher
|
||||
http.Pusher
|
||||
}{i, flush, push}
|
||||
|
||||
case !isCloseNotifier && isFlusher && !isHijacker && !isPusher && isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Flusher
|
||||
io.ReaderFrom
|
||||
}{i, flush, readFrom}
|
||||
|
||||
case !isCloseNotifier && !isFlusher && isHijacker && isPusher && !isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.Pusher
|
||||
}{i, hijack, push}
|
||||
|
||||
case !isCloseNotifier && !isFlusher && isHijacker && !isPusher && isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
io.ReaderFrom
|
||||
}{i, hijack, readFrom}
|
||||
|
||||
case !isCloseNotifier && !isFlusher && !isHijacker && isPusher && isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Pusher
|
||||
io.ReaderFrom
|
||||
}{i, push, readFrom}
|
||||
|
||||
case isCloseNotifier && isFlusher && isHijacker && !isPusher && !isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Flusher
|
||||
http.Hijacker
|
||||
}{i, closeNotifier, flush, hijack}
|
||||
|
||||
case isCloseNotifier && isFlusher && !isHijacker && isPusher && !isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Flusher
|
||||
http.Pusher
|
||||
}{i, closeNotifier, flush, push}
|
||||
|
||||
case isCloseNotifier && isFlusher && !isHijacker && !isPusher && isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Flusher
|
||||
io.ReaderFrom
|
||||
}{i, closeNotifier, flush, readFrom}
|
||||
|
||||
case isCloseNotifier && !isFlusher && isHijacker && isPusher && !isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Hijacker
|
||||
http.Pusher
|
||||
}{i, closeNotifier, hijack, push}
|
||||
|
||||
case isCloseNotifier && !isFlusher && isHijacker && !isPusher && isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Hijacker
|
||||
io.ReaderFrom
|
||||
}{i, closeNotifier, hijack, readFrom}
|
||||
|
||||
case isCloseNotifier && !isFlusher && !isHijacker && isPusher && isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Pusher
|
||||
io.ReaderFrom
|
||||
}{i, closeNotifier, push, readFrom}
|
||||
|
||||
case !isCloseNotifier && isFlusher && isHijacker && isPusher && !isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Flusher
|
||||
http.Hijacker
|
||||
http.Pusher
|
||||
}{i, flush, hijack, push}
|
||||
|
||||
case !isCloseNotifier && isFlusher && isHijacker && !isPusher && isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Flusher
|
||||
http.Hijacker
|
||||
io.ReaderFrom
|
||||
}{i, flush, hijack, readFrom}
|
||||
|
||||
case !isCloseNotifier && isFlusher && !isHijacker && isPusher && isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Flusher
|
||||
http.Pusher
|
||||
io.ReaderFrom
|
||||
}{i, flush, push, readFrom}
|
||||
|
||||
case !isCloseNotifier && !isFlusher && isHijacker && isPusher && isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.Pusher
|
||||
io.ReaderFrom
|
||||
}{i, hijack, push, readFrom}
|
||||
|
||||
case isCloseNotifier && isFlusher && isHijacker && isPusher && !isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Flusher
|
||||
http.Hijacker
|
||||
http.Pusher
|
||||
}{i, closeNotifier, flush, hijack, push}
|
||||
|
||||
case isCloseNotifier && isFlusher && isHijacker && !isPusher && isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Flusher
|
||||
http.Hijacker
|
||||
io.ReaderFrom
|
||||
}{i, closeNotifier, flush, hijack, readFrom}
|
||||
|
||||
case isCloseNotifier && isFlusher && !isHijacker && isPusher && isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Flusher
|
||||
http.Pusher
|
||||
io.ReaderFrom
|
||||
}{i, closeNotifier, flush, push, readFrom}
|
||||
|
||||
case isCloseNotifier && !isFlusher && isHijacker && isPusher && isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Hijacker
|
||||
http.Pusher
|
||||
io.ReaderFrom
|
||||
}{i, closeNotifier, hijack, push, readFrom}
|
||||
|
||||
case !isCloseNotifier && isFlusher && isHijacker && isPusher && isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Flusher
|
||||
http.Hijacker
|
||||
http.Pusher
|
||||
io.ReaderFrom
|
||||
}{i, flush, hijack, push, readFrom}
|
||||
|
||||
case isCloseNotifier && isFlusher && isHijacker && isPusher && isReaderFrom:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Flusher
|
||||
http.Hijacker
|
||||
http.Pusher
|
||||
io.ReaderFrom
|
||||
}{i, closeNotifier, flush, hijack, push, readFrom}
|
||||
|
||||
default:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
}{i}
|
||||
}
|
||||
return hj.Hijack()
|
||||
}
|
||||
|
||||
@@ -3,17 +3,18 @@ package api
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stefanprodan/k8s-podinfo/pkg/fscache"
|
||||
"go.uber.org/zap"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stefanprodan/k8s-podinfo/pkg/fscache"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -80,6 +81,8 @@ func (s *Server) registerHandlers() {
|
||||
s.router.HandleFunc("/api/info", s.infoHandler).Methods("GET")
|
||||
s.router.HandleFunc("/api/echo", s.echoHandler).Methods("POST")
|
||||
s.router.HandleFunc("/ws/echo", s.echoWsHandler)
|
||||
s.router.HandleFunc("/chunked", s.chunkedHandler)
|
||||
s.router.HandleFunc("/chunked/{wait:[0-9]+}", s.chunkedHandler)
|
||||
}
|
||||
|
||||
func (s *Server) registerMiddlewares() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package version
|
||||
|
||||
var VERSION = "1.7.0"
|
||||
var VERSION = "1.8.0"
|
||||
var REVISION = "unknown"
|
||||
|
||||
67
ui/vue.html
67
ui/vue.html
@@ -44,73 +44,6 @@
|
||||
</v-layout>
|
||||
</v-parallax>
|
||||
</section>
|
||||
<section>
|
||||
<v-layout
|
||||
column
|
||||
wrap
|
||||
class="my-5"
|
||||
align-center
|
||||
>
|
||||
<v-flex xs12 sm4 class="my-3">
|
||||
<div class="text-xs-center">
|
||||
<h2 class="headline">The best way to start developing</h2>
|
||||
<span class="subheading">
|
||||
stateless microservices with Go for Kubernetes
|
||||
</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs12>
|
||||
<v-container grid-list-xl>
|
||||
<v-layout row wrap align-center>
|
||||
<v-flex xs12 md4>
|
||||
<v-card class="elevation-0 transparent">
|
||||
<v-card-text class="text-xs-center">
|
||||
<v-icon x-large class="blue--text text--lighten-2">cloud</v-icon>
|
||||
</v-card-text>
|
||||
<v-card-title primary-title class="layout justify-center">
|
||||
<div class="headline text-xs-center">Cloud Native</div>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
Distributed as a Helm chart. Builtin Kubernetes health checks (readiness and liveness).
|
||||
Graceful shutdown on interrupt signals.
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-flex>
|
||||
<v-flex xs12 md4>
|
||||
<v-card class="elevation-0 transparent">
|
||||
<v-card-text class="text-xs-center">
|
||||
<v-icon x-large class="blue--text text--lighten-2">graphic_eq</v-icon>
|
||||
</v-card-text>
|
||||
<v-card-title primary-title class="layout justify-center">
|
||||
<div class="headline">Observability</div>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
Instrumentation with Prometheus (RED method).
|
||||
Structured logging with zap and Fluentd.
|
||||
Tracing with Jaeger and Istio.
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-flex>
|
||||
<v-flex xs12 md4>
|
||||
<v-card class="elevation-0 transparent">
|
||||
<v-card-text class="text-xs-center">
|
||||
<v-icon x-large class="blue--text text--lighten-2">flash_on</v-icon>
|
||||
</v-card-text>
|
||||
<v-card-title primary-title class="layout justify-center">
|
||||
<div class="headline text-xs-center">Release automation</div>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
Multi-platform Docker image AMD64 and ARMv7.
|
||||
CI/CD powered by: TravisCI CircleCI Quay.io Google Cloud Container Builder Skaffold Weave Flux.
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-container>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</section>
|
||||
|
||||
<v-footer class="blue darken-2">
|
||||
<v-layout row wrap align-center>
|
||||
<v-flex xs12>
|
||||
|
||||
Reference in New Issue
Block a user