From 21359f757fc905b6e9a00b2a43a51267b642cc87 Mon Sep 17 00:00:00 2001 From: rldyourmnd Date: Tue, 1 Sep 2026 04:35:05 +0500 Subject: [PATCH] fix(image): give the builder the disk the CodeQL bundle actually needs The b21 build died at 'No space left on device' while packing the rootfs: the bundle adds 0.8 GiB of archive and 2.6 GiB unpacked, doubled transiently during publish, and the 16/24 GiB builder budgets predate it. Standard moves to 22, integration to 30, the validator bound follows to 32 with the reasoning in place, and the canary pools that share these manifests widen with their classes (container-canary 20 to 30, docker-container-canary 30 to 40) so a canary keeps the same disk shape as the class it canaries for. Golden fingerprints and the plan pins move with the change. Claude-Session: https://claude.ai/code/session_01LsGid6U5RrQdFvJmvYdGCF --- config/example-runner-1.yaml | 4 ++-- config/example-runner-2.yaml | 4 ++-- config/example-runner-3.yaml | 4 ++-- config/example-runner-4.yaml | 4 ++-- config/example-services.yaml | 4 ++-- config/golden-image-container-integration.yaml | 2 +- config/golden-image-container.yaml | 2 +- config/golden-image-integration.yaml | 2 +- config/golden-image.yaml | 2 +- internal/garmbootstrap/types.go | 4 ++-- internal/imagebuild/orchestrator_test.go | 4 ++-- internal/imagemanifest/manifest_test.go | 8 ++++---- internal/imagemanifest/validate.go | 7 +++++-- internal/imageplan/plan_test.go | 4 ++-- internal/incusplan/plan_test.go | 2 +- 15 files changed, 30 insertions(+), 27 deletions(-) diff --git a/config/example-runner-1.yaml b/config/example-runner-1.yaml index a7583d8a..8099dc46 100644 --- a/config/example-runner-1.yaml +++ b/config/example-runner-1.yaml @@ -126,7 +126,7 @@ pools: resources: vcpu: 2 memory_mib: 4096 - disk_gib: 30 + disk_gib: 40 max_running: 1 warm: target_ready: 0 @@ -144,7 +144,7 @@ pools: resources: vcpu: 2 memory_mib: 2048 - disk_gib: 20 + disk_gib: 30 max_running: 12 warm: target_ready: 0 diff --git a/config/example-runner-2.yaml b/config/example-runner-2.yaml index 5e7f0578..46569708 100644 --- a/config/example-runner-2.yaml +++ b/config/example-runner-2.yaml @@ -126,7 +126,7 @@ pools: resources: vcpu: 2 memory_mib: 4096 - disk_gib: 30 + disk_gib: 40 max_running: 1 warm: target_ready: 0 @@ -144,7 +144,7 @@ pools: resources: vcpu: 2 memory_mib: 2048 - disk_gib: 20 + disk_gib: 30 max_running: 12 warm: target_ready: 0 diff --git a/config/example-runner-3.yaml b/config/example-runner-3.yaml index a23a2fe6..939fc933 100644 --- a/config/example-runner-3.yaml +++ b/config/example-runner-3.yaml @@ -127,7 +127,7 @@ pools: resources: vcpu: 2 memory_mib: 4096 - disk_gib: 30 + disk_gib: 40 max_running: 1 warm: target_ready: 0 @@ -145,7 +145,7 @@ pools: resources: vcpu: 2 memory_mib: 2048 - disk_gib: 20 + disk_gib: 30 max_running: 12 warm: target_ready: 0 diff --git a/config/example-runner-4.yaml b/config/example-runner-4.yaml index a241927b..30ca30f4 100644 --- a/config/example-runner-4.yaml +++ b/config/example-runner-4.yaml @@ -138,7 +138,7 @@ pools: resources: vcpu: 2 memory_mib: 4096 - disk_gib: 30 + disk_gib: 40 max_running: 1 warm: target_ready: 0 @@ -156,7 +156,7 @@ pools: resources: vcpu: 2 memory_mib: 2048 - disk_gib: 20 + disk_gib: 30 max_running: 12 warm: target_ready: 0 diff --git a/config/example-services.yaml b/config/example-services.yaml index f5531f93..59dd8d72 100644 --- a/config/example-services.yaml +++ b/config/example-services.yaml @@ -153,7 +153,7 @@ pools: resources: vcpu: 2 memory_mib: 4096 - disk_gib: 30 + disk_gib: 40 max_running: 1 warm: target_ready: 0 @@ -171,7 +171,7 @@ pools: resources: vcpu: 2 memory_mib: 2048 - disk_gib: 20 + disk_gib: 30 max_running: 12 warm: target_ready: 0 diff --git a/config/golden-image-container-integration.yaml b/config/golden-image-container-integration.yaml index ed3f7d40..5428a58d 100644 --- a/config/golden-image-container-integration.yaml +++ b/config/golden-image-container-integration.yaml @@ -122,7 +122,7 @@ browser_smoke: binary_path: chrome-linux64/chrome guest: - builder_disk_gib: 24 + builder_disk_gib: 30 variant: integration browser: chromium docker_action_base_ref: nddev/gha-action-base:busybox-1-1.36.1-6ubuntu3.1 diff --git a/config/golden-image-container.yaml b/config/golden-image-container.yaml index d565f2f6..fa2f2d6d 100644 --- a/config/golden-image-container.yaml +++ b/config/golden-image-container.yaml @@ -119,7 +119,7 @@ toolchains: archive_sha256: 606e7e2dfc8bcc24e1b3a70a1043288a271ad2cc71cf42248fadc25f5938a497 guest: - builder_disk_gib: 16 + builder_disk_gib: 22 path_binaries: - name: actionlint version: v1.7.12 diff --git a/config/golden-image-integration.yaml b/config/golden-image-integration.yaml index 40b7ae08..86ab112e 100644 --- a/config/golden-image-integration.yaml +++ b/config/golden-image-integration.yaml @@ -113,7 +113,7 @@ toolchains: archive_sha256: 606e7e2dfc8bcc24e1b3a70a1043288a271ad2cc71cf42248fadc25f5938a497 guest: - builder_disk_gib: 24 + builder_disk_gib: 30 variant: integration docker_action_base_ref: nddev/gha-action-base:busybox-1-1.36.1-6ubuntu3.1 path_binaries: diff --git a/config/golden-image.yaml b/config/golden-image.yaml index 4f53a3a2..58ef81d6 100644 --- a/config/golden-image.yaml +++ b/config/golden-image.yaml @@ -113,7 +113,7 @@ toolchains: archive_sha256: 606e7e2dfc8bcc24e1b3a70a1043288a271ad2cc71cf42248fadc25f5938a497 guest: - builder_disk_gib: 20 + builder_disk_gib: 22 path_binaries: - name: actionlint version: v1.7.12 diff --git a/internal/garmbootstrap/types.go b/internal/garmbootstrap/types.go index d07f701b..0509f6a9 100644 --- a/internal/garmbootstrap/types.go +++ b/internal/garmbootstrap/types.go @@ -105,8 +105,8 @@ func PublishedScaleSets() []ScaleSetClass { class(FastScaleSetName, FastImage, FastFlavor, 16, "trusted", "repository", "public-internet", "trusted", false, false, 2, 3072, 30, false), class(UntrustedScaleSetName, UntrustedImage, UntrustedFlavor, 8, "untrusted", "none", "public-internet", "none", true, false, 4, 6144, 50, false), class(ReleaseScaleSetName, ReleaseImage, ReleaseFlavor, 1, "release", "oidc-only", "release-allowlist", "none", false, false, 4, 6144, 40, false), - class(ContainerCanaryScaleSetName, ContainerCanaryImage, ContainerCanaryFlavor, 12, "trusted", "none", "public-internet", "none", false, false, 2, 2048, 20, false), - class(DockerContainerCanaryScaleSetName, DockerContainerCanaryImage, DockerContainerCanaryFlavor, 1, "trusted", "repository", "public-internet", "none", true, false, 2, 4096, 30, false), + class(ContainerCanaryScaleSetName, ContainerCanaryImage, ContainerCanaryFlavor, 12, "trusted", "none", "public-internet", "none", false, false, 2, 2048, 30, false), + class(DockerContainerCanaryScaleSetName, DockerContainerCanaryImage, DockerContainerCanaryFlavor, 1, "trusted", "repository", "public-internet", "none", true, false, 2, 4096, 40, false), class(PriorityStandardScaleSetName, PriorityStandardImage, PriorityStandardFlavor, 16, "trusted", "repository", "public-internet", "none", false, false, 2, 4096, 30, true), class(PriorityIntegrationScaleSetName, PriorityIntegrationImage, PriorityIntegrationFlavor, 8, "trusted", "repository", "public-internet", "none", true, true, 4, 6144, 50, true), class(PriorityUntrustedScaleSetName, UntrustedImage, PriorityUntrustedFlavor, 8, "untrusted", "none", "public-internet", "none", true, false, 4, 6144, 50, true), diff --git a/internal/imagebuild/orchestrator_test.go b/internal/imagebuild/orchestrator_test.go index b121f608..60f6551d 100644 --- a/internal/imagebuild/orchestrator_test.go +++ b/internal/imagebuild/orchestrator_test.go @@ -164,7 +164,7 @@ func TestRecipeFingerprintIsDeterministic(t *testing.T) { // alias is part of the recipe, so a manifest whose contents changed under an // unchanged alias would otherwise ask the builder to produce different bytes // for a name that is already promoted. - if first != "sha256:ba41e9cfcebc856bfa3ee0b75f12d4e3f7a9b85598308491ea70f72b2fe2baff" { + if first != "sha256:f3067c98c8598549a1f81bd1c082aec5fb302fa31c52a986d17725fda57f9ac7" { t.Fatalf("deployed standard recipe fingerprint drifted: %q", first) } smoke, err := SmokeFingerprint(plan) @@ -536,7 +536,7 @@ func TestInstanceInitArgsPinContainerIsolation(t *testing.T) { t.Fatalf("instance init args %q do not contain %q", args, want) } } - if !strings.Contains(strings.Join(args, " "), "--device root,size=16GiB") { + if !strings.Contains(strings.Join(args, " "), "--device root,size=22GiB") { t.Fatalf("builder init args do not contain bounded root disk: %q", args) } smokeArgs := (&Orchestrator{}).instanceInitArgs(plan, strings.Repeat("b", 64), "smoke", 0) diff --git a/internal/imagemanifest/manifest_test.go b/internal/imagemanifest/manifest_test.go index 1e6e273b..75434e9d 100644 --- a/internal/imagemanifest/manifest_test.go +++ b/internal/imagemanifest/manifest_test.go @@ -31,8 +31,8 @@ func TestRepositoryManifestIsValidAndPinned(t *testing.T) { len(manifest.GoCacheSeed.Packages) != 1 || manifest.GoCacheSeed.Packages[0] != "./cmd/gha-fleet" { t.Fatalf("Go cache seed is not exactly pinned: %#v", manifest.GoCacheSeed) } - if manifest.Guest.BuilderDiskGiB != 20 { - t.Fatalf("builder disk = %d GiB, want 20", manifest.Guest.BuilderDiskGiB) + if manifest.Guest.BuilderDiskGiB != 22 { + t.Fatalf("builder disk = %d GiB, want 22", manifest.Guest.BuilderDiskGiB) } assertBakedToolchains(t, manifest) fingerprint, err := manifest.Fingerprint() @@ -42,7 +42,7 @@ func TestRepositoryManifestIsValidAndPinned(t *testing.T) { if !strings.HasPrefix(fingerprint, "sha256:") || len(fingerprint) != len("sha256:")+64 { t.Fatalf("unexpected fingerprint %q", fingerprint) } - if fingerprint != "sha256:47a1569f94fa824b69de51d5ee917a5e8b72cc4a4c9b727c214019f891615a56" { + if fingerprint != "sha256:fe9f3da1c5e7b997008fe2b8ea04e1d4e1cfdb60f30db69cabdb4d69e06fb4b8" { t.Fatalf("standard manifest fingerprint drifted: %q", fingerprint) } } @@ -103,7 +103,7 @@ func TestIntegrationManifestPinsDockerToolchain(t *testing.T) { if err != nil { t.Fatalf("load integration manifest: %v", err) } - if !manifest.Guest.DockerCapable() || manifest.Guest.BuilderDiskGiB != 24 { + if !manifest.Guest.DockerCapable() || manifest.Guest.BuilderDiskGiB != 30 { t.Fatalf("unexpected integration guest: %#v", manifest.Guest) } for _, pkg := range []string{"busybox-static", "docker-buildx", "docker-compose-v2", "docker.io", "pigz"} { diff --git a/internal/imagemanifest/validate.go b/internal/imagemanifest/validate.go index 5e273512..2caf643e 100644 --- a/internal/imagemanifest/validate.go +++ b/internal/imagemanifest/validate.go @@ -237,8 +237,11 @@ func (m Manifest) Validate() error { validateCompilerCache(add, m.CompilerCache) validateGoCacheSeed(add, m.GoCacheSeed) validateToolchains(add, m.Toolchains) - if m.Guest.BuilderDiskGiB < 12 || m.Guest.BuilderDiskGiB > 24 { - add("guest.builder_disk_gib", "must be between 12 and 24 GiB") + // 24 GiB fit the image until the CodeQL bundle joined the tool cache: + // 0.8 GiB of archive plus 2.6 GiB unpacked, doubled transiently while the + // rootfs is packed for publish. + if m.Guest.BuilderDiskGiB < 12 || m.Guest.BuilderDiskGiB > 32 { + add("guest.builder_disk_gib", "must be between 12 and 32 GiB") } variant := m.Guest.EffectiveVariant() if variant != "standard" && variant != "integration" { diff --git a/internal/imageplan/plan_test.go b/internal/imageplan/plan_test.go index fa75f8f8..8e8c4094 100644 --- a/internal/imageplan/plan_test.go +++ b/internal/imageplan/plan_test.go @@ -73,7 +73,7 @@ func TestBuildProducesDeterministicBoundedPlan(t *testing.T) { if first.InstanceConfig["security.privileged"] != "false" || first.InstanceConfig["security.idmap.isolated"] != "true" || first.InstanceConfig["security.nesting"] != "false" { t.Fatalf("container isolation is not explicit: %#v", first.InstanceConfig) } - if first.BuilderDiskGiB != 16 || first.SmokeRootDiskGiB != 30 { + if first.BuilderDiskGiB != 22 || first.SmokeRootDiskGiB != 30 { t.Fatalf("unexpected image/runtime disk policy: %#v", first) } names := make([]string, 0, len(first.Toolchains)) @@ -131,7 +131,7 @@ func TestBuildProducesPinnedDockerIntegrationPlan(t *testing.T) { } if plan.Image.EffectiveType() != "container" || plan.Variant != "integration" || plan.InstanceConfig["security.nesting"] != "true" || plan.InstanceConfig["security.idmap.isolated"] != "true" || - plan.SmokeRootDiskGiB != 50 || plan.BuilderDiskGiB != 24 { + plan.SmokeRootDiskGiB != 50 || plan.BuilderDiskGiB != 30 { t.Fatalf("unexpected integration plan: %#v", plan) } if plan.DockerActionBaseRef != "nddev/gha-action-base:busybox-1-1.36.1-6ubuntu3.1" { diff --git a/internal/incusplan/plan_test.go b/internal/incusplan/plan_test.go index 1a17d051..58a36e1e 100644 --- a/internal/incusplan/plan_test.go +++ b/internal/incusplan/plan_test.go @@ -170,7 +170,7 @@ func TestContainerCanaryProfileIsUnprivilegedAndNonNested(t *testing.T) { profile.Config["security.privileged"] != "false" || profile.Config["security.nesting"] != "false" || profile.Config["security.syscalls.intercept.mknod"] != "false" || profile.Config["security.syscalls.intercept.setxattr"] != "false" || - profile.Config["security.secureboot"] != "" || profile.Devices["root"]["size"] != "20GiB" { + profile.Config["security.secureboot"] != "" || profile.Devices["root"]["size"] != "30GiB" { t.Fatalf("container profile is not fail-closed: %#v", profile) } }