Files
podinfo/pkg/server/api.go
2018-08-17 15:38:35 +03:00

179 lines
4.1 KiB
Go

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"
)
func (s *Server) apiInfo(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/api/info" && r.Method != http.MethodGet {
w.WriteHeader(http.StatusNotFound)
return
}
host, _ := os.Hostname()
color := os.Getenv("color")
if len(color) < 1 {
color = "blue"
}
msg := os.Getenv("message")
if len(msg) < 1 {
msg = fmt.Sprintf("Greetings from podinfo v%v", version.VERSION)
}
data := struct {
Message string `json:"message"`
Version string `json:"version"`
Revision string `json:"revision"`
Hostname string `json:"hostname"`
Color string `json:"color"`
}{
Message: msg,
Version: version.VERSION,
Revision: version.GITCOMMIT,
Hostname: host,
Color: color,
}
d, err := json.Marshal(data)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
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().Err(err).Msgf("backend call to %s failed", backendURL)
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)
}
}