From 2bb92e25e24c41faf5d7735aa11a6bdf8dc11750 Mon Sep 17 00:00:00 2001 From: wangchuan Date: Sat, 18 Jul 2026 11:30:17 +0800 Subject: [PATCH] feat(driver): configure sandbox resources --- .env.example | 11 +- pkg/config/config.go | 68 ++++++++---- pkg/config/config_test.go | 103 ++++++------------ pkg/driver/boxlite_cgo.go | 7 +- pkg/driver/microsandbox_runtime.go | 29 ++--- pkg/driver/microsandbox_runtime_test.go | 8 +- .../runtime_mount_manifest_smoke_test.go | 12 +- pkg/driver/sandbox_resources.go | 30 +++++ pkg/driver/sandbox_resources_test.go | 29 +++++ 9 files changed, 168 insertions(+), 129 deletions(-) create mode 100644 pkg/driver/sandbox_resources.go create mode 100644 pkg/driver/sandbox_resources_test.go diff --git a/.env.example b/.env.example index d4d109614..cb1d0ce37 100644 --- a/.env.example +++ b/.env.example @@ -113,18 +113,19 @@ DEFAULT_IMAGE=ghcr.io/chaitin/agent-compose-guest:latest # WORKSPACE_CLEANUP_TTL=0 # IMAGE_CACHE_CLEANUP_TTL=0 -# BoxLite runtime storage. +# BoxLite and Microsandbox microVM resources. # -------------------------------------- # BOXLITE_HOME=/data/boxlite # BOX_ROOTFS_PATH= -# BOX_DISK_SIZE_GB=6 +# SANDBOX_CPUS=4 +# Unified BoxLite/Microsandbox default; override for larger workloads. +# SANDBOX_MEMORY_MIB=4096 +# SANDBOX_DISK_SIZE_GB=6 # Docker and MicroSandbox storage. # -------------------------------------- # DOCKER_HOME=/data/docker -# Microsandbox bind mounts get this guest-write quota. Defaults to -# BOX_DISK_SIZE_GB when unset. -# MICROSANDBOX_BIND_QUOTA_GB=6 +# Microsandbox bind mounts use SANDBOX_DISK_SIZE_GB as their guest-write quota. # MICROSANDBOX_INSECURE_REGISTRIES= # --------------------------------------------------------------------------- diff --git a/pkg/config/config.go b/pkg/config/config.go index 4b2d20d85..5c6c57fcc 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -8,6 +8,7 @@ import ( "net/url" "os" "path/filepath" + "strconv" "strings" "time" @@ -19,6 +20,12 @@ const DefaultAgentComposeSocketPath = "/var/run/agent-compose.sock" const DefaultAgentTimeout = 10 * time.Hour const defaultGuestHomePath = "/root" +const ( + DefaultSandboxCPUs uint8 = 4 + DefaultSandboxMemoryMiB uint32 = 4096 + DefaultSandboxDiskSizeGB int32 = 6 +) + const ( RuntimeDriverBoxlite = "boxlite" RuntimeDriverDocker = "docker" @@ -67,14 +74,15 @@ type Config struct { MicrosandboxLibPath string MicrosandboxDefaultImage string MicrosandboxInsecure []string - MicrosandboxBindQuotaGB int DefaultImage string BoxRootfsPath string ImageRegistry string ImageStoreMode string ImageCacheRoot string ImageInsecureRegistries []string - BoxDiskSizeGB int + SandboxCPUs uint8 + SandboxMemoryMiB uint32 + SandboxDiskSizeGB int32 CacheTTL time.Duration CleanupInterval time.Duration WorkspaceCleanupTTL time.Duration @@ -273,25 +281,9 @@ func NewConfig(di do.Injector) (*Config, error) { } imageInsecureRegistries := splitAndTrimEnv(os.Getenv("IMAGE_INSECURE_REGISTRIES")) - boxDiskSizeGB := 6 - if raw := os.Getenv("BOX_DISK_SIZE_GB"); raw != "" { - var parsed int - if _, err := fmt.Sscanf(raw, "%d", &parsed); err != nil || parsed <= 0 { - logger.Warn("failed to parse BOX_DISK_SIZE_GB", "value", raw, "error", err) - } else { - boxDiskSizeGB = parsed - } - } - microsandboxBindQuotaGB := boxDiskSizeGB - if raw := os.Getenv("MICROSANDBOX_BIND_QUOTA_GB"); raw != "" { - var parsed int - if _, err := fmt.Sscanf(raw, "%d", &parsed); err != nil || parsed <= 0 { - logger.Warn("failed to parse MICROSANDBOX_BIND_QUOTA_GB", "value", raw, "error", err) - } else { - microsandboxBindQuotaGB = parsed - } - } - + sandboxCPUs := positiveUint8Env(logger, "SANDBOX_CPUS", DefaultSandboxCPUs) + sandboxMemoryMiB := positiveMemoryMiBEnv(logger, "SANDBOX_MEMORY_MIB", DefaultSandboxMemoryMiB) + sandboxDiskSizeGB := positiveDiskSizeGBEnv(logger, "SANDBOX_DISK_SIZE_GB", DefaultSandboxDiskSizeGB) cacheTTL := 7 * 24 * time.Hour cacheTTLRaw, err := envWithLegacy(logger, "CACHE_TTL", "BOX_CACHE_TTL") if err != nil { @@ -483,14 +475,15 @@ func NewConfig(di do.Injector) (*Config, error) { MicrosandboxLibPath: microsandboxLibPath, MicrosandboxDefaultImage: microsandboxDefaultImage, MicrosandboxInsecure: microsandboxInsecure, - MicrosandboxBindQuotaGB: microsandboxBindQuotaGB, DefaultImage: defaultImage, BoxRootfsPath: boxRootfsPath, ImageRegistry: imageRegistry, ImageStoreMode: imageStoreMode, ImageCacheRoot: imageCacheRoot, ImageInsecureRegistries: imageInsecureRegistries, - BoxDiskSizeGB: boxDiskSizeGB, + SandboxCPUs: sandboxCPUs, + SandboxMemoryMiB: sandboxMemoryMiB, + SandboxDiskSizeGB: sandboxDiskSizeGB, CacheTTL: cacheTTL, CleanupInterval: cleanupInterval, WorkspaceCleanupTTL: workspaceCleanupTTL, @@ -815,3 +808,32 @@ func splitAndTrimEnv(value string) []string { } return items } + +func positiveUint8Env(logger *slog.Logger, name string, defaultValue uint8) uint8 { + return uint8(positiveUintEnv(logger, name, uint64(defaultValue), 8)) +} + +func positiveMemoryMiBEnv(logger *slog.Logger, name string, defaultValue uint32) uint32 { + // BoxLite accepts memory through a signed C int, so the shared value must + // fit both that API and Microsandbox's uint32 option. + return uint32(positiveUintEnv(logger, name, uint64(defaultValue), 31)) +} + +func positiveDiskSizeGBEnv(logger *slog.Logger, name string, defaultValue int32) int32 { + // Microsandbox exposes the bind quota in MiB as uint32. Restrict the GiB + // value so multiplying it by 1024 cannot wrap. + return int32(positiveUintEnv(logger, name, uint64(defaultValue), 22)) +} + +func positiveUintEnv(logger *slog.Logger, name string, defaultValue uint64, bitSize int) uint64 { + raw := strings.TrimSpace(os.Getenv(name)) + if raw == "" { + return defaultValue + } + parsed, err := strconv.ParseUint(raw, 10, bitSize) + if err != nil || parsed == 0 { + logger.Warn("failed to parse positive integer environment variable", "name", name, "value", raw, "error", err) + return defaultValue + } + return parsed +} diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index da94e8598..41ac5ddef 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -131,7 +131,7 @@ func testNewConfigParsesEnvironment(t *testing.T) { t.Setenv("IMAGE_STORE_MODE", "oci") t.Setenv("IMAGE_CACHE_ROOT", filepath.Join(root, "custom-images")) t.Setenv("IMAGE_INSECURE_REGISTRIES", "oci-one.example; oci-two.example\noci-three.example") - t.Setenv("BOX_DISK_SIZE_GB", "11") + t.Setenv("SANDBOX_DISK_SIZE_GB", "11") t.Setenv("CACHE_TTL", "2h") t.Setenv("CLEANUP_INTERVAL", "30m") t.Setenv("WORKSPACE_CLEANUP_TTL", "24h") @@ -173,8 +173,8 @@ func testNewConfigParsesEnvironment(t *testing.T) { if config.LLMTimeout != 7*time.Second || config.AgentTimeout != 8*time.Second { t.Fatalf("timeouts = %s/%s", config.LLMTimeout, config.AgentTimeout) } - if config.BoxDiskSizeGB != 11 || config.CacheTTL != 2*time.Hour { - t.Fatalf("box disk/cache = %d/%s", config.BoxDiskSizeGB, config.CacheTTL) + if config.SandboxDiskSizeGB != 11 || config.CacheTTL != 2*time.Hour { + t.Fatalf("sandbox disk/cache = %d/%s", config.SandboxDiskSizeGB, config.CacheTTL) } if config.CleanupInterval != 30*time.Minute || config.WorkspaceCleanupTTL != 24*time.Hour || config.ImageCacheCleanupTTL != 48*time.Hour { t.Fatalf("cleanup config = %s/%s/%s", config.CleanupInterval, config.WorkspaceCleanupTTL, config.ImageCacheCleanupTTL) @@ -865,7 +865,7 @@ func testDefaultDataRootFallsBackToHome(t *testing.T) { } } -func TestBoxDiskSizeGB(t *testing.T) { +func TestBoxResources(t *testing.T) { newCfg := func(t *testing.T) *Config { t.Helper() root := t.TempDir() @@ -879,91 +879,58 @@ func TestBoxDiskSizeGB(t *testing.T) { return cfg } - t.Run("default is 6 for all VM-type drivers", func(t *testing.T) { + t.Run("defaults", func(t *testing.T) { cfg := newCfg(t) - if cfg.BoxDiskSizeGB != 6 { - t.Fatalf("BoxDiskSizeGB = %d, want 6 (default)", cfg.BoxDiskSizeGB) + if cfg.SandboxCPUs != 4 || cfg.SandboxMemoryMiB != 4096 || cfg.SandboxDiskSizeGB != 6 { + t.Fatalf("sandbox resources = %d CPUs/%d MiB/%d GiB, want 4/4096/6", cfg.SandboxCPUs, cfg.SandboxMemoryMiB, cfg.SandboxDiskSizeGB) } }) - t.Run("BOX_DISK_SIZE_GB sets the value", func(t *testing.T) { - t.Setenv("BOX_DISK_SIZE_GB", "11") + t.Run("environment sets shared resources", func(t *testing.T) { + t.Setenv("SANDBOX_CPUS", "8") + t.Setenv("SANDBOX_MEMORY_MIB", "16384") + t.Setenv("SANDBOX_DISK_SIZE_GB", "11") cfg := newCfg(t) - if cfg.BoxDiskSizeGB != 11 { - t.Fatalf("BoxDiskSizeGB = %d, want 11", cfg.BoxDiskSizeGB) + if cfg.SandboxCPUs != 8 || cfg.SandboxMemoryMiB != 16384 || cfg.SandboxDiskSizeGB != 11 { + t.Fatalf("sandbox resources = %d CPUs/%d MiB/%d GiB, want 8/16384/11", cfg.SandboxCPUs, cfg.SandboxMemoryMiB, cfg.SandboxDiskSizeGB) } }) - t.Run("invalid value keeps the default", func(t *testing.T) { - t.Setenv("BOX_DISK_SIZE_GB", "abc") - cfg := newCfg(t) - if cfg.BoxDiskSizeGB != 6 { - t.Fatalf("BoxDiskSizeGB = %d, want 6", cfg.BoxDiskSizeGB) - } - }) - - t.Run("non-positive value keeps the default", func(t *testing.T) { - t.Setenv("BOX_DISK_SIZE_GB", "-3") - cfg := newCfg(t) - if cfg.BoxDiskSizeGB != 6 { - t.Fatalf("BoxDiskSizeGB = %d, want 6", cfg.BoxDiskSizeGB) - } - }) -} - -func TestMicrosandboxBindQuotaGB(t *testing.T) { - newCfg := func(t *testing.T) *Config { - t.Helper() - root := t.TempDir() - t.Setenv("DATA_ROOT", filepath.Join(root, "data")) - di := do.New() - do.ProvideValue(di, slog.Default()) - cfg, err := NewConfig(di) - if err != nil { - t.Fatalf("NewConfig returned error: %v", err) - } - return cfg - } - - t.Run("defaults to box disk size", func(t *testing.T) { + t.Run("SANDBOX_DISK_SIZE_GB sets the value", func(t *testing.T) { + t.Setenv("SANDBOX_DISK_SIZE_GB", "11") cfg := newCfg(t) - if cfg.MicrosandboxBindQuotaGB != 6 { - t.Fatalf("MicrosandboxBindQuotaGB = %d, want 6", cfg.MicrosandboxBindQuotaGB) + if cfg.SandboxDiskSizeGB != 11 { + t.Fatalf("SandboxDiskSizeGB = %d, want 11", cfg.SandboxDiskSizeGB) } }) - t.Run("follows BOX_DISK_SIZE_GB", func(t *testing.T) { - t.Setenv("BOX_DISK_SIZE_GB", "60") - cfg := newCfg(t) - if cfg.MicrosandboxBindQuotaGB != 60 { - t.Fatalf("MicrosandboxBindQuotaGB = %d, want 60", cfg.MicrosandboxBindQuotaGB) - } - }) - - t.Run("override sets the value", func(t *testing.T) { - t.Setenv("BOX_DISK_SIZE_GB", "60") - t.Setenv("MICROSANDBOX_BIND_QUOTA_GB", "96") + t.Run("invalid value keeps the default", func(t *testing.T) { + t.Setenv("SANDBOX_CPUS", "abc") + t.Setenv("SANDBOX_MEMORY_MIB", "abc") + t.Setenv("SANDBOX_DISK_SIZE_GB", "abc") cfg := newCfg(t) - if cfg.MicrosandboxBindQuotaGB != 96 { - t.Fatalf("MicrosandboxBindQuotaGB = %d, want 96", cfg.MicrosandboxBindQuotaGB) + if cfg.SandboxCPUs != 4 || cfg.SandboxMemoryMiB != 4096 || cfg.SandboxDiskSizeGB != 6 { + t.Fatalf("sandbox resources = %d CPUs/%d MiB/%d GiB, want defaults", cfg.SandboxCPUs, cfg.SandboxMemoryMiB, cfg.SandboxDiskSizeGB) } }) - t.Run("invalid override keeps the inherited default", func(t *testing.T) { - t.Setenv("BOX_DISK_SIZE_GB", "60") - t.Setenv("MICROSANDBOX_BIND_QUOTA_GB", "abc") + t.Run("non-positive value keeps the default", func(t *testing.T) { + t.Setenv("SANDBOX_CPUS", "0") + t.Setenv("SANDBOX_MEMORY_MIB", "-1") + t.Setenv("SANDBOX_DISK_SIZE_GB", "-3") cfg := newCfg(t) - if cfg.MicrosandboxBindQuotaGB != 60 { - t.Fatalf("MicrosandboxBindQuotaGB = %d, want 60", cfg.MicrosandboxBindQuotaGB) + if cfg.SandboxCPUs != 4 || cfg.SandboxMemoryMiB != 4096 || cfg.SandboxDiskSizeGB != 6 { + t.Fatalf("sandbox resources = %d CPUs/%d MiB/%d GiB, want defaults", cfg.SandboxCPUs, cfg.SandboxMemoryMiB, cfg.SandboxDiskSizeGB) } }) - t.Run("non-positive override keeps the inherited default", func(t *testing.T) { - t.Setenv("BOX_DISK_SIZE_GB", "60") - t.Setenv("MICROSANDBOX_BIND_QUOTA_GB", "0") + t.Run("out-of-range value keeps the default", func(t *testing.T) { + t.Setenv("SANDBOX_CPUS", "256") + t.Setenv("SANDBOX_MEMORY_MIB", "2147483648") + t.Setenv("SANDBOX_DISK_SIZE_GB", "4194304") cfg := newCfg(t) - if cfg.MicrosandboxBindQuotaGB != 60 { - t.Fatalf("MicrosandboxBindQuotaGB = %d, want 60", cfg.MicrosandboxBindQuotaGB) + if cfg.SandboxCPUs != 4 || cfg.SandboxMemoryMiB != 4096 || cfg.SandboxDiskSizeGB != 6 { + t.Fatalf("sandbox resources = %d CPUs/%d MiB/%d GiB, want defaults", cfg.SandboxCPUs, cfg.SandboxMemoryMiB, cfg.SandboxDiskSizeGB) } }) } diff --git a/pkg/driver/boxlite_cgo.go b/pkg/driver/boxlite_cgo.go index 7b7164d83..2bfa435a9 100644 --- a/pkg/driver/boxlite_cgo.go +++ b/pkg/driver/boxlite_cgo.go @@ -1002,9 +1002,10 @@ func (r *cgoSandboxRuntime) buildBoxOptions(ctx context.Context, sandbox *Sandbo C.boxlite_options_set_auto_remove(options, 0) C.boxlite_options_set_detach(options, 1) C.boxlite_options_set_network_enabled(options) - if r.config.BoxDiskSizeGB > 0 { - C.boxlite_options_set_disk_size_gb(options, C.int(r.config.BoxDiskSizeGB)) - } + resources := configuredSandboxResources(r.config) + C.boxlite_options_set_cpus(options, C.int(resources.CPUs)) + C.boxlite_options_set_memory(options, C.int(resources.MemoryMiB)) + C.boxlite_options_set_disk_size_gb(options, C.int(resources.DiskSizeGB)) for _, mount := range manifest.Mounts { hostPathCString := C.CString(mount.HostPath) diff --git a/pkg/driver/microsandbox_runtime.go b/pkg/driver/microsandbox_runtime.go index f87ea0528..8b808d924 100644 --- a/pkg/driver/microsandbox_runtime.go +++ b/pkg/driver/microsandbox_runtime.go @@ -719,12 +719,9 @@ func (r *microsandboxRuntime) ensureDockerDisk(sandboxID string) (string, error) } } // Create a sparse raw file then format it as ext4. Sized by - // BOX_DISK_SIZE_GB (shared with the boxlite driver; config default 6 GiB). + // SANDBOX_DISK_SIZE_GB (shared with the BoxLite driver; default 6 GiB). // Existing .raw files are reused as-is (see os.Stat check above). - sizeGB := r.config.BoxDiskSizeGB - if sizeGB <= 0 { - sizeGB = 6 // defensive: config normally guarantees a positive value - } + sizeGB := configuredSandboxResources(r.config).DiskSizeGB f, err := os.Create(path) if err != nil { return "", fmt.Errorf("create docker disk image %s: %w", path, err) @@ -941,8 +938,8 @@ func (r *microsandboxRuntime) createSandbox(ctx context.Context, session *Sandbo "guest_path", mount.GuestPath, "readonly", mount.ReadOnly, "quota_mib", bindMount.QuotaMiB, - "configured_bind_quota_gb", r.config.MicrosandboxBindQuotaGB, - "box_disk_size_gb", r.config.BoxDiskSizeGB, + "configured_bind_quota_gb", r.config.SandboxDiskSizeGB, + "sandbox_disk_size_gb", r.config.SandboxDiskSizeGB, ) } // Give docker its own disk-backed ext4 volume. The guest root is virtiofs, @@ -1014,6 +1011,7 @@ func (r *microsandboxRuntime) createSandbox(ctx context.Context, session *Sandbo rebindDisabled := false network := microsandbox.NetworkPolicy.AllowAll() network.DNS = µsandbox.DNSConfig{RebindProtection: &rebindDisabled} + resources := configuredSandboxResources(r.config) options := []microsandbox.SandboxOption{ microsandbox.WithImage(imageRef), microsandbox.WithWorkdir("/"), @@ -1023,11 +1021,8 @@ func (r *microsandboxRuntime) createSandbox(ctx context.Context, session *Sandbo microsandbox.WithPullPolicy(pullPolicy), microsandbox.WithMounts(mounts), microsandbox.WithLabels(map[string]string{microsandboxManagedLabel: "true", microsandboxSandboxIDLabel: session.Summary.ID}), - // Fixed microVM size: the SDK defaults (512MiB / 1 CPU) are too small - // for docker-in-VM workloads (pulling large images, building from a - // container). - microsandbox.WithMemory(8192), - microsandbox.WithCPUs(4), + microsandbox.WithMemory(resources.MemoryMiB), + microsandbox.WithCPUs(resources.CPUs), } if jupyterEnabled(proxyState) && proxyState.HostPort > 0 { options = append(options, microsandbox.WithPorts(map[uint16]uint16{uint16(proxyState.HostPort): uint16(proxyState.GuestPort)})) @@ -1048,14 +1043,8 @@ func (r *microsandboxRuntime) microsandboxBindMount(hostPath string, readonly bo return mount } -func (r *microsandboxRuntime) microsandboxBindQuotaGB() int { - if r.config == nil { - return 0 - } - if r.config.MicrosandboxBindQuotaGB > 0 { - return r.config.MicrosandboxBindQuotaGB - } - return r.config.BoxDiskSizeGB +func (r *microsandboxRuntime) microsandboxBindQuotaGB() int32 { + return configuredSandboxResources(r.config).DiskSizeGB } func (r *microsandboxRuntime) resolveMicrosandboxImageRef(ctx context.Context, imageRef, pullPolicy string, pullTimeout time.Duration) (string, []string, bool, error) { diff --git a/pkg/driver/microsandbox_runtime_test.go b/pkg/driver/microsandbox_runtime_test.go index c8382729d..eb77bf33a 100644 --- a/pkg/driver/microsandbox_runtime_test.go +++ b/pkg/driver/microsandbox_runtime_test.go @@ -245,7 +245,7 @@ func TestMicrosandboxDockerDiskPathUsesIdentityHash(t *testing.T) { } func TestMicrosandboxBindMountSetsConfiguredQuota(t *testing.T) { - runtime := µsandboxRuntime{config: &appconfig.Config{MicrosandboxBindQuotaGB: 60}} + runtime := µsandboxRuntime{config: &appconfig.Config{SandboxDiskSizeGB: 60}} mount := runtime.microsandboxBindMount("/host/session", false) @@ -258,7 +258,7 @@ func TestMicrosandboxBindMountSetsConfiguredQuota(t *testing.T) { } func TestMicrosandboxBindMountPreservesReadonly(t *testing.T) { - runtime := µsandboxRuntime{config: &appconfig.Config{MicrosandboxBindQuotaGB: 11}} + runtime := µsandboxRuntime{config: &appconfig.Config{SandboxDiskSizeGB: 11}} mount := runtime.microsandboxBindMount("/host/session", true) @@ -271,7 +271,7 @@ func TestMicrosandboxBindMountPreservesReadonly(t *testing.T) { } func TestMicrosandboxBindMountFallsBackToBoxDiskSize(t *testing.T) { - runtime := µsandboxRuntime{config: &appconfig.Config{BoxDiskSizeGB: 42}} + runtime := µsandboxRuntime{config: &appconfig.Config{SandboxDiskSizeGB: 42}} mount := runtime.microsandboxBindMount("/host/session", false) @@ -295,7 +295,7 @@ func testMicrosandboxConfig(t *testing.T) *appconfig.Config { MicrosandboxHome: home, MicrosandboxMSBPath: bin, MicrosandboxLibPath: lib, - BoxDiskSizeGB: 1, + SandboxDiskSizeGB: 1, } } diff --git a/pkg/driver/runtime_mount_manifest_smoke_test.go b/pkg/driver/runtime_mount_manifest_smoke_test.go index 0b6a2b04a..b11430377 100644 --- a/pkg/driver/runtime_mount_manifest_smoke_test.go +++ b/pkg/driver/runtime_mount_manifest_smoke_test.go @@ -86,13 +86,13 @@ func newRuntimeSmokeConfig(t *testing.T, driver string) *appconfig.Config { } _ = os.RemoveAll(root) }) - boxDiskSizeGB := 0 - if raw := strings.TrimSpace(os.Getenv("SMOKE_BOX_DISK_SIZE_GB")); raw != "" { - parsed, err := strconv.Atoi(raw) + var sandboxDiskSizeGB int32 + if raw := strings.TrimSpace(os.Getenv("SMOKE_SANDBOX_DISK_SIZE_GB")); raw != "" { + parsed, err := strconv.ParseInt(raw, 10, 32) if err != nil { - t.Fatalf("parse SMOKE_BOX_DISK_SIZE_GB=%q: %v", raw, err) + t.Fatalf("parse SMOKE_SANDBOX_DISK_SIZE_GB=%q: %v", raw, err) } - boxDiskSizeGB = parsed + sandboxDiskSizeGB = int32(parsed) } repoRoot := runtimeSmokeRepoRoot(t) config := &appconfig.Config{ @@ -107,7 +107,7 @@ func newRuntimeSmokeConfig(t *testing.T, driver string) *appconfig.Config { MicrosandboxDefaultImage: firstNonEmpty(os.Getenv("SMOKE_MICROSANDBOX_DEFAULT_IMAGE"), os.Getenv("SMOKE_DEFAULT_IMAGE"), "debian:bookworm-slim"), ImageRegistry: firstNonEmpty(os.Getenv("IMAGE_REGISTRY"), "docker.io"), BoxRootfsPath: strings.TrimSpace(os.Getenv("SMOKE_BOX_ROOTFS_PATH")), - BoxDiskSizeGB: boxDiskSizeGB, + SandboxDiskSizeGB: sandboxDiskSizeGB, CacheTTL: time.Hour, BoxliteRuntimeDir: firstNonEmpty(os.Getenv("BOXLITE_RUNTIME_DIR"), filepath.Join(repoRoot, "build", "boxlite", "runtime")), MicrosandboxMSBPath: firstNonEmpty(os.Getenv("MICROSANDBOX_MSB_PATH"), filepath.Join(repoRoot, "build", "microsandbox", "bin", "msb")), diff --git a/pkg/driver/sandbox_resources.go b/pkg/driver/sandbox_resources.go new file mode 100644 index 000000000..88c5ef5b8 --- /dev/null +++ b/pkg/driver/sandbox_resources.go @@ -0,0 +1,30 @@ +package driver + +import appconfig "agent-compose/pkg/config" + +type sandboxResources struct { + CPUs uint8 + MemoryMiB uint32 + DiskSizeGB int32 +} + +func configuredSandboxResources(config *appconfig.Config) sandboxResources { + resources := sandboxResources{ + CPUs: appconfig.DefaultSandboxCPUs, + MemoryMiB: appconfig.DefaultSandboxMemoryMiB, + DiskSizeGB: appconfig.DefaultSandboxDiskSizeGB, + } + if config == nil { + return resources + } + if config.SandboxCPUs > 0 { + resources.CPUs = config.SandboxCPUs + } + if config.SandboxMemoryMiB > 0 { + resources.MemoryMiB = config.SandboxMemoryMiB + } + if config.SandboxDiskSizeGB > 0 { + resources.DiskSizeGB = config.SandboxDiskSizeGB + } + return resources +} diff --git a/pkg/driver/sandbox_resources_test.go b/pkg/driver/sandbox_resources_test.go new file mode 100644 index 000000000..5b3359746 --- /dev/null +++ b/pkg/driver/sandbox_resources_test.go @@ -0,0 +1,29 @@ +package driver + +import ( + "testing" + + appconfig "agent-compose/pkg/config" +) + +func TestConfiguredSandboxResources(t *testing.T) { + t.Run("defaults nil and zero-valued config", func(t *testing.T) { + for _, config := range []*appconfig.Config{nil, {}} { + got := configuredSandboxResources(config) + if got.CPUs != 4 || got.MemoryMiB != 4096 || got.DiskSizeGB != 6 { + t.Fatalf("configuredSandboxResources() = %+v, want 4 CPUs, 4096 MiB, 6 GiB", got) + } + } + }) + + t.Run("uses configured values", func(t *testing.T) { + got := configuredSandboxResources(&appconfig.Config{ + SandboxCPUs: 8, + SandboxMemoryMiB: 16384, + SandboxDiskSizeGB: 20, + }) + if got.CPUs != 8 || got.MemoryMiB != 16384 || got.DiskSizeGB != 20 { + t.Fatalf("configuredSandboxResources() = %+v, want configured values", got) + } + }) +}