diff --git a/CHANGELOG.md b/CHANGELOG.md index 469757f4515..e3798a71fc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## master / unreleased +* [FEATURE] Querier/Query Frontend: support Prometheus /api/v1/status/buildinfo API. #4978 ## 1.14.0 in progress diff --git a/pkg/api/api.go b/pkg/api/api.go index 2e4d6645ba7..75aadfd5e20 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -352,7 +352,7 @@ type Distributor interface { UserStatsHandler(w http.ResponseWriter, r *http.Request) } -// RegisterQueryable registers the the default routes associated with the querier +// RegisterQueryable registers the default routes associated with the querier // module. func (a *API) RegisterQueryable( queryable storage.SampleAndChunkQueryable, @@ -374,6 +374,7 @@ func (a *API) RegisterQueryAPI(handler http.Handler) { a.RegisterRoute(path.Join(a.cfg.PrometheusHTTPPrefix, "/api/v1/label/{name}/values"), handler, true, "GET") a.RegisterRoute(path.Join(a.cfg.PrometheusHTTPPrefix, "/api/v1/series"), handler, true, "GET", "POST", "DELETE") a.RegisterRoute(path.Join(a.cfg.PrometheusHTTPPrefix, "/api/v1/metadata"), handler, true, "GET") + a.RegisterRoute(path.Join(a.cfg.PrometheusHTTPPrefix, "/api/v1/status/buildinfo"), handler, true, "GET") // Register Legacy Routers a.RegisterRoute(path.Join(a.cfg.LegacyHTTPPrefix, "/api/v1/read"), handler, true, "POST") @@ -384,6 +385,7 @@ func (a *API) RegisterQueryAPI(handler http.Handler) { a.RegisterRoute(path.Join(a.cfg.LegacyHTTPPrefix, "/api/v1/label/{name}/values"), handler, true, "GET") a.RegisterRoute(path.Join(a.cfg.LegacyHTTPPrefix, "/api/v1/series"), handler, true, "GET", "POST", "DELETE") a.RegisterRoute(path.Join(a.cfg.LegacyHTTPPrefix, "/api/v1/metadata"), handler, true, "GET") + a.RegisterRoute(path.Join(a.cfg.LegacyHTTPPrefix, "/api/v1/status/buildinfo"), handler, true, "GET") } // RegisterQueryFrontend registers the Prometheus routes supported by the diff --git a/pkg/api/handlers.go b/pkg/api/handlers.go index af3e65a01f3..1b8528301b0 100644 --- a/pkg/api/handlers.go +++ b/pkg/api/handlers.go @@ -15,6 +15,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" dto "github.com/prometheus/client_model/go" "github.com/prometheus/common/route" + "github.com/prometheus/common/version" "github.com/prometheus/prometheus/config" "github.com/prometheus/prometheus/storage" v1 "github.com/prometheus/prometheus/web/api/v1" @@ -211,7 +212,14 @@ func NewQuerierHandler( false, regexp.MustCompile(".*"), func() (v1.RuntimeInfo, error) { return v1.RuntimeInfo{}, errors.New("not implemented") }, - &v1.PrometheusVersion{}, + &v1.PrometheusVersion{ + Version: version.Version, + Branch: version.Branch, + Revision: version.Revision, + BuildUser: version.BuildUser, + BuildDate: version.BuildDate, + GoVersion: version.GoVersion, + }, // This is used for the stats API which we should not support. Or find other ways to. prometheus.GathererFunc(func() ([]*dto.MetricFamily, error) { return nil, nil }), reg, @@ -255,6 +263,7 @@ func NewQuerierHandler( router.Path(path.Join(prefix, "/api/v1/label/{name}/values")).Methods("GET").Handler(promRouter) router.Path(path.Join(prefix, "/api/v1/series")).Methods("GET", "POST", "DELETE").Handler(promRouter) router.Path(path.Join(prefix, "/api/v1/metadata")).Methods("GET").Handler(promRouter) + router.Path(path.Join(prefix, "/api/v1/status/buildinfo")).Methods("GET").Handler(promRouter) // TODO(gotjosh): This custom handler is temporary until we're able to vendor the changes in: // https://github.com/prometheus/prometheus/pull/7125/files @@ -268,6 +277,7 @@ func NewQuerierHandler( router.Path(path.Join(legacyPrefix, "/api/v1/label/{name}/values")).Methods("GET").Handler(legacyPromRouter) router.Path(path.Join(legacyPrefix, "/api/v1/series")).Methods("GET", "POST", "DELETE").Handler(legacyPromRouter) router.Path(path.Join(legacyPrefix, "/api/v1/metadata")).Methods("GET").Handler(legacyPromRouter) + router.Path(path.Join(legacyPrefix, "/api/v1/status/buildinfo")).Methods("GET").Handler(legacyPromRouter) // Track execution time. return stats.NewWallTimeMiddleware().Wrap(router) diff --git a/pkg/api/handlers_test.go b/pkg/api/handlers_test.go index be0d7aa6429..a641f3e2427 100644 --- a/pkg/api/handlers_test.go +++ b/pkg/api/handlers_test.go @@ -1,14 +1,21 @@ package api import ( + "encoding/json" "io" "net/http" "net/http/httptest" + "runtime" "strings" "testing" + "github.com/prometheus/common/version" + v1 "github.com/prometheus/prometheus/web/api/v1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/weaveworks/common/user" + + "github.com/cortexproject/cortex/pkg/purger" ) func TestIndexHandlerPrefix(t *testing.T) { @@ -189,3 +196,56 @@ func TestConfigOverrideHandler(t *testing.T) { assert.NoError(t, err) assert.Equal(t, []byte("config"), body) } + +func TestBuildInfoAPI(t *testing.T) { + type buildInfo struct { + Status string `json:"status"` + Data v1.PrometheusVersion `json:"data"` + } + + for _, tc := range []struct { + name string + version string + branch string + revision string + expected buildInfo + }{ + { + name: "empty", + expected: buildInfo{Status: "success", Data: v1.PrometheusVersion{ + GoVersion: runtime.Version(), + }}, + }, + { + name: "set versions", + version: "v0.14.0", + branch: "test", + revision: "foo", + expected: buildInfo{Status: "success", Data: v1.PrometheusVersion{ + Version: "v0.14.0", + Branch: "test", + Revision: "foo", + GoVersion: runtime.Version(), + }}, + }, + } { + t.Run(tc.name, func(t *testing.T) { + cfg := Config{} + version.Version = tc.version + version.Branch = tc.branch + version.Revision = tc.revision + handler := NewQuerierHandler(cfg, nil, nil, nil, nil, purger.NewNoopTombstonesLoader(), nil, &FakeLogger{}) + writer := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/api/v1/status/buildinfo", nil) + req = req.WithContext(user.InjectOrgID(req.Context(), "test")) + handler.ServeHTTP(writer, req) + out, err := io.ReadAll(writer.Body) + require.NoError(t, err) + + var info buildInfo + err = json.Unmarshal(out, &info) + require.NoError(t, err) + require.Equal(t, tc.expected, info) + }) + } +}