Merge pull request #36 from stefanprodan/backends

Add support for multiple backends
This commit is contained in:
Stefan Prodan
2019-09-27 12:16:28 +03:00
committed by GitHub
11 changed files with 79 additions and 58 deletions

View File

@@ -10,7 +10,7 @@ GIT_COMMIT:=$(shell git describe --dirty --always)
VERSION:=$(shell grep 'VERSION' pkg/version/version.go | awk '{ print $$4 }' | tr -d '"')
run:
GO111MODULE=on go run -ldflags "-s -w -X github.com/stefanprodan/podinfo/pkg/version.REVISION=$(GIT_COMMIT)" cmd/podinfo/* --level=debug --grpc-port=9999
GO111MODULE=on go run -ldflags "-s -w -X github.com/stefanprodan/podinfo/pkg/version.REVISION=$(GIT_COMMIT)" cmd/podinfo/* --level=debug --grpc-port=9999 --backend-url=https://httpbin.org/status/401 --backend-url=https://httpbin.org/status/500
test:
GO111MODULE=on go test -v -race ./...

View File

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

View File

@@ -33,6 +33,7 @@ Parameter | Description | Default
--- | --- | ---
`affinity` | node/pod affinities | None
`backend` | echo backend URL | None
`backends` | echo backend URL array | None
`faults.delay` | random HTTP response delays between 0 and 5 seconds | `false`
`faults.error` | 1/3 chances of a random HTTP response error | `false`
`hpa.enabled` | enables HPA | `false`

View File

@@ -48,6 +48,9 @@ spec:
{{- if .Values.service.grpcService }}
- --grpc-service-name={{ .Values.service.grpcService }}
{{- end }}
{{- range .Values.backends }}
- --backend-url={{ . }}
{{- end }}
- --level={{ .Values.logLevel }}
- --random-delay={{ .Values.faults.delay }}
- --random-error={{ .Values.faults.error }}

View File

@@ -3,6 +3,7 @@
replicaCount: 1
logLevel: info
backend: #http://backend-podinfo:9898/echo
backends: []
message: #UI greetings
faults:
@@ -11,7 +12,7 @@ faults:
image:
repository: stefanprodan/podinfo
tag: 3.0.0
tag: 3.1.0
pullPolicy: IfNotPresent
service:

View File

@@ -28,7 +28,7 @@ func main() {
fs.Int("grpc-port", 0, "gRPC port")
fs.String("grpc-service-name", "podinfo", "gPRC service name")
fs.String("level", "info", "log level debug, info, warn, error, flat or panic")
fs.String("backend-url", "", "backend service URL")
fs.StringSlice("backend-url", []string{}, "backend service URL")
fs.Duration("http-client-timeout", 2*time.Minute, "client timeout duration")
fs.Duration("http-server-timeout", 30*time.Second, "server read and write timeout duration")
fs.Duration("http-server-shutdown-timeout", 5*time.Second, "server graceful shutdown timeout duration")

View File

@@ -25,7 +25,7 @@ spec:
spec:
containers:
- name: podinfod
image: stefanprodan/podinfo:3.0.0
image: stefanprodan/podinfo:3.1.0
imagePullPolicy: IfNotPresent
ports:
- name: http

View File

@@ -3,8 +3,10 @@ package api
import (
"bytes"
"context"
"fmt"
"io/ioutil"
"net/http"
"sync"
"github.com/stefanprodan/podinfo/pkg/version"
"go.uber.org/zap"
@@ -26,59 +28,68 @@ func (s *Server) echoHandler(w http.ResponseWriter, r *http.Request) {
return
}
defer r.Body.Close()
if len(s.config.BackendURL) > 0 {
backendReq, err := http.NewRequest("POST", s.config.BackendURL, bytes.NewReader(body))
if err != nil {
s.logger.Error("backend call failed", zap.Error(err), zap.String("url", s.config.BackendURL))
s.ErrorResponse(w, r, "backend call failed", http.StatusInternalServerError)
return
result := make([]string, len(s.config.BackendURL))
var wg sync.WaitGroup
wg.Add(len(s.config.BackendURL))
for i, b := range s.config.BackendURL {
go func(index int, backend string) {
defer wg.Done()
backendReq, err := http.NewRequest("POST", backend, bytes.NewReader(body))
if err != nil {
s.logger.Error("backend call failed", zap.Error(err), zap.String("url", backend))
return
}
// forward headers
copyTracingHeaders(r, backendReq)
backendReq.Header.Set("X-API-Version", version.VERSION)
backendReq.Header.Set("X-API-Revision", version.REVISION)
ctx, cancel := context.WithTimeout(backendReq.Context(), s.config.HttpClientTimeout)
defer cancel()
// call backend
resp, err := http.DefaultClient.Do(backendReq.WithContext(ctx))
if err != nil {
s.logger.Error("backend call failed", zap.Error(err), zap.String("url", backend))
result[index] = fmt.Sprintf("backend %v call failed %v", backend, err)
return
}
defer resp.Body.Close()
// copy error status from backend and exit
if resp.StatusCode >= 400 {
s.logger.Error("backend call failed", zap.Int("status", resp.StatusCode), zap.String("url", backend))
result[index] = fmt.Sprintf("backend %v response status code %v", backend, resp.StatusCode)
return
}
// forward the received body
rbody, err := ioutil.ReadAll(resp.Body)
if err != nil {
s.logger.Error(
"reading the backend request body failed",
zap.Error(err),
zap.String("url", backend))
result[index] = fmt.Sprintf("backend %v call failed %v", backend, err)
return
}
s.logger.Debug(
"payload received from backend",
zap.String("response", string(rbody)),
zap.String("url", backend))
result[index] = string(rbody)
}(i, b)
}
// forward headers
copyTracingHeaders(r, backendReq)
backendReq.Header.Set("X-API-Version", version.VERSION)
backendReq.Header.Set("X-API-Revision", version.REVISION)
ctx, cancel := context.WithTimeout(backendReq.Context(), s.config.HttpClientTimeout)
defer cancel()
// call backend
resp, err := http.DefaultClient.Do(backendReq.WithContext(ctx))
if err != nil {
s.logger.Error("backend call failed", zap.Error(err), zap.String("url", s.config.BackendURL))
s.ErrorResponse(w, r, "backend call failed", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
// copy error status from backend and exit
if resp.StatusCode >= 400 {
s.ErrorResponse(w, r, "backend error", resp.StatusCode)
return
}
// forward the received body
rbody, err := ioutil.ReadAll(resp.Body)
if err != nil {
s.logger.Error(
"reading the backend request body failed",
zap.Error(err),
zap.String("url", s.config.BackendURL))
s.ErrorResponse(w, r, "backend call failed", http.StatusInternalServerError)
return
}
s.logger.Debug(
"payload received from backend",
zap.String("response", string(rbody)),
zap.String("url", s.config.BackendURL))
wg.Wait()
w.Header().Set("X-Color", s.config.UIColor)
w.WriteHeader(http.StatusAccepted)
w.Write(rbody)
s.JSONResponse(w, r, result)
} else {
w.Header().Set("X-Color", s.config.UIColor)
w.WriteHeader(http.StatusAccepted)

View File

@@ -12,7 +12,7 @@ func NewMockServer() *Server {
Port: "9898",
HttpServerShutdownTimeout: 5 * time.Second,
HttpServerTimeout: 30 * time.Second,
BackendURL: "",
BackendURL: []string{},
ConfigPath: "/config",
DataPath: "/data",
HttpClientTimeout: 30 * time.Second,

View File

@@ -40,11 +40,16 @@ var (
watcher *fscache.Watcher
)
type FluxConfig struct {
GitUrl string `mapstructure:"git-url"`
GitBranch string `mapstructure:"git-branch"`
}
type Config struct {
HttpClientTimeout time.Duration `mapstructure:"http-client-timeout"`
HttpServerTimeout time.Duration `mapstructure:"http-server-timeout"`
HttpServerShutdownTimeout time.Duration `mapstructure:"http-server-shutdown-timeout"`
BackendURL string `mapstructure:"backend-url"`
BackendURL []string `mapstructure:"backend-url"`
UIMessage string `mapstructure:"ui-message"`
UIColor string `mapstructure:"ui-color"`
UIPath string `mapstructure:"ui-path"`

View File

@@ -1,4 +1,4 @@
package version
var VERSION = "3.0.0"
var VERSION = "3.1.0"
var REVISION = "unknown"