聊聊cortex的tenant

序 本文主要研究一下cortex的tenant
tenant cortex/pkg/tenant/tenant.go

var ( errTenantIDTooLong = errors.New("tenant ID is too long: max 150 characters") )type errTenantIDUnsupportedCharacter struct { posint tenantID string }func (e *errTenantIDUnsupportedCharacter) Error() string { return fmt.Sprintf( "tenant ID '%s' contains unsupported character '%c'", e.tenantID, e.tenantID[e.pos], ) }const tenantIDsLabelSeparator = "|"// NormalizeTenantIDs is creating a normalized form by sortiing and de-duplicating the list of tenantIDs func NormalizeTenantIDs(tenantIDs []string) []string { sort.Strings(tenantIDs)count := len(tenantIDs) if count <= 1 { return tenantIDs }posOut := 1 for posIn := 1; posIn < count; posIn++ { if tenantIDs[posIn] != tenantIDs[posIn-1] { tenantIDs[posOut] = tenantIDs[posIn] posOut++ } }return tenantIDs[0:posOut] }// ValidTenantID func ValidTenantID(s string) error { // check if it contains invalid runes for pos, r := range s { if !isSupported(r) { return &errTenantIDUnsupportedCharacter{ tenantID: s, pos:pos, } } }if len(s) > 150 { return errTenantIDTooLong }return nil }func JoinTenantIDs(tenantIDs []string) string { return strings.Join(tenantIDs, tenantIDsLabelSeparator) }// this checks if a rune is supported in tenant IDs (according to // https://cortexmetrics.io/docs/guides/limitations/#tenant-id-naming) func isSupported(c rune) bool { // characters if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') { return true }// digits if '0' <= c && c <= '9' { return true }// special return c == '!' || c == '-' || c == '_' || c == '.' || c == '*' || c == '\'' || c == '(' || c == ')' }// TenantIDsFromOrgID extracts different tenants from an orgID string value // // ignore stutter warning //nolint:golint func TenantIDsFromOrgID(orgID string) ([]string, error) { return TenantIDs(user.InjectOrgID(context.TODO(), orgID)) }

tenant提供了NormalizeTenantIDs、ValidTenantID、JoinTenantIDs方法;NormalizeTenantIDs用于去重和排序tenantIDs;ValidTenantID会通过isSupported进行校验,并校验长度;JoinTenantIDs使用 |来连接TenantID
Resolver cortex/pkg/tenant/resolver.go
type Resolver interface { // TenantID returns exactly a single tenant ID from the context. It should be // used when a certain endpoint should only support exactly a single // tenant ID. It returns an error user.ErrNoOrgID if there is no tenant ID // supplied or user.ErrTooManyOrgIDs if there are multiple tenant IDs present. TenantID(context.Context) (string, error)// TenantIDs returns all tenant IDs from the context. It should return // normalized list of ordered and distinct tenant IDs (as produced by // NormalizeTenantIDs). TenantIDs(context.Context) ([]string, error) }

Resolver接口定义了TenantID、TenantIDs方法
SingleResolver
cortex/pkg/tenant/resolver.go
type SingleResolver struct { }func (t *SingleResolver) TenantID(ctx context.Context) (string, error) { //lint:ignore faillint wrapper around upstream method return user.ExtractOrgID(ctx) }func (t *SingleResolver) TenantIDs(ctx context.Context) ([]string, error) { //lint:ignore faillint wrapper around upstream method orgID, err := user.ExtractOrgID(ctx) if err != nil { return nil, err } return []string{orgID}, err }

SingleResolver实现了Resolver接口,其TenantID使用user.ExtractOrgID(ctx)提取TenantID;TenantIDs方法则返回的是orgID数组
MultiResolver
cortex/pkg/tenant/resolver.go
type MultiResolver struct { }// NewMultiResolver creates a tenant resolver, which allows request to have // multiple tenant ids submitted separated by a '|' character. This enforces // further limits on the character set allowed within tenants as detailed here: // https://cortexmetrics.io/docs/guides/limitations/#tenant-id-naming) func NewMultiResolver() *MultiResolver { return &MultiResolver{} }func (t *MultiResolver) TenantID(ctx context.Context) (string, error) { orgIDs, err := t.TenantIDs(ctx) if err != nil { return "", err }if len(orgIDs) > 1 { return "", user.ErrTooManyOrgIDs }return orgIDs[0], nil }func (t *MultiResolver) TenantIDs(ctx context.Context) ([]string, error) { //lint:ignore faillint wrapper around upstream method orgID, err := user.ExtractOrgID(ctx) if err != nil { return nil, err }orgIDs := strings.Split(orgID, tenantIDsLabelSeparator) for _, orgID := range orgIDs { if err := ValidTenantID(orgID); err != nil { return nil, err } }return NormalizeTenantIDs(orgIDs), nil }

MultiResolver实现了Resolver接口,其TenantID方法使用TenantIDs提取orgIDs,并校验其长度;TenantIDs方法使用user.ExtractOrgID(ctx)提取orgID,然后使用tenantIDsLabelSeparator分割提取为orgIDs,最后通过NormalizeTenantIDs返回
小结 【聊聊cortex的tenant】cortex的tenant提供了NormalizeTenantIDs、ValidTenantID、JoinTenantIDs方法;Resolver接口定义了TenantID、TenantIDs方法;SingleResolver及MultiResolver都实现了Resolver接口,基本都是通过user.ExtractOrgID(ctx)提取tenantID。
doc
  • cortex

    推荐阅读