From 95ffcdc7235e46d72430ba256b7a9c9ae530a2c2 Mon Sep 17 00:00:00 2001 From: Brett Logan Date: Mon, 11 Nov 2019 20:21:04 -0500 Subject: [PATCH] [FAB-15814] Add endpoint for versioning metadata Adds a /version endpoint to the operations server that serves the Version and CommitSHA from the common/metadata package Change-Id: Ic16e044f989bf9bdf827637412217fb07a8394e3 Signed-off-by: Brett Logan (cherry picked from commit b66fc3351522b7bacc1d813c10487cb80e0b16bc) --- core/operations/system.go | 10 +++++++ core/operations/system_test.go | 11 ++++++++ core/operations/version.go | 50 +++++++++++++++++++++++++++++++++ core/operations/version_test.go | 43 ++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 core/operations/version.go create mode 100644 core/operations/version_test.go diff --git a/core/operations/system.go b/core/operations/system.go index acf3d2e5828..79315bbe9d7 100644 --- a/core/operations/system.go +++ b/core/operations/system.go @@ -19,6 +19,7 @@ import ( "github.com/hyperledger/fabric-lib-go/healthz" "github.com/hyperledger/fabric/common/flogging" "github.com/hyperledger/fabric/common/flogging/httpadmin" + "github.com/hyperledger/fabric/common/metadata" "github.com/hyperledger/fabric/common/metrics" "github.com/hyperledger/fabric/common/metrics/disabled" "github.com/hyperledger/fabric/common/metrics/prometheus" @@ -86,6 +87,7 @@ func NewSystem(o Options) *System { system.initializeHealthCheckHandler() system.initializeLoggingHandler() system.initializeMetricsProvider() + system.initializeVersionInfoHandler() return system } @@ -201,6 +203,14 @@ func (s *System) initializeHealthCheckHandler() { s.mux.Handle("/healthz", s.handlerChain(s.healthHandler, false)) } +func (s *System) initializeVersionInfoHandler() { + versionInfo := &VersionInfoHandler{ + CommitSHA: metadata.CommitSHA, + Version: metadata.Version, + } + s.mux.Handle("/version", s.handlerChain(versionInfo, false)) +} + func (s *System) startMetricsTickers() error { m := s.options.Metrics if s.statsd != nil { diff --git a/core/operations/system_test.go b/core/operations/system_test.go index 9479f04d48f..430c9aa5089 100644 --- a/core/operations/system_test.go +++ b/core/operations/system_test.go @@ -78,6 +78,17 @@ var _ = Describe("System", func() { } }) + It("hosts an unsecured endpoint for the version information", func() { + err := system.Start() + Expect(err).NotTo(HaveOccurred()) + + versionURL := fmt.Sprintf("https://%s/version", system.Addr()) + resp, err := client.Get(versionURL) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + resp.Body.Close() + }) + It("hosts a secure endpoint for logging", func() { err := system.Start() Expect(err).NotTo(HaveOccurred()) diff --git a/core/operations/version.go b/core/operations/version.go new file mode 100644 index 00000000000..50703436a82 --- /dev/null +++ b/core/operations/version.go @@ -0,0 +1,50 @@ +/* +Copyright IBM Corp All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package operations + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/hyperledger/fabric/common/flogging" +) + +type VersionInfoHandler struct { + CommitSHA string `json:"CommitSHA,omitempty"` + Version string `json:"Version,omitempty"` +} + +func (m *VersionInfoHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) { + switch req.Method { + case http.MethodGet: + m.sendResponse(resp, http.StatusOK, m) + default: + err := fmt.Errorf("invalid request method: %s", req.Method) + m.sendResponse(resp, http.StatusBadRequest, err) + } +} + +type errorResponse struct { + Error string `json:"Error"` +} + +func (m *VersionInfoHandler) sendResponse(resp http.ResponseWriter, code int, payload interface{}) { + if err, ok := payload.(error); ok { + payload = &errorResponse{Error: err.Error()} + } + js, err := json.Marshal(payload) + if err != nil { + logger := flogging.MustGetLogger("operations.runner") + logger.Errorw("failed to encode payload", "error", err) + resp.WriteHeader(http.StatusInternalServerError) + } else { + resp.WriteHeader(code) + resp.Header().Set("Content-Type", "application/json") + resp.Write(js) + } +} diff --git a/core/operations/version_test.go b/core/operations/version_test.go new file mode 100644 index 00000000000..b0424dfae1c --- /dev/null +++ b/core/operations/version_test.go @@ -0,0 +1,43 @@ +/* +Copyright IBM Corp All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package operations + +import ( + "net/http" + "net/http/httptest" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Version", func() { + It("returns 200 if the method is GET", func() { + resp := httptest.NewRecorder() + + versionInfoHandler := &VersionInfoHandler{Version: "latest"} + versionInfoHandler.ServeHTTP(resp, &http.Request{Method: http.MethodGet}) + Expect(resp.Result().StatusCode).To(Equal(http.StatusOK)) + Expect(resp.Body).To(MatchJSON(`{"Version": "latest"}`)) + }) + + It("returns 400 when an unsupported method is used", func() { + resp := httptest.NewRecorder() + + versionInfoHandler := &VersionInfoHandler{} + versionInfoHandler.ServeHTTP(resp, &http.Request{Method: http.MethodPut}) + Expect(resp.Result().StatusCode).To(Equal(http.StatusBadRequest)) + Expect(resp.Body).To(MatchJSON(`{"Error": "invalid request method: PUT"}`)) + }) + + It("returns 500 when the payload is invalid JSON", func() { + resp := httptest.NewRecorder() + + versionInfoHandler := &VersionInfoHandler{} + versionInfoHandler.sendResponse(resp, 200, make(chan int)) + Expect(resp.Result().StatusCode).To(Equal(http.StatusInternalServerError)) + }) +})