Skip to content

Commit 95e1903

Browse files
tomassrnkaclaude
andcommitted
feat: use {version}/{arch} path for Firecracker binaries
Standardize Firecracker binary paths to include architecture: {version}/{arch}/firecracker (e.g. v1.12.1/arm64/firecracker) This aligns with the fc-kernels convention of {version}/{arch}/ and prepares for multi-arch production deployments. Changes: - config.go: FirecrackerPath includes runtime.GOARCH in path - create-build: download FC from GCS bucket (not GitHub releases), using {version}/{arch}/firecracker with legacy fallback on 404 - create-build: add errNotFound sentinel for reliable 404 detection, handle url.JoinPath errors explicitly - script_builder_test.go: update expected paths for arch directory Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 3aa0ff4 commit 95e1903

3 files changed

Lines changed: 58 additions & 10 deletions

File tree

packages/orchestrator/cmd/create-build/main.go

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"net/url"
1212
"os"
1313
"path/filepath"
14+
"runtime"
1415
"strings"
1516
"time"
1617

@@ -417,30 +418,71 @@ func setupKernel(ctx context.Context, dir, version string) error {
417418
return nil
418419
}
419420

420-
kernelURL, _ := url.JoinPath("https://storage.googleapis.com/e2b-prod-public-builds/kernels/", version, "vmlinux.bin")
421+
// On arm64, try arch-specific URL first (e.g. .../vmlinux-6.1.102/arm64/vmlinux.bin)
422+
if runtime.GOARCH == "arm64" {
423+
archURL, err := url.JoinPath("https://storage.googleapis.com/e2b-prod-public-builds/kernels/", version, "arm64", "vmlinux.bin")
424+
if err != nil {
425+
return fmt.Errorf("invalid kernel URL for arm64: %w", err)
426+
}
427+
fmt.Printf("⬇ Downloading kernel %s (arm64)...\n", version)
428+
if err := download(ctx, archURL, dstPath, 0o644); err == nil {
429+
return nil
430+
} else if !errors.Is(err, errNotFound) {
431+
return fmt.Errorf("failed to download arm64 kernel: %w", err)
432+
}
433+
fmt.Printf(" arm64 kernel not found, trying generic URL...\n")
434+
}
435+
436+
kernelURL, err := url.JoinPath("https://storage.googleapis.com/e2b-prod-public-builds/kernels/", version, "vmlinux.bin")
437+
if err != nil {
438+
return fmt.Errorf("invalid kernel URL: %w", err)
439+
}
421440
fmt.Printf("⬇ Downloading kernel %s...\n", version)
422441

423442
return download(ctx, kernelURL, dstPath, 0o644)
424443
}
425444

426445
func setupFC(ctx context.Context, dir, version string) error {
427-
dstPath := filepath.Join(dir, version, "firecracker")
446+
arch := runtime.GOARCH
447+
dstPath := filepath.Join(dir, version, arch, "firecracker")
448+
428449
if err := os.MkdirAll(filepath.Dir(dstPath), 0o755); err != nil {
429450
return fmt.Errorf("mkdir firecracker dir: %w", err)
430451
}
431452

432453
if _, err := os.Stat(dstPath); err == nil {
433-
fmt.Printf("✓ Firecracker %s exists\n", version)
454+
fmt.Printf("✓ Firecracker %s (%s) exists\n", version, arch)
455+
456+
return nil
457+
}
458+
459+
// Download from GCS bucket with {version}/{arch}/firecracker path
460+
fcURL, err := url.JoinPath("https://storage.googleapis.com/e2b-prod-public-builds/fc-versions/", version, arch, "firecracker")
461+
if err != nil {
462+
return fmt.Errorf("invalid Firecracker URL: %w", err)
463+
}
464+
465+
fmt.Printf("⬇ Downloading Firecracker %s (%s)...\n", version, arch)
434466

467+
if err := download(ctx, fcURL, dstPath, 0o755); err == nil {
435468
return nil
469+
} else if !errors.Is(err, errNotFound) {
470+
return fmt.Errorf("failed to download Firecracker: %w", err)
436471
}
437472

438-
fcURL := fmt.Sprintf("https://github.com/e2b-dev/fc-versions/releases/download/%s/firecracker", version)
439-
fmt.Printf("⬇ Downloading Firecracker %s...\n", version)
473+
// Fallback to legacy path without arch directory
474+
legacyURL, err := url.JoinPath("https://storage.googleapis.com/e2b-prod-public-builds/fc-versions/", version, "firecracker")
475+
if err != nil {
476+
return fmt.Errorf("invalid Firecracker legacy URL: %w", err)
477+
}
478+
479+
fmt.Printf(" %s path not found, trying legacy URL...\n", arch)
440480

441-
return download(ctx, fcURL, dstPath, 0o755)
481+
return download(ctx, legacyURL, dstPath, 0o755)
442482
}
443483

