mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
obtain tenant information from headers
This commit is contained in:
parent
fbaa026ae6
commit
d0dd381512
4 changed files with 36 additions and 17 deletions
|
@ -197,16 +197,16 @@ func getOpenTSDBHTTPInsertHandler() func(req *http.Request) error {
|
|||
}
|
||||
}
|
||||
return func(req *http.Request) error {
|
||||
path := strings.Replace(req.URL.Path, "//", "/", -1)
|
||||
at, err := getAuthTokenFromPath(path)
|
||||
at, err := getAuthTokenFromReq(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot obtain auth token from path %q: %w", path, err)
|
||||
return fmt.Errorf("cannot obtain auth token: %w", err)
|
||||
}
|
||||
return opentsdbhttp.InsertHandler(at, req)
|
||||
}
|
||||
}
|
||||
|
||||
func getAuthTokenFromPath(path string) (*auth.Token, error) {
|
||||
func getAuthTokenFromReq(req *http.Request) (*auth.Token, error) {
|
||||
path := strings.Replace(req.URL.Path, "//", "/", -1)
|
||||
p, err := httpserver.ParsePath(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse multitenant path: %w", err)
|
||||
|
@ -217,7 +217,7 @@ func getAuthTokenFromPath(path string) (*auth.Token, error) {
|
|||
if p.Suffix != "opentsdb/api/put" {
|
||||
return nil, fmt.Errorf("unsupported path requested: %q; expecting 'opentsdb/api/put'", p.Suffix)
|
||||
}
|
||||
return auth.NewTokenPossibleMultitenant(p.AuthToken)
|
||||
return auth.NewTokenPossibleMultitenant(p.AuthToken, req.Header)
|
||||
}
|
||||
|
||||
func requestHandler(w http.ResponseWriter, r *http.Request) bool {
|
||||
|
@ -265,6 +265,7 @@ func requestHandler(w http.ResponseWriter, r *http.Request) bool {
|
|||
// See https://github.com/VictoriaMetrics/VictoriaMetrics/pull/2670
|
||||
path = strings.TrimSuffix(path, "/")
|
||||
}
|
||||
|
||||
switch path {
|
||||
case "/prometheus/api/v1/write", "/api/v1/write", "/api/v1/push", "/prometheus/api/v1/push":
|
||||
if common.HandleVMProtoServerHandshake(w, r) {
|
||||
|
@ -498,7 +499,7 @@ func processMultitenantRequest(w http.ResponseWriter, r *http.Request, path stri
|
|||
httpserver.Errorf(w, r, `unsupported multitenant prefix: %q; expected "insert"`, p.Prefix)
|
||||
return true
|
||||
}
|
||||
at, err := auth.NewToken(p.AuthToken)
|
||||
at, err := auth.NewTokenPossibleMultitenant(p.AuthToken, r.Header)
|
||||
if err != nil {
|
||||
httpserver.Errorf(w, r, "cannot obtain auth token: %s", err)
|
||||
return true
|
||||
|
|
|
@ -61,8 +61,11 @@ It increases cluster availability, and simplifies cluster maintenance as well as
|
|||
## Multitenancy
|
||||
|
||||
VictoriaMetrics cluster supports multiple isolated tenants (aka namespaces).
|
||||
Tenants are identified by `accountID` or `accountID:projectID`, which are put inside request urls.
|
||||
See [these docs](#url-format) for details.
|
||||
Tenants are identified by `accountID` or `accountID:projectID`, which are either put inside request urls or inside headers.
|
||||
Its also possible to accept data from multiple tenants via a special `multitenant` endpoints `http://vminsert:8480/insert/multitenant/<suffix>`,
|
||||
where `<suffix>` can be replaced with any supported suffix for data ingestion from [this list](#url-format). In this case tenants can be passed via:
|
||||
- [`headers`](#multitenancy-via-headers)
|
||||
- [`labels`](#multitenancy-via-labels)
|
||||
|
||||
Some facts about tenants in VictoriaMetrics:
|
||||
|
||||
|
@ -85,13 +88,19 @@ when different tenants have different amounts of data and different query load.
|
|||
|
||||
- VictoriaMetrics exposes various per-tenant statistics via metrics - see [these docs](https://docs.victoriametrics.com/pertenantstatistic/).
|
||||
|
||||
See also [multitenancy via labels](#multitenancy-via-labels).
|
||||
See also
|
||||
- [`multitenancy via headers`](#multitenancy-via-headers)
|
||||
- [`multitenancy via labels`](#multitenancy-via-labels)
|
||||
|
||||
|
||||
## Multitenancy via headers
|
||||
|
||||
To pass tenant information via header it's required to use special `multitenant` endpoint and pass `accountID:projectID` via `TenantID` HTTP header.
|
||||
If no `TenantID` header is set, tenant information will be obtained from [special labels](#multitenancy-via-labels).
|
||||
|
||||
|
||||
## Multitenancy via labels
|
||||
|
||||
`vminsert` can accept data from multiple [tenants](#multitenancy) via a special `multitenant` endpoints `http://vminsert:8480/insert/multitenant/<suffix>`,
|
||||
where `<suffix>` can be replaced with any supported suffix for data ingestion from [this list](#url-format).
|
||||
In this case the account id and project id are obtained from optional `vm_account_id` and `vm_project_id` labels of the incoming samples.
|
||||
If `vm_account_id` or `vm_project_id` labels are missing or invalid, then the corresponding `accountID` or `projectID` is set to 0.
|
||||
These labels are automatically removed from samples before forwarding them to `vmstorage`.
|
||||
|
|
|
@ -2,6 +2,7 @@ package auth
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
@ -35,8 +36,11 @@ func NewToken(authToken string) (*Token, error) {
|
|||
// NewTokenPossibleMultitenant returns new Token for the given authToken.
|
||||
//
|
||||
// If authToken == "multitenant", then nil Token is returned.
|
||||
func NewTokenPossibleMultitenant(authToken string) (*Token, error) {
|
||||
func NewTokenPossibleMultitenant(authToken string, headers http.Header) (*Token, error) {
|
||||
if authToken == "multitenant" {
|
||||
if tenantID := headers.Get("TenantID"); tenantID != "" {
|
||||
return NewToken(tenantID)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
return NewToken(authToken)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -29,9 +30,11 @@ func TestNewTokenSuccess(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNewTokenPossibleMultitenantSuccess(t *testing.T) {
|
||||
f := func(token string, want string) {
|
||||
f := func(token string, tenantIDValue string, want string) {
|
||||
t.Helper()
|
||||
newToken, err := NewTokenPossibleMultitenant(token)
|
||||
headers := http.Header{}
|
||||
headers.Set("TenantID", tenantIDValue)
|
||||
newToken, err := NewTokenPossibleMultitenant(token, headers)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
@ -41,11 +44,13 @@ func TestNewTokenPossibleMultitenantSuccess(t *testing.T) {
|
|||
}
|
||||
}
|
||||
// token with accountID only
|
||||
f("1", "1")
|
||||
f("1", "", "1")
|
||||
// token with accountID and projecTID
|
||||
f("1:2", "1:2")
|
||||
f("1:2", "", "1:2")
|
||||
// multitenant
|
||||
f("multitenant", "multitenant")
|
||||
f("multitenant", "", "multitenant")
|
||||
// multitenant with tenantID in headers
|
||||
f("multitenant", "1:2", "1:2")
|
||||
}
|
||||
|
||||
func TestNewTokenFailure(t *testing.T) {
|
||||
|
|
Loading…
Reference in a new issue