From af452047d32bd88971307146814e5e82b2bbabda Mon Sep 17 00:00:00 2001 From: Karsten Jeschkies Date: Fri, 9 Jul 2021 16:00:47 +0200 Subject: [PATCH] Return build info under `/loki/api/v1/status/buildinfo`. (#3972) * Return build info under `/version`. Closes #3221 * Update documentation. * Correct docs. * Format version handler code. * Ignore write error. * Sort imports. * Use `PrometheusVersion` struct. * Set Go version. --- docs/sources/api/_index.md | 7 +++++ pkg/loki/loki.go | 5 +++- pkg/loki/version_handler.go | 31 ++++++++++++++++++++++ pkg/loki/version_handler_test.go | 41 +++++++++++++++++++++++++++++ pkg/querier/queryrange/roundtrip.go | 2 +- pkg/util/build/build.go | 8 +++++- 6 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 pkg/loki/version_handler.go create mode 100644 pkg/loki/version_handler_test.go diff --git a/docs/sources/api/_index.md b/docs/sources/api/_index.md index 97b8ac6963..24b6d2c752 100644 --- a/docs/sources/api/_index.md +++ b/docs/sources/api/_index.md @@ -19,6 +19,7 @@ These endpoints are exposed by all components: - [`GET /ready`](#get-ready) - [`GET /metrics`](#get-metrics) - [`GET /config`](#get-config) +- [`GET /loki/api/v1/status/buildinfo`](#get-buildinfo) These endpoints are exposed by the querier and the frontend: @@ -823,6 +824,12 @@ and the current are returned. A value of `defaults` returns the default configur In microservices mode, the `/config` endpoint is exposed by all components. +## `GET buildinfo` + +`/loki/api/v1/status/buildinfo` exposes the build information in a JSON object. The fields are `version`, `revision`, `branch`, `buildDate`, `buildUser`, and `goVersion`. + +In microservices mode, the `/version` endpoint is exposed by all components. + ## Series The Series API is available under the following: diff --git a/pkg/loki/loki.go b/pkg/loki/loki.go index 592aa202c8..1d116682d2 100644 --- a/pkg/loki/loki.go +++ b/pkg/loki/loki.go @@ -261,7 +261,10 @@ func (t *Loki) Run() error { t.Server.HTTP.Path("/ready").Handler(t.readyHandler(sm)) // This adds a way to see the config and the changes compared to the defaults - t.Server.HTTP.Path("/config").HandlerFunc(configHandler(t.Cfg, newDefaultConfig())) + t.Server.HTTP.Path("/loki/api/v1/status/buildinfo").HandlerFunc(configHandler(t.Cfg, newDefaultConfig())) + + // Each component serves its version. + t.Server.HTTP.Path("/version").HandlerFunc(versionHandler()) t.Server.HTTP.Path("/debug/fgprof").Handler(fgprof.Handler()) diff --git a/pkg/loki/version_handler.go b/pkg/loki/version_handler.go new file mode 100644 index 0000000000..316d4825f7 --- /dev/null +++ b/pkg/loki/version_handler.go @@ -0,0 +1,31 @@ +package loki + +import ( + "encoding/json" + "net/http" + + prom "github.com/prometheus/prometheus/web/api/v1" + + "github.com/grafana/loki/pkg/util/build" +) + +func versionHandler() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + info := prom.PrometheusVersion{ + Version: build.Version, + Revision: build.Revision, + Branch: build.Branch, + BuildUser: build.BuildUser, + BuildDate: build.BuildDate, + GoVersion: build.GoVersion, + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + // We ignore errors here, because we cannot do anything about them. + // Write will trigger sending Status code, so we cannot send a different status code afterwards. + // Also this isn't internal error, but error communicating with client. + _ = json.NewEncoder(w).Encode(info) + } +} diff --git a/pkg/loki/version_handler_test.go b/pkg/loki/version_handler_test.go new file mode 100644 index 0000000000..083f611861 --- /dev/null +++ b/pkg/loki/version_handler_test.go @@ -0,0 +1,41 @@ +package loki + +import ( + "io/ioutil" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/grafana/loki/pkg/util/build" +) + +func TestVersionHandler(t *testing.T) { + build.Version = "0.0.1" + build.Branch = "main" + build.Revision = "foobar" + build.BuildDate = "yesterday" + build.BuildUser = "Turing" + build.GoVersion = "42" + + req := httptest.NewRequest("GET", "http://test.com/config?mode=diff", nil) + w := httptest.NewRecorder() + + h := versionHandler() + h(w, req) + resp := w.Result() + assert.Equal(t, http.StatusOK, resp.StatusCode) + + expected := `{ + "version":"0.0.1", + "branch":"main", + "buildDate":"yesterday", + "buildUser":"Turing", + "revision":"foobar", + "goVersion": "42" + }` + body, err := ioutil.ReadAll(resp.Body) + assert.NoError(t, err) + assert.JSONEq(t, expected, string(body)) +} diff --git a/pkg/querier/queryrange/roundtrip.go b/pkg/querier/queryrange/roundtrip.go index 758c576c7b..400e156726 100644 --- a/pkg/querier/queryrange/roundtrip.go +++ b/pkg/querier/queryrange/roundtrip.go @@ -254,7 +254,7 @@ func NewLogFilterTripperware( }, nil } -// NewSeriesripperware creates a new frontend tripperware responsible for handling series requests +// NewSeriesTripperware creates a new frontend tripperware responsible for handling series requests func NewSeriesTripperware( cfg Config, log log.Logger, diff --git a/pkg/util/build/build.go b/pkg/util/build/build.go index bab8f4d52e..9a6b75a8bc 100644 --- a/pkg/util/build/build.go +++ b/pkg/util/build/build.go @@ -1,6 +1,10 @@ package build -import "github.com/prometheus/common/version" +import ( + "runtime" + + "github.com/prometheus/common/version" +) // Version information passed to Prometheus version package. // Package path as used by linker changes based on vendoring being used or not, @@ -12,6 +16,7 @@ var ( Branch string BuildUser string BuildDate string + GoVersion string ) func init() { @@ -20,4 +25,5 @@ func init() { version.Branch = Branch version.BuildUser = BuildUser version.BuildDate = BuildDate + version.GoVersion = runtime.Version() }