mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2026-05-17 00:26:36 +03:00
This commit adds possibility to omit tenantID in the URL path. In this case, tenantID will be fetched from HTTP headers `AccountID` and `ProjectID`. If headers are missing too, then default `0:0` tenantID is used. This functionality can be enabled only if -enableMultitenantHandlers cmd-line flag was set to vminsert, vmselect or vmagent. Motivation: this change makes VM configuration for multienancy consistent with VL configuration - see https://docs.victoriametrics.com/victorialogs/#multitenancy. And keeps backward compatibility in the same time. fixes https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4241
85 lines
3.4 KiB
Go
85 lines
3.4 KiB
Go
package httpserver
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestParsePathAndHeadersSuccess(t *testing.T) {
|
|
f := func(path, headers, prefix, authToken, suffix string) {
|
|
t.Helper()
|
|
header := make(http.Header)
|
|
hs := strings.Split(headers, ";")
|
|
for _, h := range hs {
|
|
if h == "" {
|
|
continue
|
|
}
|
|
parts := strings.Split(h, ":")
|
|
header.Set(parts[0], parts[1])
|
|
}
|
|
p, err := ParsePathAndHeaders(path, header)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
if p.Prefix != prefix {
|
|
t.Fatalf("unexpected Prefix; got %q; want %q", p.Prefix, prefix)
|
|
}
|
|
if p.AuthToken != authToken {
|
|
t.Fatalf("unexpected AuthToken; got %q; want %q", p.AuthToken, authToken)
|
|
}
|
|
if p.Suffix != suffix {
|
|
t.Fatalf("unexpected Suffix; got %q; want %q", p.Suffix, suffix)
|
|
}
|
|
}
|
|
|
|
// tenant is omitted in the Path, so we try reading it from headers
|
|
f("/select/prometheus/api/v1/query", "AccountID:1;ProjectID:1", "select", "1:1", "prometheus/api/v1/query")
|
|
f("/select/prometheus/api/v1/query_range", "AccountID:1", "select", "1:0", "prometheus/api/v1/query_range")
|
|
f("/select/prometheus/api/v1/query_range", "ProjectID:1", "select", "0:1", "prometheus/api/v1/query_range")
|
|
f("/insert/prometheus", "", "insert", "0:0", "prometheus")
|
|
f("/insert/prometheus", "AccountID:1;ProjectID:1", "insert", "1:1", "prometheus")
|
|
f("/insert/prometheus", "AccountID:multitenant", "insert", "multitenant", "prometheus")
|
|
f("/delete/prometheus/api/v1/admin/tsdb/delete_series", "AccountID:1;ProjectID:1", "delete", "1:1", "prometheus/api/v1/admin/tsdb/delete_series")
|
|
f("/insert//prometheus/api/v1/import/prometheus", "AccountID:2", "insert", "2:0", "prometheus/api/v1/import/prometheus")
|
|
|
|
// If headers are empty, we assume 0:0 as default.
|
|
f("/insert/prometheus", "", "insert", "0:0", "prometheus")
|
|
f("/select/prometheus/api/v1/query", "", "select", "0:0", "prometheus/api/v1/query")
|
|
|
|
// tenant is present in the Path
|
|
f("/insert/123/prometheus/api/v1/write", "", "insert", "123", "prometheus/api/v1/write")
|
|
f("/select/1:15/prometheus/api/v1/query", "", "select", "1:15", "prometheus/api/v1/query")
|
|
f("/insert/multitenant/prometheus/api/v1/write", "", "insert", "multitenant", "prometheus/api/v1/write")
|
|
f("/insert/0/prometheus/api/v1/write", "", "insert", "0", "prometheus/api/v1/write")
|
|
f("/insert/0:0/prometheus/api/v1/write", "", "insert", "0:0", "prometheus/api/v1/write")
|
|
f("/delete/123/prometheus/api/v1/admin/tsdb/delete_series", "", "delete", "123", "prometheus/api/v1/admin/tsdb/delete_series")
|
|
|
|
// tenant in the Path takes priority over headers
|
|
f("/insert/123/prometheus/api/v1/write", "AccountID:1;ProjectID:1", "insert", "123", "prometheus/api/v1/write")
|
|
f("/insert/multitenant/prometheus/api/v1/write", "AccountID:1;ProjectID:1", "insert", "multitenant", "prometheus/api/v1/write")
|
|
f("/insert/123:1/prometheus/api/v1/write", "AccountID:multitenant", "insert", "123:1", "prometheus/api/v1/write")
|
|
|
|
// Double slashes in Path
|
|
f("//insert//123//prometheus//api/v1/write", "", "insert", "123", "prometheus/api/v1/write")
|
|
f("//insert//prometheus//api/v1/write", "AccountID:1;ProjectID:1", "insert", "1:1", "prometheus/api/v1/write")
|
|
|
|
}
|
|
|
|
func TestParsePathAndHeadersFailure(t *testing.T) {
|
|
f := func(path string) {
|
|
t.Helper()
|
|
p, err := ParsePathAndHeaders(path, nil)
|
|
if err == nil {
|
|
t.Fatalf("expecting non-nil error; got path %+v", p)
|
|
}
|
|
}
|
|
|
|
// No prefix or suffix
|
|
f("/")
|
|
// Only prefix, no suffix or tenant
|
|
f("/insert")
|
|
f("/select/")
|
|
f("/select//")
|
|
}
|