Skip to content
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

mimir-continuous-test tool: Add basic authentication support #2717

Merged
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

### Mimir Continuous Test

* [ENHANCEMENT] Added basic authentication and bearer token support for when Mimir is behind a gateway authenticating the calls. #2717

### Documentation


Expand Down
7 changes: 5 additions & 2 deletions docs/sources/operators-guide/tools/mimir-continuous-test.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,14 @@ chmod +x mimir-continuous-test

## Configure mimir-continuous-test

Mimir-continuous-test requires the endpoints of the backend Grafana Mimir clusters and the tenant ID for writing and querying testing metrics:
Mimir-continuous-test requires the endpoints of the backend Grafana Mimir clusters and the authentication for writing and querying testing metrics:

- Set `-tests.write-endpoint` to the base endpoint on the write path. Remove any trailing slash from the URL. The tool appends the specific API path to the URL, for example `/api/v1/push` for the remote-write API.
- Set `-tests.read-endpoint` to the base endpoint on the read path. Remove any trailing slash from the URL. The tool appends the specific API path to the URL, for example `/api/v1/query_range` for the range-query API.
- Set `-tests.tenant-id` to the tenant ID to use to write and read metrics in tests.
- Set the authentication means to use to write and read metrics in tests. By priority order:
- `-tests.bearer-token` for bearer token authentication.
- `-tests.basic-auth-user` and `-tests.basic-auth-password` for a basic authentication.
- `-tests.tenant-id` to the tenant ID, default to `anonymous`.
- Set `-tests.smoke-test` to run the test once and immediately exit. In this mode, the process exit code is non-zero when the test fails.

> **Note:** You can run `mimir-continuous-test -help` to list all available configuration options.
Expand Down
40 changes: 33 additions & 7 deletions pkg/continuoustest/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ type MimirClient interface {
}

type ClientConfig struct {
TenantID string
TenantID string
BasicAuthUser string
BasicAuthPassword string
BearerToken string

WriteBaseEndpoint flagext.URLValue
WriteBatchSize int
Expand All @@ -54,7 +57,10 @@ type ClientConfig struct {
}

func (cfg *ClientConfig) RegisterFlags(f *flag.FlagSet) {
f.StringVar(&cfg.TenantID, "tests.tenant-id", "anonymous", "The tenant ID to use to write and read metrics in tests.")
f.StringVar(&cfg.TenantID, "tests.tenant-id", "anonymous", "The tenant ID to use to write and read metrics in tests. (mutually exclusive with basic-auth or bearer-token flags)")
f.StringVar(&cfg.BasicAuthUser, "tests.basic-auth-user", "", "The username to use for HTTP bearer authentication. (mutually exclusive with tenant-id or bearer-token flags)")
f.StringVar(&cfg.BasicAuthPassword, "tests.basic-auth-password", "", "The password to use for HTTP bearer authentication. (mutually exclusive with tenant-id or bearer-token flags)")
f.StringVar(&cfg.BearerToken, "tests.bearer-token", "", "The bearer token to use for HTTP bearer authentication. (mutually exclusive with tenant-id flag or basic-auth flags)")

f.Var(&cfg.WriteBaseEndpoint, "tests.write-endpoint", "The base endpoint on the write path. The URL should have no trailing slash. The specific API path is appended by the tool to the URL, for example /api/v1/push for the remote write API endpoint, so the configured URL must not include it.")
f.IntVar(&cfg.WriteBatchSize, "tests.write-batch-size", 1000, "The maximum number of series to write in a single request.")
Expand All @@ -73,8 +79,11 @@ type Client struct {

func NewClient(cfg ClientConfig, logger log.Logger) (*Client, error) {
rt := &clientRoundTripper{
tenantID: cfg.TenantID,
rt: instrumentation.TracerTransport{},
tenantID: cfg.TenantID,
basicAuthUser: cfg.BasicAuthUser,
basicAuthPassword: cfg.BasicAuthPassword,
bearerToken: cfg.BearerToken,
rt: instrumentation.TracerTransport{},
}

// Ensure the required config has been set.
Expand All @@ -84,6 +93,14 @@ func NewClient(cfg ClientConfig, logger log.Logger) (*Client, error) {
if cfg.ReadBaseEndpoint.URL == nil {
return nil, errors.New("the read endpoint has not been set")
}
// Ensure not both tenant-id and basic-auth are used at the same time
// anonymous is the default value for TenantID.
if (cfg.TenantID != "anonymous" && cfg.BasicAuthUser != "" && cfg.BasicAuthPassword != "" && cfg.BearerToken != "") || // all authentication at once
(cfg.TenantID != "anonymous" && cfg.BasicAuthUser != "" && cfg.BasicAuthPassword != "") || // tenant-id and basic auth
(cfg.TenantID != "anonymous" && cfg.BearerToken != "") || // tenant-id and bearer token
(cfg.BasicAuthUser != "" && cfg.BasicAuthPassword != "" && cfg.BearerToken != "") { // basic auth and bearer token
return nil, errors.New("either set tests.tenant-id or tests.basic-auth-user/tests.basic-auth-password or tests.bearer-token")
}

apiCfg := api.Config{
Address: cfg.ReadBaseEndpoint.String(),
Expand Down Expand Up @@ -242,8 +259,11 @@ type key int
var requestOptionsKey key

type clientRoundTripper struct {
tenantID string
rt http.RoundTripper
tenantID string
basicAuthUser string
basicAuthPassword string
bearerToken string
rt http.RoundTripper
}

// RoundTrip add the tenant ID header required by Mimir.
Expand All @@ -254,6 +274,12 @@ func (rt *clientRoundTripper) RoundTrip(req *http.Request) (*http.Response, erro
req.Header.Set("Cache-Control", "no-store")
}

req.Header.Set("X-Scope-OrgID", rt.tenantID)
if rt.bearerToken != "" {
req.Header.Set("Authorization", "Bearer "+rt.bearerToken)
} else if rt.basicAuthUser != "" && rt.basicAuthPassword != "" {
req.SetBasicAuth(rt.basicAuthUser, rt.basicAuthPassword)
} else {
req.Header.Set("X-Scope-OrgID", rt.tenantID)
}
return rt.rt.RoundTrip(req)
}