forked from Deuxfleurs/guichet
151 lines
3.3 KiB
Go
151 lines
3.3 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
|
|
garage "git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang"
|
|
"github.com/go-ldap/ldap/v3"
|
|
)
|
|
|
|
const (
|
|
// --- Default Quota Values ---
|
|
QUOTA_WEBSITE_SIZE_DEFAULT = 1024 * 1024 * 50 // 50MB
|
|
QUOTA_WEBSITE_SIZE_BURSTED = 1024 * 1024 * 200 // 200MB
|
|
QUOTA_WEBSITE_OBJECTS = 10000 // 10k objects
|
|
QUOTA_WEBSITE_COUNT = 5 // 5 buckets
|
|
|
|
// --- Per-user overridable fields ---
|
|
FIELD_QUOTA_WEBSITE_SIZE_BURSTED = "quota_website_size_bursted"
|
|
FIELD_QUOTA_WEBSITE_COUNT = "quota_website_count"
|
|
)
|
|
|
|
type UserQuota struct {
|
|
WebsiteCount int64
|
|
WebsiteSizeDefault int64
|
|
WebsiteSizeBursted int64
|
|
WebsiteObjects int64
|
|
}
|
|
|
|
func NewUserQuota() *UserQuota {
|
|
return &UserQuota{
|
|
WebsiteCount: QUOTA_WEBSITE_COUNT,
|
|
WebsiteSizeDefault: QUOTA_WEBSITE_SIZE_DEFAULT,
|
|
WebsiteSizeBursted: QUOTA_WEBSITE_SIZE_BURSTED,
|
|
WebsiteObjects: QUOTA_WEBSITE_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
|
|
}
|
|
|
|
return quotas
|
|
}
|
|
|
|
func (q *UserQuota) DefaultWebsiteQuota() *garage.UpdateBucketRequestQuotas {
|
|
qr := garage.NewUpdateBucketRequestQuotas()
|
|
|
|
qr.SetMaxSize(q.WebsiteSizeDefault)
|
|
qr.SetMaxObjects(q.WebsiteSizeBursted)
|
|
|
|
return qr
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
// --- 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)
|
|
}
|