fix(ports): align user port upper bound strictly inward to block end <= b
The previous normalization logic aligned the upper bound using (b // 100) * 100 + 99, which rounded down to a block start and then extended to +99. For any input where the upper bound did not already end in 99 (such as 10000-15000 or 10000-20000), this spilled over by (99 - (b % 100)) ports past the user's requested range (e.g. 10000-15000 became 10000-15099). Ports outside the user's input may be in use or unavailable. Align the upper bound strictly inward so each allocated block is a subset of the input: - calculate hi as ((b + 1) / PortsPerUser) * PortsPerUser - 1 (ending in ...99 <= b) - mirror the calculation in scripts/00-ip-ask.sh normalize_user_ports - update unit tests in internal/cfg to verify strict inward alignment - update docs/configuration.md examples and container capacity readouts
This commit is contained in:
1 parent
8d9156b333
commit
1fdcf3f37b
4 files changed
+26
-24
No files matched your search
@@ -179,13 +179,14 @@ Rules:
|
||||
outside `10000-29999` — only the overlap with the usable domain counts, and a
|
||||
fully-outside range contributes nothing.
|
||||
- Values are **auto-aligned to whole hundreds**: the low end rounds up to a
|
||||
block start and the high end rounds down then extends to `+99`, so the
|
||||
effective range always ends in `...99`, never `...00`. For example
|
||||
`10001-29998` becomes `10100-29999`.
|
||||
block start and the high end rounds down to a block end (`...99 <= B`), so the
|
||||
effective range always ends in `...99`, never `...00`, and never spills past
|
||||
the requested upper bound. For example `10001-29998` becomes `10100-29899`,
|
||||
and `10000-15000` becomes `10000-14999`.
|
||||
- At least one whole 100-port block must remain (the value is rejected
|
||||
otherwise) — a range that cannot host even one container is an error.
|
||||
- Capacity follows the ranges: `10000-29999` allows **200** containers;
|
||||
`10000-20000, 25000-30000` allows **151**; and so on. The interactive prompt
|
||||
`10000-20000, 25000-30000` allows **150**; and so on. The interactive prompt
|
||||
echoes the computed capacity for confirmation.
|
||||
- A narrowed set reserves far fewer host user ports, so an install's
|
||||
port-occupancy check scans only the configured ranges — not the whole
|
||||
|
||||
@@ -315,8 +315,8 @@ PY
|
||||
# comma-separated) and the container capacity on one line ("canon|cap"). Exit 0
|
||||
# only when the value yields at least one usable 100-port block. Mirrors
|
||||
# cfg.ParseUserPorts: low end rounds up to a block start, high end rounds down
|
||||
# then +99 (never ends in ...00); ranges outside 10000-29999 are clamped, and
|
||||
# fully-outside ranges contribute nothing.
|
||||
# to a block end <= b (never ends in ...00, never exceeds user's b); ranges
|
||||
# outside 10000-29999 are clamped, and fully-outside ranges contribute nothing.
|
||||
normalize_user_ports(){
|
||||
python3 - "$1" <<'PY'
|
||||
import sys
|
||||
@@ -337,7 +337,7 @@ def norm(s):
|
||||
if a > b:
|
||||
continue
|
||||
lo = ((a + 99) // 100) * 100
|
||||
hi = (b // 100) * 100 + 99
|
||||
hi = ((b + 1) // 100) * 100 - 1
|
||||
if lo > hi:
|
||||
continue
|
||||
out.append([lo, hi])
|
||||
|
||||
@@ -386,9 +386,8 @@ func LegacySlotRangeToUserPorts(slotRange string) (string, error) {
|
||||
// - Lo/hi may extend outside [UserPortBase, UserPortMax] — only the overlap
|
||||
// with the usable domain counts (a range fully outside contributes nothing).
|
||||
// - Each range is aligned inward to whole hundreds: the low end rounds UP to
|
||||
// a block start, the high end rounds DOWN to a block start then extends to
|
||||
// +99 (so the effective range always ends in ...99, never ...00). A range
|
||||
// narrower than one block is dropped.
|
||||
// a block start, the high end rounds DOWN to a block end (never exceeding
|
||||
// b, ending in ...99). A range narrower than one block is dropped.
|
||||
// - Overlapping/adjacent ranges are merged, so capacity is never double-counted.
|
||||
// - At least one usable 100-port block must remain, or the value is rejected
|
||||
// (a range that cannot host even one container is an error).
|
||||
@@ -421,9 +420,9 @@ func ParseUserPorts(s string) ([]PortRange, error) {
|
||||
if a > b {
|
||||
continue
|
||||
}
|
||||
// Align inward: lo up to a block start, hi down to a block start then +99.
|
||||
// Align inward: lo rounds UP to a block start, hi rounds DOWN to a block end <= b.
|
||||
lo := ((a + PortsPerUser - 1) / PortsPerUser) * PortsPerUser
|
||||
hi := (b/PortsPerUser)*PortsPerUser + PortsPerUser - 1
|
||||
hi := ((b + 1) / PortsPerUser) * PortsPerUser - 1
|
||||
if lo > hi {
|
||||
continue // narrower than one block
|
||||
}
|
||||
@@ -448,7 +447,7 @@ func ParseUserPorts(s string) ([]PortRange, error) {
|
||||
}
|
||||
|
||||
// CanonicalUserPorts renders parsed ranges as their canonical comma-separated
|
||||
// string form ("10000-20099, 25000-29999"). Returns the raw input unchanged if
|
||||
// string form ("10000-19999, 25000-29999"). Returns the raw input unchanged if
|
||||
// it does not parse.
|
||||
func CanonicalUserPorts(s string) string {
|
||||
rs, err := ParseUserPorts(s)
|
||||
|
||||
@@ -266,17 +266,19 @@ func TestParseUserPorts(t *testing.T) {
|
||||
canon string // canonical form; "" = must be rejected
|
||||
}{
|
||||
{"10000-29999", 200, "10000-29999"}, // default full range
|
||||
{"10000-20000", 101, "10000-20099"}, // hi rounds down then +99 (never ends in 00)
|
||||
{"10001-29998", 199, "10100-29999"}, // inward-aligned both ends
|
||||
{"10000-20000", 100, "10000-19999"}, // hi rounds down to block end <= b (never exceeds b)
|
||||
{"10000-15000", 50, "10000-14999"}, // boundary test: 15000 rounds down to 14999, not 15099
|
||||
{"10001-29998", 198, "10100-29899"}, // inward-aligned both ends (29998 rounds down to 29899)
|
||||
{"10001-29999", 199, "10100-29999"}, // inward-aligned low end only
|
||||
{"10001-10099", 0, ""}, // narrower than one block
|
||||
{"10000-10099", 1, "10000-10099"}, // exactly one block
|
||||
{"10000-20000, 25000-30000", 151, "10000-20099, 25000-29999"}, // discontiguous
|
||||
{"10000-15000, 14000-20000", 101, "10000-20099"}, // overlapping → merged
|
||||
{"5000-15000", 51, "10000-15099"}, // lo below domain clamps to 10000
|
||||
{"10000-20000, 25000-30000", 150, "10000-19999, 25000-29999"}, // discontiguous
|
||||
{"10000-15000, 14000-20000", 100, "10000-19999"}, // overlapping → merged
|
||||
{"5000-15000", 50, "10000-14999"}, // lo below domain clamps to 10000
|
||||
{"20000-40000", 100, "20000-29999"}, // hi above domain clamps to 29999
|
||||
{"10000-10000", 1, "10000-10099"}, // single port still yields its block
|
||||
{"10000-10000", 0, ""}, // single port has no whole block
|
||||
{"5000-6000", 0, ""}, // fully outside domain
|
||||
{"10000-20000, 21000-21099", 102, "10000-20099, 21000-21099"}, // adjacent not merged (gap 100)
|
||||
{"10000-20000, 21000-21099", 101, "10000-19999, 21000-21099"}, // adjacent not merged (gap 100)
|
||||
}
|
||||
for _, c := range cases {
|
||||
rs, err := ParseUserPorts(c.in)
|
||||
@@ -329,8 +331,8 @@ func TestUserPortCount(t *testing.T) {
|
||||
t.Errorf("default UserPortCount() = %d, want 200", n)
|
||||
}
|
||||
cfg.Net.UserPorts = "10000-20000, 25000-30000"
|
||||
if n := cfg.UserPortCount(); n != 151 {
|
||||
t.Errorf("UserPortCount() = %d, want 151", n)
|
||||
if n := cfg.UserPortCount(); n != 150 {
|
||||
t.Errorf("UserPortCount() = %d, want 150", n)
|
||||
}
|
||||
cfg.Net.UserPorts = "garbage"
|
||||
if n := cfg.UserPortCount(); n != 200 {
|
||||
@@ -346,8 +348,8 @@ func TestFillAutoUserPortsEnv(t *testing.T) {
|
||||
if err := c.FillAuto(); err != nil {
|
||||
t.Fatalf("FillAuto: %v", err)
|
||||
}
|
||||
if c.Net.UserPorts != "10000-20099, 25000-29999" {
|
||||
t.Errorf("user_ports = %q, want canonical 10000-20099, 25000-29999", c.Net.UserPorts)
|
||||
if c.Net.UserPorts != "10000-19999, 25000-29999" {
|
||||
t.Errorf("user_ports = %q, want canonical 10000-19999, 25000-29999", c.Net.UserPorts)
|
||||
}
|
||||
|
||||
// A bad env value must fail loudly, not silently default.
|
||||
|
||||
Reference in new issue
Block a user