aboutsummaryrefslogtreecommitdiff
path: root/quotas.go
diff options
context:
space:
mode:
Diffstat (limited to 'quotas.go')
-rw-r--r--quotas.go151
1 files changed, 151 insertions, 0 deletions
diff --git a/quotas.go b/quotas.go
new file mode 100644
index 0000000..894ea3c
--- /dev/null
+++ b/quotas.go
@@ -0,0 +1,151 @@
+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)
+}