From aca2c7fe2a518ceb90d79d94978440e9f8c51331 Mon Sep 17 00:00:00 2001 From: Edward Wilde Date: Mon, 2 Jul 2018 16:42:16 +0100 Subject: [PATCH] Enhance info endpoint to include gateway version Extend the health endpoint and add gateway version information Resolves: #733 Signed-off-by: Edward Wilde --- build.sh | 1 + gateway/Dockerfile | 10 +++- gateway/build.sh | 7 +++ gateway/handlers/infohandler.go | 56 ++++++++++++++++++ gateway/server.go | 3 +- gateway/tests/integration/infohandler_test.go | 48 +++++++++++++++ gateway/tests/integration/routes_test.go | 11 ++-- gateway/types/inforequest.go | 58 +++++++++++++++++++ gateway/version/version.go | 20 +++++++ watchdog/build.sh | 2 +- 10 files changed, 209 insertions(+), 7 deletions(-) create mode 100644 gateway/handlers/infohandler.go create mode 100644 gateway/tests/integration/infohandler_test.go create mode 100644 gateway/types/inforequest.go create mode 100644 gateway/version/version.go diff --git a/build.sh b/build.sh index 2854f245..a6c97f1b 100755 --- a/build.sh +++ b/build.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -e if [ ! -s "$TRAVIS_TAG" ] ; then echo "This build will be published under the tag: ${TRAVIS_TAG}" diff --git a/gateway/Dockerfile b/gateway/Dockerfile index 555a424e..50e1842d 100644 --- a/gateway/Dockerfile +++ b/gateway/Dockerfile @@ -1,4 +1,7 @@ FROM golang:1.9.7 as build +ARG GIT_COMMIT_SHA +ARG GIT_COMMIT_MESSAGE +ARG VERSION='dev' RUN curl -sL https://github.com/alexellis/license-check/releases/download/0.2.2/license-check \ > /usr/bin/license-check \ @@ -16,13 +19,18 @@ COPY tests tests COPY types types COPY queue queue COPY plugin plugin +COPY version version COPY server.go . # Run a gofmt and exclude all vendored code. RUN license-check -path ./ --verbose=false "Alex Ellis" "OpenFaaS Project" "OpenFaaS Authors" \ && test -z "$(gofmt -l $(find . -type f -name '*.go' -not -path "./vendor/*"))" \ && go test $(go list ./... | grep -v integration | grep -v /vendor/ | grep -v /template/) -cover \ - && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o gateway . + && CGO_ENABLED=0 GOOS=linux go build --ldflags "-s -w \ + -X github.com/openfaas/faas/gateway/version.GitCommitSHA=${GIT_COMMIT_SHA}\ + -X \"github.com/openfaas/faas/gateway/version.GitCommitMessage=${GIT_COMMIT_MESSAGE}\"\ + -X github.com/openfaas/faas/gateway/version.Version=${VERSION}" \ + -a -installsuffix cgo -o gateway . FROM alpine:3.7 diff --git a/gateway/build.sh b/gateway/build.sh index f5e93b1e..96c80fd0 100755 --- a/gateway/build.sh +++ b/gateway/build.sh @@ -1,4 +1,5 @@ #!/bin/sh +set -e export dockerfile="Dockerfile" export arch=$(uname -m) @@ -20,5 +21,11 @@ fi echo Building openfaas/gateway:$eTAG +GIT_COMMIT_MESSAGE=$(git log -1 --pretty=%B 2>&1 | head -n 1) +GIT_COMMIT_SHA=$(git rev-list -1 HEAD) +VERSION=$(git describe --all --exact-match `git rev-parse HEAD` | grep tags | sed 's/tags\///' || echo dev) + docker build --build-arg https_proxy=$https_proxy --build-arg http_proxy=$http_proxy \ + --build-arg GIT_COMMIT_MESSAGE="$GIT_COMMIT_MESSAGE" --build-arg GIT_COMMIT_SHA=$GIT_COMMIT_SHA \ + --build-arg VERSION=${VERSION:-dev} \ -t openfaas/gateway:$eTAG . -f $dockerfile --no-cache diff --git a/gateway/handlers/infohandler.go b/gateway/handlers/infohandler.go new file mode 100644 index 00000000..94a4eff8 --- /dev/null +++ b/gateway/handlers/infohandler.go @@ -0,0 +1,56 @@ +package handlers + +import ( + "encoding/json" + "log" + "net/http" + + "github.com/openfaas/faas/gateway/types" + "github.com/openfaas/faas/gateway/version" +) + +// MakeInfoHandler is responsible for display component version information +func MakeInfoHandler(h http.Handler) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + sw := types.NewStringResponseWriter() + h.ServeHTTP(sw, r) + + log.Printf("Body: %s", sw.Body()) + provider := make(map[string]interface{}) + providerVersion := &types.VersionInfo{} + + err := json.Unmarshal(sw.Body(), &provider) + if err != nil { + log.Printf("Error unmarshalling provider json. Got %s. Error %s\n", string(sw.Body()), err.Error()) + } + + versionMap := provider["version"].(map[string]interface{}) + providerVersion.SHA = versionMap["sha"].(string) + providerVersion.Release = versionMap["release"].(string) + + gatewayInfo := &types.GatewayInfo{ + Version: &types.VersionInfo{ + CommitMessage: version.GitCommitMessage, + Release: version.BuildVersion(), + SHA: version.GitCommitSHA, + }, + Provider: &types.ProviderInfo{ + Version: providerVersion, + Name: provider["provider"].(string), + Orchestration: provider["orchestration"].(string), + }, + } + + jsonOut, marshalErr := json.Marshal(gatewayInfo) + if marshalErr != nil { + log.Printf("Error during unmarshal of gateway info request %s\n", marshalErr.Error()) + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write(jsonOut) + + } +} diff --git a/gateway/server.go b/gateway/server.go index 1c656344..99707142 100644 --- a/gateway/server.go +++ b/gateway/server.go @@ -118,7 +118,8 @@ func main() { r.HandleFunc("/function/{name:[-a-zA-Z_0-9]+}", faasHandlers.Proxy) r.HandleFunc("/function/{name:[-a-zA-Z_0-9]+}/", faasHandlers.Proxy) - r.HandleFunc("/system/info", handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver)).Methods(http.MethodGet) + r.HandleFunc("/system/info", handlers.MakeInfoHandler(handlers.MakeForwardingProxyHandler( + reverseProxy, forwardingNotifiers, urlResolver))).Methods(http.MethodGet) r.HandleFunc("/system/alert", faasHandlers.Alert) diff --git a/gateway/tests/integration/infohandler_test.go b/gateway/tests/integration/infohandler_test.go new file mode 100644 index 00000000..a9f803d3 --- /dev/null +++ b/gateway/tests/integration/infohandler_test.go @@ -0,0 +1,48 @@ +package inttests + +import ( + "encoding/json" + "net/http" + "testing" + + "github.com/openfaas/faas/gateway/types" +) + +func Test_InfoEndpoint_Returns_200(t *testing.T) { + _, code, err := fireRequest("http://localhost:8080/system/info", http.MethodGet, "") + + if err != nil { + t.Log(err) + t.Fail() + } + + wantCode := http.StatusOK + if code != wantCode { + t.Errorf("status code, want: %d, got: %d", wantCode, code) + t.Fail() + } +} + +func Test_InfoEndpoint_Returns_Gateway_Version_SHA_And_Message(t *testing.T) { + body, _, err := fireRequest("http://localhost:8080/system/info", http.MethodGet, "") + + if err != nil { + t.Log(err) + t.Fail() + } + + gatewayInfo := &types.GatewayInfo{} + err = json.Unmarshal([]byte(body), gatewayInfo) + if err != nil { + t.Log(err) + t.Fail() + } + + if len(gatewayInfo.Version.SHA) != 40 { + t.Errorf("length of SHA incorrect, want: %d, got: %d. Json body was %s", 40, len(gatewayInfo.Version.SHA), body) + } + + if len(gatewayInfo.Version.CommitMessage) == 0 { + t.Errorf("length of commit message should be greater than 0. Json body was %s", body) + } +} diff --git a/gateway/tests/integration/routes_test.go b/gateway/tests/integration/routes_test.go index 5263f0fd..052ed2cc 100644 --- a/gateway/tests/integration/routes_test.go +++ b/gateway/tests/integration/routes_test.go @@ -50,7 +50,7 @@ func fireRequestWithHeaders(url string, method string, reqBody string, headers m func TestGet_Rejected(t *testing.T) { var reqBody string unsupportedMethod := http.MethodHead - _, code, err := fireRequest("http://localhost:8080/function/func_echoit", unsupportedMethod, reqBody) + _, code, err := fireRequest("http://localhost:8080/function/echoit", unsupportedMethod, reqBody) want := http.StatusMethodNotAllowed if code != want { t.Logf("Failed got: %d, wanted: %d", code, want) @@ -68,15 +68,18 @@ func TestEchoIt_Post_Route_Handler_ForwardsClientHeaders(t *testing.T) { headers := make(map[string]string, 0) headers["X-Api-Key"] = "123" - body, code, err := fireRequestWithHeaders("http://localhost:8080/function/func_echoit", http.MethodPost, reqBody, headers) + body, code, err := fireRequestWithHeaders("http://localhost:8080/function/echoit", http.MethodPost, reqBody, headers) if err != nil { t.Log(err) t.Fail() } + if code != http.StatusOK { - t.Log("Failed") + t.Logf("Failed, code: %d, body:%s", code, body) + t.Fail() } + if body != reqBody { t.Log("Expected body returned") t.Fail() @@ -85,7 +88,7 @@ func TestEchoIt_Post_Route_Handler_ForwardsClientHeaders(t *testing.T) { func TestEchoIt_Post_Route_Handler(t *testing.T) { reqBody := "test message" - body, code, err := fireRequest("http://localhost:8080/function/func_echoit", http.MethodPost, reqBody) + body, code, err := fireRequest("http://localhost:8080/function/echoit", http.MethodPost, reqBody) if err != nil { t.Log(err) diff --git a/gateway/types/inforequest.go b/gateway/types/inforequest.go new file mode 100644 index 00000000..4e1e08c7 --- /dev/null +++ b/gateway/types/inforequest.go @@ -0,0 +1,58 @@ +package types + +import ( + "bytes" + "net/http" +) + +// GatewayInfo provides information about the gateway and it's connected components +type GatewayInfo struct { + Provider *ProviderInfo `json:"provider"` + Version *VersionInfo `json:"version"` +} + +// ProviderInfo provides information about the configured provider +type ProviderInfo struct { + Name string `json:"provider"` + Version *VersionInfo `json:"version"` + Orchestration string `json:"orchestration"` +} + +// VersionInfo provides the commit message, sha and release version number +type VersionInfo struct { + CommitMessage string `json:"commit_message,omitempty"` + SHA string `json:"sha"` + Release string `json:"release"` +} + +// StringResponseWriter captures the handlers HTTP response in a buffer +type StringResponseWriter struct { + body *bytes.Buffer + headerCode int + header http.Header +} + +// NewStringResponseWriter create a new StringResponseWriter +func NewStringResponseWriter() *StringResponseWriter { + return &StringResponseWriter{body: &bytes.Buffer{}, header: make(http.Header)} +} + +// Header capture the Header information +func (s StringResponseWriter) Header() http.Header { + return s.header +} + +// Write captures the response data +func (s StringResponseWriter) Write(data []byte) (int, error) { + return s.body.Write(data) +} + +// WriteHeader captures the status code of the response +func (s StringResponseWriter) WriteHeader(statusCode int) { + s.headerCode = statusCode +} + +// Body returns the response body bytes +func (s StringResponseWriter) Body() []byte { + return s.body.Bytes() +} diff --git a/gateway/version/version.go b/gateway/version/version.go new file mode 100644 index 00000000..16093fe4 --- /dev/null +++ b/gateway/version/version.go @@ -0,0 +1,20 @@ +package version + +var ( + //Version release version of the provider + Version string + //GitCommit SHA of the last git commit + GitCommitSHA string + //GitCommit message of the last commit + GitCommitMessage string + //DevVersion string for the development version + DevVersion = "dev" +) + +//BuildVersion returns current version of the provider +func BuildVersion() string { + if len(Version) == 0 { + return DevVersion + } + return Version +} diff --git a/watchdog/build.sh b/watchdog/build.sh index 80efb2ce..c6e1c670 100755 --- a/watchdog/build.sh +++ b/watchdog/build.sh @@ -1,5 +1,5 @@ #!/bin/sh - +set -e export arch=$(uname -m) if [ "$arch" = "armv7l" ] ; then