本文主要研究一下cortex的tenant
cortex/pkg/tenant/tenant.go
var ( errTenantIDTooLong = errors.New("tenant ID is too long: max 150 characters") ) type errTenantIDUnsupportedCharacter struct { pos int 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
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方法
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数组
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提供了NormalizeTenantIDs、ValidTenantID、JoinTenantIDs方法;Resolver接口定义了TenantID、TenantIDs方法;SingleResolver及MultiResolver都实现了Resolver接口,基本都是通过user.ExtractOrgID(ctx)提取tenantID。