feat: permit using K8s quantity values as HELM_MAX_*

Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
pull/30743/head
Benoit Tigeot 9 months ago
parent 8b73a97bdf
commit 688a49b1c9
No known key found for this signature in database
GPG Key ID: 8E6D4FC8AEBDA62C

@ -25,12 +25,14 @@ package cli
import (
"fmt"
"log/slog"
"net/http"
"os"
"strconv"
"strings"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/rest"
@ -119,8 +121,8 @@ func New() *EnvSettings {
BurstLimit: envIntOr("HELM_BURST_LIMIT", defaultBurstLimit),
QPS: envFloat32Or("HELM_QPS", defaultQPS),
ColorMode: envColorMode(),
MaxChartSize: envInt64Or("HELM_MAX_CHART_SIZE", 100*1024*1024), // 100 MiB
MaxChartFileSize: envInt64Or("HELM_MAX_FILE_SIZE", 5*1024*1024), // 5 MiB
MaxChartSize: envInt64OrQuantityBytes("HELM_MAX_CHART_SIZE", 100*1024*1024), // 100 MiB
MaxChartFileSize: envInt64OrQuantityBytes("HELM_MAX_FILE_SIZE", 5*1024*1024), // 5 MiB
}
env.Debug, _ = strconv.ParseBool(os.Getenv("HELM_DEBUG"))
@ -220,18 +222,36 @@ func envFloat32Or(name string, def float32) float32 {
return float32(ret)
}
// We want to handle int64 like returned by https://pkg.go.dev/io/fs#FileInfo
func envInt64Or(name string, def int64) int64 {
// Tries to parse as a k8s Quantity first, falls back to plain int64 parsing.
func envInt64OrQuantityBytes(name string, def int64) int64 {
if name == "" {
return def
}
envVal := envOr(name, strconv.FormatInt(def, 10))
ret, err := strconv.ParseInt(envVal, 10, 64)
if err != nil {
fmt.Fprintf(os.Stderr, "Warning: Environment variable %s has invalid value %q (expected an integer): %v\n", name, envVal, err)
envVal := os.Getenv(name)
if envVal == "" {
return def
}
return ret
envVal = strings.TrimSpace(envVal)
if q, err := resource.ParseQuantity(envVal); err == nil {
if v, ok := q.AsInt64(); ok {
return v
}
f := q.AsApproximateFloat64()
if f > 0 && f < float64(^uint64(0)>>1) {
return int64(f)
}
slog.Warn("Environment variable %s is too large to fit in int64: %q", name, envVal)
return def
}
if v, err := strconv.ParseInt(envVal, 10, 64); err == nil {
return v
}
slog.Warn("Environment variable %s has invalid value %q (expected int or k8s Quantity like 512Mi): using default %d", name, envVal, def)
return def
}
func envCSV(name string) (ls []string) {

@ -242,7 +242,7 @@ func TestEnvOrBool(t *testing.T) {
}
}
func TestEnvInt64Or(t *testing.T) {
func TestEnvInt64OrQuantityBytes(t *testing.T) {
envName := "TEST_ENV_INT64"
tests := []struct {
@ -253,9 +253,9 @@ func TestEnvInt64Or(t *testing.T) {
expected int64
}{
{
name: "empty env with default",
name: "empty env name uses default",
env: "",
val: "",
val: "999",
def: 100,
expected: 100,
},
@ -280,16 +280,78 @@ func TestEnvInt64Or(t *testing.T) {
def: 200,
expected: 200,
},
// Quantity cases (bytes)
{
name: "quantity Mi",
env: envName,
val: "512Mi",
def: 100,
expected: 512 * 1024 * 1024,
},
{
name: "quantity Gi",
env: envName,
val: "2Gi",
def: 100,
expected: 2 * 1024 * 1024 * 1024,
},
{
name: "quantity Ki",
env: envName,
val: "4096Ki",
def: 100,
expected: 4096 * 1024,
},
{
name: "decimal SI 1G (base10)",
env: envName,
val: "1G",
def: 100,
// 1G in decimal SI is 1,000,000,000 bytes
expected: 1_000_000_000,
},
{
name: "decimal SI 500M (base10)",
env: envName,
val: "500M",
def: 100,
expected: 500_000_000,
},
{
name: "lowercase suffix returns default with error message",
env: envName,
val: "1gi",
def: 100,
expected: 100, // Returns default but prints error about uppercase requirement
},
{
name: "whitespace trimmed",
env: envName,
val: " 256Mi ",
def: 100,
expected: 256 * 1024 * 1024,
},
{
name: "too large to fit in int64 returns default",
env: envName,
// ~9.22e18 is max int64; use larger than that to trigger overflow handling.
val: "10000000000Gi", // 10,000,000,000 * 1024^3 bytes ≈ 1.07e22
def: 1234,
expected: 1234,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Clear previous value to avoid bleed between tests
t.Setenv(envName, "")
if tt.env != "" {
t.Setenv(tt.env, tt.val)
}
actual := envInt64Or(tt.env, tt.def)
actual := envInt64OrQuantityBytes(tt.env, tt.def)
if actual != tt.expected {
t.Errorf("expected result %d, got %d", tt.expected, actual)
t.Errorf("expected result %d, got %d (env=%q val=%q def=%d)", tt.expected, actual, tt.env, tt.val, tt.def)
}
})
}

Loading…
Cancel
Save