grafana/pkg/services/quota/model.go
Marcus Efraimsson 040b7d2571
Chore: Add errutils helpers (#73577)
Add helpers for the errutil package in favor of errutil.NewBase.
2023-08-22 12:52:24 +02:00

219 lines
4.3 KiB
Go

package quota
import (
"strings"
"sync"
"time"
"github.com/grafana/grafana/pkg/util/errutil"
)
var ErrBadRequest = errutil.BadRequest("quota.bad-request")
var ErrInvalidTargetSrv = errutil.BadRequest("quota.invalid-target")
var ErrInvalidScope = errutil.BadRequest("quota.invalid-scope")
var ErrFailedToGetScope = errutil.Internal("quota.failed-get-scope")
var ErrInvalidTarget = errutil.Internal("quota.invalid-target-table")
var ErrUsageFoundForTarget = errutil.NotFound("quota.missing-target-usage")
var ErrTargetSrvConflict = errutil.BadRequest("quota.target-srv-conflict")
var ErrDisabled = errutil.Forbidden("quota.disabled", errutil.WithPublicMessage("Quotas not enabled"))
var ErrInvalidTagFormat = errutil.Internal("quota.invalid-invalid-tag-format")
type ScopeParameters struct {
OrgID int64
UserID int64
}
type Scope string
const (
GlobalScope Scope = "global"
OrgScope Scope = "org"
UserScope Scope = "user"
)
func (s Scope) Validate() error {
switch s {
case GlobalScope, OrgScope, UserScope:
return nil
default:
return ErrInvalidScope.Errorf("bad scope: %s", s)
}
}
type TargetSrv string
type Target string
const delimiter = ":"
// Tag is a string with the format <srv>:<target>:<scope>
type Tag string
func NewTag(srv TargetSrv, t Target, scope Scope) (Tag, error) {
if err := scope.Validate(); err != nil {
return "", err
}
tag := Tag(strings.Join([]string{string(srv), string(t), string(scope)}, delimiter))
return tag, nil
}
func (t Tag) split() ([]string, error) {
parts := strings.SplitN(string(t), delimiter, -1)
if len(parts) != 3 {
return nil, ErrInvalidTagFormat.Errorf("tag format should be ^(?<srv>\\w):(?<target>\\w):(?<scope>\\w)$")
}
return parts, nil
}
func (t Tag) GetSrv() (TargetSrv, error) {
parts, err := t.split()
if err != nil {
return "", err
}
return TargetSrv(parts[0]), nil
}
func (t Tag) GetTarget() (Target, error) {
parts, err := t.split()
if err != nil {
return "", err
}
return Target(parts[1]), nil
}
func (t Tag) GetScope() (Scope, error) {
parts, err := t.split()
if err != nil {
return "", err
}
return Scope(parts[2]), nil
}
type Item struct {
Tag Tag
Value int64
}
type Map struct {
mutex sync.RWMutex
m map[Tag]int64
}
func (m *Map) Set(tag Tag, limit int64) {
m.mutex.Lock()
defer m.mutex.Unlock()
if len(m.m) == 0 {
m.m = make(map[Tag]int64, 0)
}
m.m[tag] = limit
}
func (m *Map) Get(tag Tag) (int64, bool) {
m.mutex.RLock()
defer m.mutex.RUnlock()
limit, ok := m.m[tag]
return limit, ok
}
func (m *Map) Merge(l2 *Map) {
l2.mutex.RLock()
defer l2.mutex.RUnlock()
for k, v := range l2.m {
// TODO check for conflicts?
m.Set(k, v)
}
}
func (m *Map) Iter() <-chan Item {
m.mutex.RLock()
defer m.mutex.RUnlock()
ch := make(chan Item)
go func() {
defer close(ch)
for t, v := range m.m {
ch <- Item{Tag: t, Value: v}
}
}()
return ch
}
func (m *Map) Scopes() (map[Scope]struct{}, error) {
res := make(map[Scope]struct{})
for item := range m.Iter() {
scope, err := item.Tag.GetScope()
if err != nil {
return nil, err
}
res[scope] = struct{}{}
}
return res, nil
}
func (m *Map) Services() (map[TargetSrv]struct{}, error) {
res := make(map[TargetSrv]struct{})
for item := range m.Iter() {
srv, err := item.Tag.GetSrv()
if err != nil {
return nil, err
}
res[srv] = struct{}{}
}
return res, nil
}
func (m *Map) Targets() (map[Target]struct{}, error) {
res := make(map[Target]struct{})
for item := range m.Iter() {
target, err := item.Tag.GetTarget()
if err != nil {
return nil, err
}
res[target] = struct{}{}
}
return res, nil
}
type Quota struct {
Id int64
OrgId int64
UserId int64
Target string
Limit int64
Created time.Time
Updated time.Time
}
type QuotaDTO struct {
OrgId int64 `json:"org_id,omitempty"`
UserId int64 `json:"user_id,omitempty"`
Target string `json:"target"`
Limit int64 `json:"limit"`
Used int64 `json:"used"`
Service string `json:"-"`
Scope string `json:"-"`
}
func (dto QuotaDTO) Tag() (Tag, error) {
return NewTag(TargetSrv(dto.Service), Target(dto.Target), Scope(dto.Scope))
}
type UpdateQuotaCmd struct {
Target string `json:"target"`
Limit int64 `json:"limit"`
OrgID int64 `json:"-"`
UserID int64 `json:"-"`
}
type NewUsageReporter struct {
TargetSrv TargetSrv
DefaultLimits *Map
Reporter UsageReporterFunc
}