Move ping to api/echo

This commit is contained in:
Stefan Prodan
2018-08-16 00:05:32 +03:00
parent d1574a6601
commit cf8531c224
4 changed files with 136 additions and 31 deletions
+124
View File
@@ -1,11 +1,16 @@
package server
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"time"
"github.com/rs/zerolog/log"
"github.com/stefanprodan/k8s-podinfo/pkg/version"
)
@@ -52,3 +57,122 @@ func (s *Server) apiInfo(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write(d)
}
func (s *Server) apiEcho(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/api/echo" && r.Method != http.MethodPost {
w.WriteHeader(http.StatusNotFound)
return
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Error().Msgf("Reading the request body failed: %v", err)
jsonError(w, "invalid request body", http.StatusBadRequest)
return
}
backendURL := os.Getenv("backendURL")
if len(backendURL) > 0 {
backendReq, err := http.NewRequest("POST", backendURL, bytes.NewReader(body))
if err != nil {
log.Error().Err(err).Msgf("%v backend call failed", r.URL.Path)
jsonError(w, "backend call failed", http.StatusInternalServerError)
return
}
// forward headers
copyTracingHeaders(r, backendReq)
setVersionHeaders(backendReq)
// TODO: make the timeout configurable
ctx, cancel := context.WithTimeout(backendReq.Context(), 2*time.Minute)
defer cancel()
// call backend
resp, err := http.DefaultClient.Do(backendReq.WithContext(ctx))
if err != nil {
log.Error().Msgf("%v backend call failed", r.URL.Path)
jsonError(w, "backend call failed", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
// copy error status from backend and exit
if resp.StatusCode >= 400 {
w.WriteHeader(resp.StatusCode)
return
}
// forward the received body
rbody, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error().Err(err).Msgf("%v reading the backend request body failed", r.URL.Path)
jsonError(w, "backend call failed", http.StatusInternalServerError)
return
}
// set logLevel=info when load testing
log.Debug().Msgf("Payload received %v from backend: %s", r.URL.Path, string(rbody))
setResponseHeaders(w)
w.Write(rbody)
} else {
setResponseHeaders(w)
w.Write(body)
}
}
func copyTracingHeaders(from *http.Request, to *http.Request) {
headers := []string{
"x-request-id",
"x-b3-traceid",
"x-b3-spanid",
"x-b3-parentspanid",
"x-b3-sampled",
"x-b3-flags",
"x-ot-span-context",
}
for i := range headers {
headerValue := from.Header.Get(headers[i])
if len(headerValue) > 0 {
to.Header.Set(headers[i], headerValue)
}
}
}
func setVersionHeaders(r *http.Request) {
r.Header.Set("X-API-Version", version.VERSION)
r.Header.Set("X-API-Revision", version.GITCOMMIT)
}
func setResponseHeaders(w http.ResponseWriter) {
color := os.Getenv("color")
if len(color) < 1 {
color = "blue"
}
w.Header().Set("X-Color", color)
w.WriteHeader(http.StatusAccepted)
}
func jsonError(w http.ResponseWriter, error string, code int) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(code)
data := struct {
Code int `json:"code"`
Message string `json:"message"`
}{
Code: code,
Message: error,
}
body, err := json.Marshal(data)
if err != nil {
log.Debug().Err(err).Msg("jsonError marshal failed")
} else {
w.Write(body)
}
}
+4 -30
View File
@@ -104,27 +104,6 @@ func (s *Server) echoHeaders(w http.ResponseWriter, r *http.Request) {
w.Write(d)
}
func copyTracingHeaders(from *http.Request, to *http.Request) {
headers := []string{
"x-request-id",
"x-b3-traceid",
"x-b3-spanid",
"x-b3-parentspanid",
"x-b3-sampled",
"x-b3-flags",
"x-ot-span-context",
}
for i := range headers {
headerValue := from.Header.Get(headers[i])
if len(headerValue) > 0 {
to.Header.Set(headers[i], headerValue)
}
}
to.Header.Set("X-API-Version", version.VERSION)
}
func (s *Server) backend(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "POST":
@@ -136,7 +115,7 @@ func (s *Server) backend(w http.ResponseWriter, r *http.Request) {
return
}
backendURL := os.Getenv("backend_url")
backendURL := os.Getenv("backendURL")
if len(backendURL) > 0 {
backendReq, err := http.NewRequest("POST", backendURL, bytes.NewReader(body))
if err != nil {
@@ -148,6 +127,7 @@ func (s *Server) backend(w http.ResponseWriter, r *http.Request) {
// forward tracing headers
copyTracingHeaders(r, backendReq)
setVersionHeaders(backendReq)
resp, err := http.DefaultClient.Do(backendReq)
if err != nil {
@@ -170,17 +150,11 @@ func (s *Server) backend(w http.ResponseWriter, r *http.Request) {
}
log.Debug().Msgf("Payload received from backend: %s", string(rbody))
color := os.Getenv("color")
if len(color) < 1 {
color = "blue"
}
w.Header().Set("X-Color", color)
w.WriteHeader(http.StatusAccepted)
setResponseHeaders(w)
w.Write(rbody)
} else {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Backend not specified, set backend_url env var"))
w.Write([]byte("Backend not specified, set backendURL env var"))
}
default:
w.WriteHeader(http.StatusNotAcceptable)
+7
View File
@@ -59,6 +59,7 @@ func NewServer(options ...func(*Server)) *Server {
// API
s.mux.HandleFunc("/api/info", s.apiInfo)
s.mux.HandleFunc("/api/echo", s.apiEcho)
return s
}
@@ -118,6 +119,12 @@ func ListenAndServe(port string, timeout time.Duration, stopCh <-chan struct{})
log.Info().Msgf("Shutting down HTTP server with timeout: %v", timeout)
// wait for Kubernetes readiness probe
// to remove this instance from the load balancer
// the readiness check interval must lower than the timeout
time.Sleep(timeout)
// attempt graceful shutdown
if err := srv.Shutdown(ctx); err != nil {
log.Error().Err(err).Msg("HTTP server graceful shutdown failed")
} else {
+1 -1
View File
@@ -166,7 +166,7 @@
},
postBackend: function() {
var self = this
fetch("/backend", {
fetch("/api/echo", {
method: 'post',
headers: {
"Content-type": "application/json; charset=UTF-8",