guichet/quotas.go
Quentin Dufour 1a9d750de7
Some checks reported errors
continuous-integration/drone/push Build was killed
Implement basic PIM management
2024-02-12 19:19:41 +01:00

201 lines
4.6 KiB
Go

package main
import (
"errors"
"fmt"
"strconv"
garage "git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang"
"github.com/go-ldap/ldap/v3"
)
// Note: PIM = Personal Information Manager
const (
// --- Default Quota Values Websites ---
QUOTA_WEBSITE_SIZE_DEFAULT = 1024 * 1024 * 50 // 50MB
QUOTA_WEBSITE_SIZE_BURSTED = 1024 * 1024 * 200 // 200MB
QUOTA_WEBSITE_OBJECTS = 10_000 // 10k objects
QUOTA_WEBSITE_COUNT = 5 // 5 buckets
// --- Default Quota Values PIM ---
QUOTA_PIM_SIZE_DEFAULT = 1024 * 1024 * 100 // 100MB
QUOTA_PIM_SIZE_BURSTED = 1024 * 1024 * 500 // 500MB
QUOTA_PIM_OBJECTS = 100_000 // 100k objects
// --- Per-user overridable fields ---
FIELD_QUOTA_WEBSITE_SIZE_BURSTED = "quota_website_size_bursted"
FIELD_QUOTA_WEBSITE_COUNT = "quota_website_count"
FIELD_QUOTA_PIM_SIZE_BURSTED = "quota_pim_size_bursted"
)
type UserQuota struct {
WebsiteCount int64
WebsiteSizeDefault int64
WebsiteSizeBursted int64
WebsiteObjects int64
PimSizeDefault int64
PimSizeBursted int64
PimObjects int64
}
func NewUserQuota() *UserQuota {
return &UserQuota{
WebsiteCount: QUOTA_WEBSITE_COUNT,
WebsiteSizeDefault: QUOTA_WEBSITE_SIZE_DEFAULT,
WebsiteSizeBursted: QUOTA_WEBSITE_SIZE_BURSTED,
WebsiteObjects: QUOTA_WEBSITE_OBJECTS,
PimSizeDefault: QUOTA_PIM_SIZE_DEFAULT,
PimSizeBursted: QUOTA_PIM_SIZE_BURSTED,
PimObjects: QUOTA_PIM_OBJECTS,
}
}
var (
ErrQuotaEmpty = fmt.Errorf("No quota is defined for this entry")
ErrQuotaInvalid = fmt.Errorf("The defined quota can't be parsed")
)
func entryToQuota(entry *ldap.Entry, field string) (int64, error) {
f := entry.GetAttributeValue(field)
if f == "" {
return -1, ErrQuotaEmpty
}
q, err := strconv.ParseInt(f, 10, 64)
if err != nil {
return -1, errors.Join(ErrQuotaInvalid, err)
}
return q, nil
}
func NewUserQuotaFromEntry(entry *ldap.Entry) *UserQuota {
quotas := NewUserQuota()
if q, err := entryToQuota(entry, FIELD_QUOTA_WEBSITE_COUNT); err == nil {
quotas.WebsiteCount = q
}
if q, err := entryToQuota(entry, FIELD_QUOTA_WEBSITE_SIZE_BURSTED); err == nil {
quotas.WebsiteSizeBursted = q
}
if q, err := entryToQuota(entry, FIELD_QUOTA_PIM_SIZE_BURSTED); err == nil {
quotas.PimSizeBursted = q
}
return quotas
}
func (q *UserQuota) DefaultWebsiteQuota() *garage.UpdateBucketRequestQuotas {
qr := garage.NewUpdateBucketRequestQuotas()
qr.SetMaxSize(q.WebsiteSizeDefault)
qr.SetMaxObjects(q.WebsiteSizeBursted)
return qr
}
func (q *UserQuota) DefaultPimQuota() *garage.UpdateBucketRequestQuotas {
qr := garage.NewUpdateBucketRequestQuotas()
qr.SetMaxSize(q.PimSizeDefault)
qr.SetMaxObjects(q.PimObjects)
return qr
}
// Website getters/setters
func (q *UserQuota) WebsiteSizeAdjust(sz int64) int64 {
if sz < q.WebsiteSizeDefault {
return q.WebsiteSizeDefault
} else if sz > q.WebsiteSizeBursted {
return q.WebsiteSizeBursted
} else {
return sz
}
}
func (q *UserQuota) WebsiteObjectAdjust(objs int64) int64 {
if objs > q.WebsiteObjects || objs <= 0 {
return q.WebsiteObjects
} else {
return objs
}
}
func (q *UserQuota) WebsiteSizeBurstedPretty() string {
return prettyValue(q.WebsiteSizeBursted)
}
// PIM getters/setters
func (q *UserQuota) PimSizeAdjust(sz int64) int64 {
if sz < q.PimSizeDefault {
return q.PimSizeDefault
} else if sz > q.PimSizeBursted {
return q.PimSizeBursted
} else {
return sz
}
}
func (q *UserQuota) PimObjectAdjust(objs int64) int64 {
if objs > q.PimObjects || objs <= 0 {
return q.PimObjects
} else {
return objs
}
}
func (q *UserQuota) PimSizeBurstedPretty() string {
return prettyValue(q.PimSizeBursted)
}
// --- A quota stat we can use
type QuotaStat struct {
Current int64 `json:"current"`
Max int64 `json:"max"`
Ratio float64 `json:"ratio"`
Burstable bool `json:"burstable"`
}
func NewQuotaStat(current, max int64, burstable bool) QuotaStat {
return QuotaStat{
Current: current,
Max: max,
Ratio: float64(current) / float64(max),
Burstable: burstable,
}
}
func (q *QuotaStat) IsFull() bool {
return q.Current >= q.Max
}
func (q *QuotaStat) Percent() int64 {
return int64(q.Ratio * 100)
}
func (q *QuotaStat) PrettyCurrent() string {
return prettyValue(q.Current)
}
func (q *QuotaStat) PrettyMax() string {
return prettyValue(q.Max)
}
func prettyValue(v int64) string {
if v < 1024 {
return fmt.Sprintf("%d octets", v)
}
v = v / 1024
if v < 1024 {
return fmt.Sprintf("%d kio", v)
}
v = v / 1024
if v < 1024 {
return fmt.Sprintf("%d Mio", v)
}
v = v / 1024
if v < 1024 {
return fmt.Sprintf("%d Gio", v)
}
v = v / 1024
return fmt.Sprintf("%d Tio", v)
}