From a12d0a1ed7ad7ec91909f86db031324270f7460e Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Fri, 27 Sep 2019 11:52:22 +0300 Subject: [PATCH 1/2] Add support for multiple backends When calling /echo, the backends requests will be run in parallel and the results are aggregated and returned to the caller as a json array --- Makefile | 2 +- charts/podinfo/README.md | 1 + charts/podinfo/templates/deployment.yaml | 3 + charts/podinfo/values.yaml | 1 + cmd/podinfo/main.go | 2 +- pkg/api/echo.go | 109 +++++++++++++---------- pkg/api/mock.go | 2 +- pkg/api/server.go | 7 +- 8 files changed, 74 insertions(+), 53 deletions(-) diff --git a/Makefile b/Makefile index 134e64f..77f0482 100644 --- a/Makefile +++ b/Makefile @@ -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 ./... diff --git a/charts/podinfo/README.md b/charts/podinfo/README.md index 8dc7d73..f142e13 100644 --- a/charts/podinfo/README.md +++ b/charts/podinfo/README.md @@ -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` diff --git a/charts/podinfo/templates/deployment.yaml b/charts/podinfo/templates/deployment.yaml index 218788d..741fcba 100644 --- a/charts/podinfo/templates/deployment.yaml +++ b/charts/podinfo/templates/deployment.yaml @@ -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 }} diff --git a/charts/podinfo/values.yaml b/charts/podinfo/values.yaml index cd4083f..de8c179 100644 --- a/charts/podinfo/values.yaml +++ b/charts/podinfo/values.yaml @@ -3,6 +3,7 @@ replicaCount: 1 logLevel: info backend: #http://backend-podinfo:9898/echo +backends: [] message: #UI greetings faults: diff --git a/cmd/podinfo/main.go b/cmd/podinfo/main.go index 5acc75d..5418558 100644 --- a/cmd/podinfo/main.go +++ b/cmd/podinfo/main.go @@ -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") diff --git a/pkg/api/echo.go b/pkg/api/echo.go index 0eb2694..899d9b0 100644 --- a/pkg/api/echo.go +++ b/pkg/api/echo.go @@ -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) diff --git a/pkg/api/mock.go b/pkg/api/mock.go index 327c11f..ae04725 100644 --- a/pkg/api/mock.go +++ b/pkg/api/mock.go @@ -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, diff --git a/pkg/api/server.go b/pkg/api/server.go index 9367391..9f76402 100644 --- a/pkg/api/server.go +++ b/pkg/api/server.go @@ -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"` From 56b404bd84c14af6cab46e79928eb86d291b57cd Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Fri, 27 Sep 2019 12:10:29 +0300 Subject: [PATCH 2/2] Release v3.1.0 --- charts/podinfo/Chart.yaml | 4 ++-- charts/podinfo/values.yaml | 2 +- kustomize/deployment.yaml | 2 +- pkg/version/version.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/charts/podinfo/Chart.yaml b/charts/podinfo/Chart.yaml index a0215d3..5d17ec3 100644 --- a/charts/podinfo/Chart.yaml +++ b/charts/podinfo/Chart.yaml @@ -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 diff --git a/charts/podinfo/values.yaml b/charts/podinfo/values.yaml index de8c179..f5ace6c 100644 --- a/charts/podinfo/values.yaml +++ b/charts/podinfo/values.yaml @@ -12,7 +12,7 @@ faults: image: repository: stefanprodan/podinfo - tag: 3.0.0 + tag: 3.1.0 pullPolicy: IfNotPresent service: diff --git a/kustomize/deployment.yaml b/kustomize/deployment.yaml index ff14f77..b9191c0 100644 --- a/kustomize/deployment.yaml +++ b/kustomize/deployment.yaml @@ -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 diff --git a/pkg/version/version.go b/pkg/version/version.go index f43a4c8..b097d6e 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -1,4 +1,4 @@ package version -var VERSION = "3.0.0" +var VERSION = "3.1.0" var REVISION = "unknown"