fix: [quota.default].TOTAL config setting supports unit suffixes (#9252)

Bring the code in line with the documentation that claims the `[quota.default].TOTAL` option supports unit suffixes.
Resolves forgejo/forgejo#8996

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9252
Reviewed-by: Lucas <sclu1034@noreply.codeberg.org>
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Brook <brook@noreply.codeberg.org>
Co-committed-by: Brook <brook@noreply.codeberg.org>
This commit is contained in:
Brook 2025-09-12 00:44:09 +02:00 committed by Gusted
commit 60cab1bafd
6 changed files with 99 additions and 38 deletions

View file

@ -6,6 +6,7 @@ package setting
import (
"errors"
"fmt"
"math"
"os"
"path/filepath"
"strconv"
@ -15,6 +16,7 @@ import (
"forgejo.org/modules/log"
"forgejo.org/modules/util"
"github.com/dustin/go-humanize"
"gopkg.in/ini.v1" //nolint:depguard
)
@ -320,6 +322,16 @@ func mustMapSetting(rootCfg ConfigProvider, sectionName string, setting any) {
}
}
// mustBytes returns -1 on parse error, or value out of range 0 to math.MaxInt64
func mustBytes(section ConfigSection, key string) int64 {
value := section.Key(key).String()
bytes, err := humanize.ParseBytes(value)
if err != nil || bytes > math.MaxInt64 {
return -1
}
return int64(bytes)
}
// DeprecatedWarnings contains the warning message for various deprecations, including: setting option, file/folder, etc
var DeprecatedWarnings []string

View file

@ -155,3 +155,24 @@ func TestDisableSaving(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, "k1 = a\nk2 = y\nk3 = z\n", string(bs))
}
func TestMustBytes(t *testing.T) {
test := func(value string) int64 {
cfg, err := NewConfigProviderFromData("[test]")
require.NoError(t, err)
sec := cfg.Section("test")
sec.NewKey("VALUE", value)
return mustBytes(sec, "VALUE")
}
assert.EqualValues(t, -1, test(""))
assert.EqualValues(t, -1, test("-1"))
assert.EqualValues(t, 0, test("0"))
assert.EqualValues(t, 1, test("1"))
assert.EqualValues(t, 10000, test("10000"))
assert.EqualValues(t, 1000000, test("1 mb"))
assert.EqualValues(t, 1048576, test("1mib"))
assert.EqualValues(t, 1782579, test("1.7mib"))
assert.EqualValues(t, -1, test("1 yib")) // too large
}

View file

@ -5,12 +5,9 @@ package setting
import (
"fmt"
"math"
"net/url"
"os"
"path/filepath"
"github.com/dustin/go-humanize"
)
// Package registry settings
@ -110,17 +107,3 @@ func loadPackagesFrom(rootCfg ConfigProvider) (err error) {
Packages.LimitSizeAlt = mustBytes(sec, "LIMIT_SIZE_ALT")
return nil
}
func mustBytes(section ConfigSection, key string) int64 {
const noLimit = "-1"
value := section.Key(key).MustString(noLimit)
if value == noLimit {
return -1
}
bytes, err := humanize.ParseBytes(value)
if err != nil || bytes > math.MaxInt64 {
return -1
}
return int64(bytes)
}

View file

@ -10,27 +10,6 @@ import (
"github.com/stretchr/testify/require"
)
func TestMustBytes(t *testing.T) {
test := func(value string) int64 {
cfg, err := NewConfigProviderFromData("[test]")
require.NoError(t, err)
sec := cfg.Section("test")
sec.NewKey("VALUE", value)
return mustBytes(sec, "VALUE")
}
assert.EqualValues(t, -1, test(""))
assert.EqualValues(t, -1, test("-1"))
assert.EqualValues(t, 0, test("0"))
assert.EqualValues(t, 1, test("1"))
assert.EqualValues(t, 10000, test("10000"))
assert.EqualValues(t, 1000000, test("1 mb"))
assert.EqualValues(t, 1048576, test("1mib"))
assert.EqualValues(t, 1782579, test("1.7mib"))
assert.EqualValues(t, -1, test("1 yib")) // too large
}
func Test_getStorageInheritNameSectionTypeForPackages(t *testing.T) {
// packages storage inherits from storage if nothing configured
iniStr := `

View file

@ -23,4 +23,7 @@ var Quota = struct {
func loadQuotaFrom(rootCfg ConfigProvider) {
mustMapSetting(rootCfg, "quota", &Quota)
sec := rootCfg.Section("quota.default")
Quota.Default.Total = mustBytes(sec, "TOTAL")
}

View file

@ -0,0 +1,63 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
import (
"fmt"
"testing"
"forgejo.org/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestConfigQuotaDefaultTotal(t *testing.T) {
iniStr := ``
cfg, err := NewConfigProviderFromData(iniStr)
require.NoError(t, err)
loadQuotaFrom(cfg)
assert.False(t, Quota.Enabled)
assert.EqualValues(t, -1, Quota.Default.Total)
testSets := []struct {
iniTotal string
expectTotal int64
}{
{"0", 0},
{"5000", 5000},
{"12,345,678", 12_345_678},
{"2k", 2000},
{"2MiB", 2 * 1024 * 1024},
{"3G", 3_000_000_000},
{"3GiB", 3 * 1024 * 1024 * 1024},
{"9EB", 9_000_000_000_000_000_000},
{"42EB", -1},
{"-1", -1},
{"-42", -1},
{"-1MiB", -1},
{"hello", -1},
{"unlimited", -1},
}
for _, testSet := range testSets {
t.Run(testSet.iniTotal, func(t *testing.T) {
defer test.MockVariableValue(&Quota.Default.Total, -404)()
iniStr := fmt.Sprintf(`
[quota]
ENABLED = true
[quota.default]
TOTAL = %s`, testSet.iniTotal)
cfg, err := NewConfigProviderFromData(iniStr)
require.NoError(t, err)
loadQuotaFrom(cfg)
assert.True(t, Quota.Enabled)
assert.Equal(t, testSet.expectTotal, Quota.Default.Total)
})
}
}