484+
var errNotFound = errors.New("not found")
485+
444486
func download(ctx context.Context, url, path string, perm os.FileMode) error {
445487
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
446488
resp, err := (&http.Client{Timeout: 5 * time.Minute}).Do(req)
@@ -449,6 +491,9 @@ func download(ctx context.Context, url, path string, perm os.FileMode) error {
449491
}
450492
defer resp.Body.Close()
451493

494+
if resp.StatusCode == http.StatusNotFound {
495+
return fmt.Errorf("%w: %s", errNotFound, url)
496+
}
452497
if resp.StatusCode != http.StatusOK {
453498
return fmt.Errorf("HTTP %d: %s", resp.StatusCode, url)
454499
}

packages/orchestrator/internal/sandbox/fc/config.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package fc
22

33
import (
44
"path/filepath"
5+
"runtime"
56

67
"github.com/e2b-dev/infra/packages/orchestrator/internal/cfg"
78
)
@@ -35,7 +36,7 @@ func (t Config) HostKernelPath(config cfg.BuilderConfig) string {
3536
}
3637

3738
func (t Config) FirecrackerPath(config cfg.BuilderConfig) string {
38-
return filepath.Join(config.FirecrackerVersionsDir, t.FirecrackerVersion, FirecrackerBinaryName)
39+
return filepath.Join(config.FirecrackerVersionsDir, t.FirecrackerVersion, runtime.GOARCH, FirecrackerBinaryName)
3940
}
4041

4142
type RootfsPaths struct {

packages/orchestrator/internal/sandbox/fc/script_builder_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package fc
22

33
import (
4+
"fmt"
5+
"runtime"
46
"strings"
57
"testing"
68

@@ -47,7 +49,7 @@ func TestStartScriptBuilder_Build(t *testing.T) {
4749
"ln -s /orchestrator/sandbox/rootfs-test-sandbox-static-id.link /fc-vm/rootfs.ext4",
4850
"mkdir -p /fc-vm/6.1.0",
4951
"ln -s /fc-kernels/6.1.0/vmlinux.bin /fc-vm/6.1.0/vmlinux.bin",
50-
"ip netns exec ns-789 /fc-versions/1.4.0/firecracker --api-sock",
52+
fmt.Sprintf("ip netns exec ns-789 /fc-versions/1.4.0/%s/firecracker --api-sock", runtime.GOARCH),
5153
"fc-test-sandbox-static-id.sock",
5254
},
5355
},
@@ -72,7 +74,7 @@ func TestStartScriptBuilder_Build(t *testing.T) {
7274
"ln -s /orchestrator/sandbox/rootfs-legacy-sandbox-legacy-id.link /mnt/disks/fc-envs/v1/legacy-template/builds/legacy-build/rootfs.ext4",
7375
"mount -t tmpfs tmpfs /fc-vm/5.10.0 -o X-mount.mkdir",
7476
"ln -s /fc-kernels/5.10.0/vmlinux.bin /fc-vm/5.10.0/vmlinux.bin",
75-
"ip netns exec legacy-ns /fc-versions/1.3.0/firecracker --api-sock",
77+
fmt.Sprintf("ip netns exec legacy-ns /fc-versions/1.3.0/%s/firecracker --api-sock", runtime.GOARCH),
7678
"fc-legacy-sandbox-legacy-id.sock",
7779
},
7880
},
@@ -94,7 +96,7 @@ func TestStartScriptBuilder_Build(t *testing.T) {
9496
expectedScriptContent: []string{
9597
"mkdir -p /fc-vm/6.2.1",
9698
"ln -s /fc-kernels/6.2.1/vmlinux.bin /fc-vm/6.2.1/vmlinux.bin",
97-
"ip netns exec custom-ns-id /fc-versions/1.5.0-beta/firecracker --api-sock",
99+
fmt.Sprintf("ip netns exec custom-ns-id /fc-versions/1.5.0-beta/%s/firecracker --api-sock", runtime.GOARCH),
98100
"fc-custom-sandbox-custom-id.sock",
99101
},
100102
},

0 commit comments

Comments
 (0)