Skip to content

Support Prometheus /api/v1/status/buildinfo API on Querier/QFE #4978

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog

## master / unreleased
* [FEATURE] Querier/Query Frontend: support Prometheus /api/v1/status/buildinfo API. #4978

## 1.14.0 in progress

Expand Down
4 changes: 3 additions & 1 deletion pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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")
Expand All @@ -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
Expand Down
12 changes: 11 additions & 1 deletion pkg/api/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -211,7 +212,14 @@ func NewQuerierHandler(
false,
regexp.MustCompile(".*"),
func() (v1.RuntimeInfo, error) { return v1.RuntimeInfo{}, errors.New("not implemented") },
&v1.PrometheusVersion{},
&v1.PrometheusVersion{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we decided not to support the following?

BuildUser string `json:"buildUser"`
BuildDate string `json:"buildDate"`
GoVersion string `json:"goVersion"`

Not that I want to support them, but I thought it do be nice to have explicit discussion on this as documentation purposes.

Copy link
Contributor Author

@yeya24 yeya24 Nov 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could add GoVersion for sure. Thanks for catching.
But for other variables, right now we don't propagate these variables right now from our build. I will add them here first and when needed we can propagate these info through makefile

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,
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down
60 changes: 60 additions & 0 deletions pkg/api/handlers_test.go
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down Expand Up @@ -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)
})
}
}