feat(orch): distro-aware template provisioning (Fedora/RHEL/Arch base images) - #3381
tomassrnka wants to merge 20 commits into
Conversation
PR SummaryMedium Risk Overview Boot and runtime paths are broadened for minimal images: provisioning runs through baked busybox without Reviewed by Cursor Bugbot for commit 369027b. Bugbot is set up for automated code reviews on this repo. Configure here. |
❌ 2 Tests Failed:
View the top 3 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 68d6b0ad2a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| Key: "arch", | ||
| IDs: []string{"arch", "archarm"}, | ||
| Packages: []string{ | ||
| "systemd", "shadow", "openssh", "sudo", "chrony", "socat", "curl", | ||
| "ca-certificates", "fuse3", "iptables", "git", "nfs-utils", "less", | ||
| "nftables", "iputils", "jq", "bash", | ||
| }, | ||
| PkgQueryBody: `pacman -Q "$1" >/dev/null 2>&1`, | ||
| PkgInstall: "pacman -Sy --noconfirm\n pacman -S --noconfirm --needed \"$@\"", | ||
| InitBinary: "/usr/lib/systemd/systemd", | ||
| TimeSyncUnit: "chronyd", | ||
| AdminGroup: "wheel", | ||
| CABundle: "/etc/ssl/certs/ca-certificates.crt", | ||
| CARefresh: "update-ca-certificates", |
There was a problem hiding this comment.
🟡 The Arch profile's PkgInstall runs pacman -Sy --noconfirm (sync package databases) followed by a plain pacman -S --noconfirm --needed "$@" (no -u), which is the Arch-documented-unsupported partial-upgrade pattern.
Extended reasoning...
The bug: in distro.go's arch Profile, PkgInstall is:
PkgInstall: "pacman -Sy --noconfirm\n pacman -S --noconfirm --needed \"$@\"",pacman -Sy refreshes the local package databases to the latest state of the configured repos, but does not upgrade any already-installed packages. The subsequent pacman -S --needed "$@" then resolves and installs the new packages against that freshly-synced database. This is exactly the "partial upgrade" pattern that the Arch Wiki explicitly documents as unsupported: because Arch is a rolling release with no cross-version ABI/compatibility guarantees between package versions, a newly-installed package (or one of its direct dependencies) can end up linked against a newer shared-library/glibc version than what's already present on the rest of the (un-upgraded) base image. The correct, supported invocation is pacman -Syu --noconfirm (sync + full system upgrade) before/while installing new packages.
Trigger path: provision.sh's e2b_pkg_install is only invoked when MISSING is non-empty — i.e., when one or more of the profile's declared packages (systemd, openssh, sudo, chrony, socat, curl, ca-certificates, fuse3, git, nfs-utils, less, nftables, iputils, jq, bash) is absent from the base image. On a minimal Arch base (e.g. the official archlinux OCI image), most of these will be missing, so this code path runs on essentially every Arch template build, not as a rare edge case.
Why nothing prevents it: there's no -u flag, no separate pacman -Syu step, and no test asserting the invocation is upgrade-safe — distro_test.go's TestFamiliesDiffer only checks that the arch install string contains "pacman", it doesn't validate the flags used.
Impact: because Arch base images here are pulled from OCI/dockerhub, they can lag the live Arch repos by an arbitrary amount of time (dockerhub images aren't rebuilt on every repo update). The longer that lag, the more likely a partial upgrade pulls in a package built against newer shared libraries than what's on the stale base, which can manifest as broken dependency resolution during install, or as binaries that crash or misbehave at runtime after boot — a failure mode that's intermittent and hard to diagnose after the fact, since it depends on the staleness of whatever base image happened to be pulled.
Proof walkthrough:
- Arch OCI base image is built from a repo snapshot at time T0.
- Time passes; upstream Arch repos move to T1, where some library
libfoo.so.2has been superseded bylibfoo.so.3and a dependent packagebarhas been rebuilt againstlibfoo.so.3. - Template build runs
e2b_pkg_install, which doespacman -Sy— this syncs the local package DB metadata to T1, but the base image's installed packages (including whatever already depends onlibfoo.so.2) remain at T0. pacman -S --needed bar(one of the MISSING packages, or pulled in as its dependency) installsbarat its T1 build, dynamically linked againstlibfoo.so.3.- If
libfoo.so.2is still what's installed on the base (never upgraded because-uwas never run),barfails to resolvelibfoo.so.3at runtime and crashes, or dependency resolution during thepacman -Sstep itself fails/conflicts.
Fix: change the arch profile's PkgInstall to run pacman -Syu --noconfirm before/instead of the bare -Sy, e.g. "pacman -Syu --noconfirm\n pacman -S --noconfirm --needed \"$@\"", matching the Arch-documented supported invocation.
Severity note: this only affects the new Arch profile (v1, no existing templates to regress), and the failure is probabilistic — it only manifests when the pulled base image has actually lagged the live repos and a newly-installed package needs a newer shared library. It doesn't guarantee a build failure on every run. The fix itself is a one-token change with no meaningful downside. Given all four independent verifiers leaned toward "nit" (with one flagging it as defensibly either way), this is a worthwhile correctness fix but not a merge-blocker.
There was a problem hiding this comment.
Valid — the Arch profile uses pacman -Sy then -S (the partial-upgrade pattern Arch discourages). Routed to the engineer to move to -Syu (or document the fresh-base assumption) and verify on a real Arch build. Open until resolved.
| Key: "rhel", | ||
| // Fedora, RHEL, CentOS Stream, Rocky, Alma, Oracle Linux, Amazon Linux. | ||
| IDs: []string{"fedora", "rhel", "centos", "rocky", "almalinux", "ol", "amzn"}, | ||
| Packages: []string{ | ||
| "systemd", "shadow-utils", "passwd", "openssh-server", "sudo", "chrony", | ||
| "socat", "curl", "ca-certificates", "fuse3", "iptables-nft", "git", | ||
| "nfs-utils", "less", "nftables", "iputils", "jq", "bash", | ||
| }, | ||
| PkgQueryBody: `rpm -q "$1" >/dev/null 2>&1`, | ||
| PkgInstall: `dnf -y --allowerasing install "$@" 2>/dev/null || microdnf -y install "$@"`, | ||
| InitBinary: "/usr/lib/systemd/systemd", | ||
| TimeSyncUnit: "chronyd", | ||
| AdminGroup: "wheel", | ||
| CABundle: "/etc/ssl/certs/ca-certificates.crt", | ||
| CARefresh: "update-ca-trust extract", | ||
| }, |
There was a problem hiding this comment.
🟡 The rhel Profile's PkgInstall (dnf ... 2>/dev/null || microdnf ...) has no yum fallback, but the profile's own declared IDs include centos and amzn — CentOS 7 and Amazon Linux 2 ship yum only (no dnf/microdnf), so provisioning a declared-supported base with any missing package aborts the whole script under set -eu. The 2>/dev/null on the dnf branch also hides the real 'command not found' cause, leaving only microdnf's failure in the logs. A one-line || yum -y install "$@" fallback fixes it.
Extended reasoning...
The rhel Profile in distro.go registers IDs: []string{"fedora", "rhel", "centos", "rocky", "almalinized", "ol", "amzn"} and sets:
PkgInstall: `dnf -y --allowerasing install "$@" 2>/dev/null || microdnf -y install "$@"`,There is no yum fallback. CentOS 7 (ID=centos) and Amazon Linux 2 (ID=amzn) are both explicitly claimed by this profile via their os-release ID, but neither ships dnf or microdnf — dnf was introduced in RHEL/CentOS 8 and Amazon Linux 2023, and microdnf only appears in dnf-based minimal container images (e.g. ubi-minimal), not on centos:7 or amazonlinux:2.
Trigger path: provision.sh sources /etc/os-release, sets E2B_DISTRO_ID, and the generated selector (ShellSelector() in distro.go) matches centos/amzn into the rhel case arm, defining e2b_pkg_install() from the string above. If any package in the required set (systemd, openssh-server, chrony, socat, fuse3, nftables, etc. — a plausible gap on a minimal base image) is missing, provision.sh calls e2b_pkg_install $MISSING unconditionally (not guarded by if/||). On CentOS 7 / AL2 both dnf and microdnf resolve to "command not found" (exit 127), so e2b_pkg_install returns non-zero. Because provision.sh runs under set -eu and the call isn't wrapped in a conditional, the entire script aborts immediately — the template build fails.
Why nothing else catches this: TestRHELFamilyAliases in distro_test.go only asserts that "centos" and "amzn" are present in the rhel profile's IDs slice — it never exercises the actual install command against a real CentOS 7 or AL2 rootfs, so the gap is invisible to the test suite. The PR's own validation section says the rendered provisioning was exercised against "real Ubuntu 22.04 / Fedora 40 / Alpine rootfses" — CentOS 7 / AL2 specifically were not part of that manual check either.
Secondary issue: the 2>/dev/null on the dnf branch swallows dnf's stderr, so on these bases only microdnf's "command not found" surfaces in the build logs, not dnf's. This directly undercuts the PR's stated design goal of "exit[ing] fast with a clear, customer-visible error" instead of an opaque package-manager failure — the actual root cause (no dnf on this release) is masked, and the user only sees the microdnf error.
Step-by-step proof (CentOS 7 base image):
- Base image os-release has
ID="centos",VERSION_ID="7". provision.shsetsE2B_DISTRO_ID=centos, selector matches therhelcase arm.MISSINGends up non-empty for at least one required package not preinstalled on a minimal CentOS 7 image (e.g.nftables,fuse3, orjq).e2b_pkg_install $MISSINGexpands todnf -y --allowerasing install <pkgs> 2>/dev/null || microdnf -y install <pkgs>.dnf: command not found(exit 127, stderr discarded) → falls through tomicrodnf: command not found(exit 127) → function returns 127.set -eutriggers immediate script exit with a non-zero status.provisionSandbox(provision.go) readsE2B_PROVISIONING_EXIT:<nonzero>and the build fails, even thoughcentosis one of the profile's explicitly declared, "supported" IDs.
Fix: append a yum fallback, e.g. dnf -y --allowerasing install "$@" 2>/dev/null || microdnf -y install "$@" 2>/dev/null || yum -y install "$@".
Severity note: this is a real gap but it fails safely (a clear build-time error, not silent corruption or a runtime break), it's a strict improvement over pre-PR behavior where every non-Debian base failed, and the mainstream/actively-targeted RHEL-family bases (Fedora, RHEL8+/CentOS Stream, Rocky, Alma, Oracle Linux, AL2023) all use dnf and are unaffected. CentOS 7 and Amazon Linux 2 are also both EOL or near-EOL as of the current date. Given the trivial fix and the fact that this is incomplete coverage of a brand-new capability rather than a regression, I'm marking this a nit rather than blocking.
Validation update — verified through provisioning; template-build boot blocked by the local nested-virt env (not this change)Ran this branch on a real infra-local Lima dev slot (nested-KVM, Apple Silicon). Honest status: Verified (observed):
Blocked (environment, not this PR): the build then fails at the incremental-snapshot memory-diff step: Firecracker crashes (exit 148) servicing (Separately filed the infra-local dev-slot tooling fix that was needed to bring the slot up at all: e2b-dev/infra-local#10.) |
5aad415 to
d71980e
Compare
|
Thank you for your pull request and welcome to our community. We could not parse the GitHub identity of the following contributors: root.
|
This comment has been minimized.
This comment has been minimized.
|
Thank you for your pull request and welcome to our community. We could not parse the GitHub identity of the following contributors: root.
|
…fixed-5008 flake) (#3401) `TestGetOrConnectNode_CacheMiss_DiscoversAndConnects` bound a **fixed** port (`consts.OrchestratorAPIPort` = 5008) for its fake gRPC server and flaked with `address already in use` — on shared CI runners (parallel shards) and on dev boxes running a real orchestrator on 5008. The helper now binds an **ephemeral** port and returns it; the mocked Nomad service registration carries that port, so discovery still dials the right listener. **Pre-existing flake, independent of the multi-distro work** — split out of #3381 to keep that PR focused (per operator). Verified: `go test ./internal/orchestrator/ -run TestGetOrConnectNode|TestConnectToNode` green locally; gofmt clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…a/RHEL/Arch) Template builds are Debian/Ubuntu-only today: provision.sh hardcodes apt/dpkg, Debian package names, the /lib/systemd/systemd init path and the chrony unit, so a Fedora/RHEL/Arch base image fails opaquely during provisioning. This makes provisioning distro-aware by keying on the base image's DECLARED /etc/os-release ID (ADR-010) — not by probing which package manager exists (the axis rejected in #2941). Each supported family is a declared Profile in a new `phases/base/distro` package; provision.sh selects the profile in-guest by $ID via a selector generated from that registry, and uses its package set, package-manager functions, init path, chrony unit, admin group and CA handling. An unsupported distro exits fast with a clear, customer-visible error. - new `phases/base/distro`: Profile registry (debian/ubuntu, RHEL family incl. rocky/alma/centos/rhel/ol/amzn, arch) + generated shell selector + unit tests. - provision.sh: os-release detect + selector; profile-driven package check/ install, init-link, chrony enable, CA-bundle ensure. Debian package set/query/ init path preserved (AC2). - rootfs.go: drop the static Debian chrony.service symlink; provision.sh now enables the distro-correct unit (chrony vs chronyd). - configure.sh: adduser -> useradd (portable); admin group sudo||wheel. Scope: systemd family only (v1). Alpine (OpenRC/musl) is explicitly rejected and tracked separately. Egress-proxy CA trust on RHEL (envd /etc/pki path) is a follow-up. Refs FEAT-145 / IMPL-145. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…enable cannot prune it The multi-user.target.wants/envd.service link was written with a relative target that resolves inside the .wants directory and dangles. provision.sh's new offline `systemctl enable $E2B_TIMESYNC_UNIT` prunes dangling .wants symlinks, silently disabling envd autostart on Fedora (build died at 'wait for envd'). Absolute target survives the prune; Ubuntu unaffected. Real-KVM diagnosis: IMPL-145 qa.md QA10. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
update-ca-trust extract regenerates /etc/pki/ca-trust/extracted/* but never creates /etc/ssl/certs/ca-certificates.crt, so on Fedora envd.service's ExecStartPre found no bundle and fell back to update-ca-certificates — which does not exist there — failing the unit forever (observed: sshd up, envd port never opens, build dies at 'wait for envd' even with the autostart symlink restored). Link the bundle to the extracted PEM in e2b_ca_refresh, and make the unit's regenerate fallback tolerate distros without update-ca-certificates. Arch profile gets the correct update-ca-trust too. Real-KVM diagnosis: IMPL-145 qa.md QA10/QA11. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…he baked wants-symlink On the RHEL family the base image has no systemd package, so provisioning's dnf install triggers systemd's RPM scriptlet 'systemctl preset-all', whose Fedora policy ends with 'disable *' — deleting envd's baked autostart symlink even when non-dangling (verified by chroot experiment on the real provisioned rootfs: preset-all removes it, enable recreates it). apt has no such pass, which is why Ubuntu never hit this. Enabling envd after the package transaction guarantees autostart on every family. Real-KVM diagnosis: IMPL-145 qa.md QA11. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-end)
Two remaining blockers for RHEL-family template builds, both observed live
on real KVM (SSH into the failing guest):
1. provision.sh removes /etc/machine-id, so the template's next boot is a
systemd FIRST boot — PID1 then applies the distro preset policy to all
units, and the RHEL family's policy ends with 'disable *', deleting
envd's autostart symlink at boot no matter how provisioning created it
(observed: unit 'disabled; preset: disabled' in the live guest). Ship
/etc/systemd/system-preset/00-e2b.preset ('enable envd.service') so
every preset-all — RPM scriptlet or first boot — enables envd instead.
2. The DEFAULT USER build step used Debian's 'adduser --disabled-password
--gecos' (exit 2 on Fedora, where adduser is useradd) and
'usermod -aG sudo' (no sudo group there). Ported to useradd + a
sudo-then-wheel fallback, mirroring configure.sh (W1 T4).
With these plus the CA-bundle fix, the Fedora 40 template build completes
on real x86 KVM.
Real-KVM diagnosis: IMPL-145 qa.md QA11.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…late support (W5) Profiles now declare their init family (systemd | openrc); everything init-specific that runs at provisioning time is one declared shell block per init system (distro/init.go), rendered into the selector as e2b_init_setup(). provision.sh keeps a single init-agnostic body; the systemd block is moved verbatim so the Debian render stays behaviorally identical (AC2). Alpine profile (apk, busybox init -> OpenRC runlevels): the OpenRC setup replaces the one-shot provisioning inittab with the real boot sequence, wires the sysinit/boot runlevels a container image ships without, and enables envd via the baked /etc/init.d/envd (supervise-daemon, mirrors envd.service incl. the CA-store seeding; inert on systemd images). Layer executor's envd restart is now init-agnostic. Images without /etc/os-release are rejected with a message naming the real problem. Verified on real x86 KVM: alpine:3.24 builds end-to-end and a booted sandbox returns Alpine os-release with envd supervised (qa.md QA12). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…bsent (W3) The process wrapper hardcoded /usr/bin/ionice and /usr/bin/nice; on images without util-linux/coreutils (Alpine base, minimal/UBI-class) every spawned process died with exit 127 — observed on real KVM as the base-phase sync command failing right after envd came up. Resolve the helpers via LookPath and build the prefix from what exists; the oom_score_adj write (pure /proc) stays unconditional. TT-4 covers present/absent/partial helper sets. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-harden the provisioning inittab (AC4/AC7) Provisioning output is only logged at debug, so customers saw rejections as a bare 'exit status: 1'. Keep a rolling tail of the guest's provisioning lines and attach it to the failure error — which is user-visible — so unsupported images fail with the actual reason and the supported-distro list. Run the provisioning pipeline entirely through the baked busybox: bare images (pure-Nix, distroless) ship no /bin/sh or sed, which previously swallowed the rejection message exactly where it matters most. Observed on real KVM with nixos/nix: the build log now names the missing os-release and lists supported distros. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Debian's systemctl enable synchronizes SysV state via update-rc.d, which aborts on a non-LSB /etc/init.d/envd and failed the whole Ubuntu provisioning (caught by the new failure-tail in the build error). Bake the script at /usr/local/share/e2b/envd.openrc and install it to /etc/init.d only in the OpenRC family's init setup. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ependency chronyd treats a missing PHC refclock as FATAL, and /dev/ptp0 does not exist where nested virtualization can't expose kvm-ptp (dev slots) — observed on real KVM: chrony/chronyd dead in Ubuntu, Fedora AND Alpine guests (a pre-existing baseline issue, not a FEAT-145 regression). Reference the PHC only when the device exists at provisioning time (same host/KVM as runtime) and fall back to an NTP pool otherwise. On Alpine, chronyd's 'need net' pulled in the networking service, which errored on a missing /etc/network/interfaces; a loopback-only interfaces file lets it provide 'net' without touching the kernel-managed eth0. Production (kvm-ptp present) keeps the exact PHC config. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… cache key (W1 T5) The fallback provision version hashed only the raw provision.sh template and the embedded files/* — changes to the distro profiles/init blocks (rendered into the script at build time) or to the baked symlink layer did not rotate the key, silently reusing stale provisioned bases (bit dev twice, qa.md QA11). Add distro.Fingerprint() (sha256 of the generated selector + an explicit Version) to the fallback hash, and fold the symlink-layer map into FilesHash. Production rollout stays on the BuildProvisionVersion flag. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…(W5 T4) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t; explicit failure paths everywhere NixOS tier (qa.md QA13 proposal, operator-approved): a 'nixos' profile for PREMADE images built from the E2B NixOS configuration — no package manager to drive (everything is declared in the image), stage-2 init at the profile symlink, and a Bootstrap hook that puts the baked busybox's applets on PATH (no FHS userland exists before the first activation). The provisioning boot itself is now self-contained on the baked busybox: - rcS drives mkdir/mount through it (bare images have neither on PATH) - busybox init hands any inittab line with shell metacharacters to /bin/sh, which bare images don't have — the pipeline moved into a baked runner script and every inittab entry is a plain exec; fsfreeze falls back to sync-only with a message where util-linux is absent Failure-path policy (operator directive): no '|| true' silencing — explicit checks with stated code paths. CA refresh failures now fail provisioning loudly; missing OpenRC scripts / skel / passwd are checked and logged as deliberate skips; user creation and admin-group failures are hard errors; cert seeding is one shared warn-and-continue script (e2b-seed-certs) used by both envd services instead of two silent fallback chains. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n real KVM
The nixos-base-image/ directory is the versioned source of the premade image
(configuration.nix + build/publish script + operational notes). Boot fixes
found by observation on the real console/guest:
- NixOS activation refuses to symlink /etc/systemd/system over the baked
drop-ins, leaving systemd with no units at all ('Unit default.target not
found', frozen boot) — the nixos init setup removes them; the image's own
configuration declares envd
- premade images lack /etc/profile.d, /root and /usr/sbin pre-activation —
the shared provisioning body creates them
- the image declares /bin/bash (build commands invoke it explicitly), a
'user' group matching useradd semantics (configure.sh chowns user:user),
the exact sudoers line the build steps grep for, and the journald
watchdog override
Verified end-to-end: template builds in 18s; booted sandbox returns
ID=nixos / NixOS 24.05, envd active, chronyd active, nix-env on PATH,
HTTPS CA trust works (qa.md QA14).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
coreutils >= 9.2 (Fedora 40, Arch) makes 'cp -n' exit 1 when it skips an existing file — the previous '|| true' masked this real semantic landmine and removing it broke Fedora finalize (observed: 'cp: not replacing ...bashrc', exit 1). Walk /etc/skel explicitly and copy only missing files; real copy failures stay loud. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The envd-update step restarts envd via a command executed BY envd. systemctl hands the restart to PID1 and survives envd's death; rc-service runs it synchronously in the spawned shell, which envd's shutdown kills between the stop and start halves — the new envd never starts and the update times out (observed on Alpine finalize). setsid + background lets the restart outlive its parent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…c-service The detached rc-service restart raced the next build command against the old envd's death (observed: configure step hit 'unexpected EOF'). Killing envd and letting supervise-daemon's respawn start the replaced binary makes the old instance's death immediate — the post-update wait can only ever see the new envd. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…i port-collision flake - lint: nlreturn/paralleltest/modernize findings in the FEAT-145 test files (blank lines before return/continue, t.Parallel() on all distro tests, range strings.SplitSeq) - api: TestGetOrConnectNode_CacheMiss_DiscoversAndConnects bound a FIXED port (consts.OrchestratorAPIPort, 5008) and flaked with 'address already in use' wherever anything held it (persistent CI runners, dev boxes with a real orchestrator). The nomad discovery dials the registration's own address:port, so the fake server now uses an ephemeral port carried through the mocked registration — no fixed bind at all. Gates run locally (matching .github/workflows): golangci-lint v2.11.4 → 0 issues on envd, orchestrator, api; go test ./... green on all seven shards (api/client-proxy/db/docker-reverse-proxy/envd/orchestrator/shared); GOARCH=arm64 build+vet green (CGO orchestrator via aarch64-linux-gnu-gcc). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
e048b52 to
e101748
Compare
|
Thank you for your pull request and welcome to our community. We could not parse the GitHub identity of the following contributors: root.
|
|
Thank you for your pull request and welcome to our community. We could not parse the GitHub identity of the following contributors: root.
|
|
bugbot run |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 369027b. Configure here.
| "nftables", "iputils", "jq", "bash", | ||
| }, | ||
| PkgQueryBody: `pacman -Q "$1" >/dev/null 2>&1`, | ||
| PkgInstall: "pacman -Sy --noconfirm\n pacman -S --noconfirm --needed \"$@\"", |
There was a problem hiding this comment.
Arch partial upgrade install
Medium Severity
Arch PkgInstall runs pacman -Sy then pacman -S, which refreshes the DB without upgrading installed packages. That partial-upgrade pattern can leave the guest with incompatible dependency versions during provisioning.
Reviewed by Cursor Bugbot for commit 369027b. Configure here.
|
Superseded by #3411 — reopened from a clean branch (internal refs stripped, NixOS split out to a stacked PR, review threads addressed at source). Closing this one. |
…ge helpers Customer-facing delivery surface for multi-distro template support (server side: e2b-dev/runtime#3381). Mirrors the existing Debian-family convenience helpers (fromDebianImage/fromUbuntuImage/...) in both the JS and Python SDKs: - fromFedoraImage(variant='latest') -> fedora:<variant> - fromAlpineImage(variant='latest') -> alpine:<variant> - fromArchImage(variant='latest') -> archlinux:<variant>
Resolve the base image's distro from its `/etc/os-release` ID and drive provisioning from a declared per-family profile — Debian/Ubuntu (apt), the RHEL family (dnf/microdnf/yum), Arch (pacman), and Alpine on OpenRC (apk) — instead of probing for a package manager. Unsupported or identity-less images are rejected with a clear build-log error. Once merged, the biggest change is adduser -> useradd and introduction of /usr/local/bin/e2b-seed-certs instead of long one-liner for envd. Supersedes #3381 (reopened from a clean branch, no bot-thread churn). NixOS support follows in a stacked PR #3412.
Resolve the base image's distro from its `/etc/os-release` ID and drive provisioning from a declared per-family profile — Debian/Ubuntu (apt), the RHEL family (dnf/microdnf/yum), Arch (pacman), and Alpine on OpenRC (apk) — instead of probing for a package manager. Unsupported or identity-less images are rejected with a clear build-log error. Once merged, the biggest change is adduser -> useradd and introduction of /usr/local/bin/e2b-seed-certs instead of long one-liner for envd. Supersedes #3381 (reopened from a clean branch, no bot-thread churn). NixOS support follows in a stacked PR #3412.


What
Template builds are Debian/Ubuntu-only today: the base-phase
provision.shhardcodesapt/dpkg, Debian package names, the/lib/systemd/systemdinit path and thechronyunit — so a Fedora / RHEL / CentOS Stream / Rocky / Alma / Arch base image fails opaquely during provisioning (apt-get: command not found).This makes provisioning distro-aware by keying on the base image's declared
/etc/os-releaseID — not by probing which package-manager binary happens to exist. Each supported family is a declaredProfilein a newphases/base/distropackage;provision.shselects the profile in-guest by$IDvia a selector generated from that registry, then uses the profile's package set, package-manager functions, init path, chrony unit, admin group and CA handling. An unsupported distro exits fast with a clear, customer-visible error instead of failing halfway.Why this shape (not package-manager detection)
Keying on the distro (os-release ID) rather than the package manager (
command -v dnf) is deliberate: package-manager detection conflates distros that share a manager but differ in unit names, paths and package names, and it can't represent a non-systemd distro at all. Keeping the axis on the declared distro puts each family's divergences in one declared table and makes the init system a first-class dimension. (Rationale captured in the architecture record as ADR-010; this supersedes the approach in #2941, reusing only its verified package-name/unit/CA mapping.)Changes
phases/base/distro—Profileregistry (debian/ubuntu; RHEL family incl.fedora|rhel|centos|rocky|almalinux|ol|amzn; arch) + a generated POSIX-sh selector + unit tests.provision.sh— sources/etc/os-release, runs the selector, then does profile-driven package check/install, init-link, chrony enable, and CA-bundle ensure. Debian package set / query / init path preserved (no behaviour change for existing templates).rootfs.go— drops the static Debianchrony.serviceautostart symlink;provision.shnow enables the distro-correct unit (chronyvschronyd).configure.sh—adduser→useradd(portable across families); admin groupsudo || wheel.Scope
v1 = the systemd family (Debian/Ubuntu unchanged; adds Fedora/RHEL/Arch). Explicitly out of scope / follow-ups:
cacerts.gohardcodes the Debian bundle path/anchor dir; RHEL's/etc/pki+update-ca-trustpath needs profile-fed handling before egress interception works on RHEL. (This PR ensures the trust bundle exists at the expected path; the envd-side per-distro anchor handling is the follow-up.)Validation
go test ./pkg/template/build/phases/base/distro/— green (Debian preserved, families differ, selector keys on$E2B_DISTRO_IDwith no package-manager probing, unknown distro rejected, RHEL aliases resolve).aptstep givescommand not found(exit 127) on Fedora, while the profile-driven path provisions Ubuntu and Fedora cleanly (packages installed, systemd init path present, CA bundle at the envd-expected path); Alpine confirmed it needs the OpenRC path (no systemd init binary).🤖 Generated with Claude Code