Skip to content

test(integration): run the Alpine distro build with a musl-capable envd - #3444

Merged
tomassrnka merged 1 commit into
mainfrom
test/alpine-static-envd
Jul 29, 2026
Merged

tomassrnka merged 1 commit into
mainfrom
test/alpine-static-envd

Conversation

@tomassrnka

Copy link
Copy Markdown
Member

make build-debug builds envd with -race, which needs cgo, so the binary the integration host injects into every guest is dynamically linked against glibc — and a musl guest has no loader for it. envd never answers, the base layer times out and the build dies with a masked internal error, which is why the Alpine case was skipped. Linking it statically fixes that; osusergo,netgo keep user and DNS lookups pure Go as in the CGO_ENABLED=0 binary we ship, so only the race instrumentation differs. The detector is intact (297 __tsan symbols; a deliberate racy program built with the same flags still reports on musl), and guest console output reaches orchestrator.log, so the workflow's data-race grep keeps covering envd.

Checked on a KVM dev stack, cold cache each time: with the old binary ubuntu/fedora/arch pass and alpine fails on wait for envd: syncing took too long; with the static one all four pass (alpine 30s). envd -version also runs under alpine:3.24, where the old binary gives exec: no such file or directory.

@cla-bot cla-bot Bot added the cla-signed label Jul 29, 2026
@cursor

cursor Bot commented Jul 29, 2026 •

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes how every integration/debug envd binary is built (cgo, static link, tags); wrong flags could break local debug or race detection outside musl, but production build is unchanged.

Overview
The integration host injects the build-debug envd into sandbox guests; -race forces cgo and used to produce a glibc-linked binary that musl guests could not execute, so Alpine template builds timed out waiting for envd. The debug build now uses static external linking with osusergo and netgo so the instrumented binary does not depend on a guest dynamic loader while keeping user and DNS behavior aligned with the shipped CGO_ENABLED=0 binary. The Alpine case in the distro-family template build test is no longer skipped.

Reviewed by Cursor Bugbot for commit 7201839. Bugbot is set up for automated code reviews on this repo. Configure here.

@codecov

codecov Bot commented Jul 29, 2026 •

Copy link
Copy Markdown

❌ 5 Tests Failed:

Tests completed Failed Passed Skipped
3613 5 3608 7
View the top 2 failed test(s) by shortest run time
github.com/e2b-dev/infra/tests/integration/internal/tests/api/templates::TestSandboxCreateWithAliasAndTag
Stack Traces | 157s run time
=== RUN   TestSandboxCreateWithAliasAndTag
=== PAUSE TestSandboxCreateWithAliasAndTag
=== CONT  TestSandboxCreateWithAliasAndTag
    template_tags_test.go:207: Build failed: {<nil> An internal error occurred. Please try again or contact support with the build ID. <nil>}
--- FAIL: TestSandboxCreateWithAliasAndTag (157.31s)
github.com/e2b-dev/infra/tests/integration/internal/tests/api/templates::TestTemplateTagDeleteLatestNotAllowed
Stack Traces | 178s run time
=== RUN   TestTemplateTagDeleteLatestNotAllowed
=== PAUSE TestTemplateTagDeleteLatestNotAllowed
=== CONT  TestTemplateTagDeleteLatestNotAllowed
    template_tags_test.go:94: Build failed: {<nil> An internal error occurred. Please try again or contact support with the build ID. <nil>}
--- FAIL: TestTemplateTagDeleteLatestNotAllowed (178.12s)
View the full list of 8 ❄️ flaky test(s)
github.com/e2b-dev/infra/tests/integration/internal/tests/api/templates::TestSandboxCreateWithTag

Flake rate in main: 10.06% (Passed 161 times, Failed 18 times)

Stack Traces | 165s run time
=== RUN   TestSandboxCreateWithTag
=== PAUSE TestSandboxCreateWithTag
=== CONT  TestSandboxCreateWithTag
    template_tags_test.go:115: Build failed: {<nil> An internal error occurred. Please try again or contact support with the build ID. <nil>}
--- FAIL: TestSandboxCreateWithTag (165.41s)
github.com/e2b-dev/infra/tests/integration/internal/tests/api/templates::TestTemplateBuildRUN

Flake rate in main: 11.21% (Passed 103 times, Failed 13 times)

Stack Traces | 0s run time
=== RUN   TestTemplateBuildRUN
=== PAUSE TestTemplateBuildRUN
=== CONT  TestTemplateBuildRUN
--- FAIL: TestTemplateBuildRUN (0.00s)
github.com/e2b-dev/infra/tests/integration/internal/tests/api/templates::TestTemplateBuildRUN/Single_RUN_command

Flake rate in main: 17.86% (Passed 46 times, Failed 10 times)

Stack Traces | 157s run time
=== RUN   TestTemplateBuildRUN/Single_RUN_command
=== PAUSE TestTemplateBuildRUN/Single_RUN_command
=== CONT  TestTemplateBuildRUN/Single_RUN_command
    build_template_test.go:159: test-ubuntu-run: [info] Building template 1sjdoybzjhobjzncokda/c3298956-9ca6-4906-a3ae-0b7afa4f6468
    build_template_test.go:159: test-ubuntu-run: [info] [base] FROM ubuntu:22.04 [a607f7e276930de2dbe6cbe4815822b2a9ce4fb01533ac9bb9549fb7e5269d25]
    build_template_test.go:159: test-ubuntu-run: [info] Base Docker image size: 30 MB
    build_template_test.go:159: test-ubuntu-run: [info] Creating file system and pulling Docker image
    build_template_test.go:159: test-ubuntu-run: [info] Uncompressing layer sha256:d6834b4a794c03efa2c998853e64969fa8851b11b2ade63292268872a37759d0 30 MB
    build_template_test.go:159: test-ubuntu-run: [info] Uncompressing layer sha256:4ea345d294ce622a2710a34f51abdeffd8f4f1af13e5f3cd2aeba9163eebc717 14 MB
    build_template_test.go:159: test-ubuntu-run: [info] Uncompressing layer sha256:e216621d8a0a7a4486d6a228759b29bc378eff95e6ea9469854b8624b267dd13 137 B
    build_template_test.go:159: test-ubuntu-run: [info] Layers extracted
    build_template_test.go:159: test-ubuntu-run: [info] Root filesystem structure: bin, boot, dev, etc, home, lib, lib32, lib64, libx32, media, mnt, opt, proc, root, run, sbin, srv, sys, tmp, usr, var
    build_template_test.go:159: test-ubuntu-run: [info] Provisioning sandbox template
    build_template_test.go:159: test-ubuntu-run: [info] Provisioning was successful, cleaning up
    build_template_test.go:159: test-ubuntu-run: [info] Sandbox template provisioned
    build_template_test.go:159: test-ubuntu-run: [info] [base] DEFAULT USER user [39b4099771c741ffc671d95a7b27021b0f7ea31e5943b6af306b0ff9dc9ea7c4]
    build_template_test.go:159: test-ubuntu-run: [info] [builder 1/1] RUN echo 'Hello, World!' [8cd4a019e61b09e03ac8204ef1149de7fce6a62e8d174590d604b6b255eb4443]
    build_template_test.go:159: test-ubuntu-run: [info] [builder 1/1] [stdout]: Hello, World!
    build_template_test.go:159: test-ubuntu-run: [info] [finalize] Finalizing template build [d6cfed4c540bc6a357913a9609cc6076005d6fcaaa475b1907863212b7adac83]
    build_template_test.go:159: test-ubuntu-run: [error] Build failed: An internal error occurred. Please try again or contact support with the build ID.
    build_template_test.go:192: Build failed: An internal error occurred. Please try again or contact support with the build ID.
--- FAIL: TestTemplateBuildRUN/Single_RUN_command (157.32s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir

Flake rate in main: 21.23% (Passed 141 times, Failed 38 times)

Stack Traces | 1.37s run time
=== RUN   TestListDir
=== PAUSE TestListDir
=== CONT  TestListDir
Executing command update-ca-certificates in sandbox i30ld7ej9yapnl7knlxce (user: root)
--- FAIL: TestListDir (1.37s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_0_lists_only_root_directory

Flake rate in main: 25.93% (Passed 20 times, Failed 7 times)

Stack Traces | 0.01s run time
=== RUN   TestListDir/depth_0_lists_only_root_directory
=== PAUSE TestListDir/depth_0_lists_only_root_directory
=== CONT  TestListDir/depth_0_lists_only_root_directory
    filesystem_test.go:96: 
        	Error Trace:	.../tests/envd/filesystem_test.go:96
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_0_lists_only_root_directory
--- FAIL: TestListDir/depth_0_lists_only_root_directory (0.01s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_1_lists_root_directory

Flake rate in main: 16.79% (Passed 114 times, Failed 23 times)

Stack Traces | 0.01s run time
=== RUN   TestListDir/depth_1_lists_root_directory
=== PAUSE TestListDir/depth_1_lists_root_directory
=== CONT  TestListDir/depth_1_lists_root_directory
    filesystem_test.go:96: 
        	Error Trace:	.../tests/envd/filesystem_test.go:96
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_1_lists_root_directory
--- FAIL: TestListDir/depth_1_lists_root_directory (0.01s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)

Flake rate in main: 50.00% (Passed 1 times, Failed 1 times)

Stack Traces | 0.01s run time
=== RUN   TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
=== PAUSE TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
=== CONT  TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
    filesystem_test.go:96: 
        	Error Trace:	.../tests/envd/filesystem_test.go:96
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
--- FAIL: TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory) (0.01s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_3_lists_all_directories_and_files

Flake rate in main: 18.09% (Passed 77 times, Failed 17 times)

Stack Traces | 0.01s run time
=== RUN   TestListDir/depth_3_lists_all_directories_and_files
=== PAUSE TestListDir/depth_3_lists_all_directories_and_files
=== CONT  TestListDir/depth_3_lists_all_directories_and_files
    filesystem_test.go:96: 
        	Error Trace:	.../tests/envd/filesystem_test.go:96
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_3_lists_all_directories_and_files
--- FAIL: TestListDir/depth_3_lists_all_directories_and_files (0.01s)

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@tomassrnka
tomassrnka marked this pull request as ready for review July 29, 2026 15:24

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — this is a well-scoped test/build-tooling fix with a clear, verified root cause.

What was reviewed: the build-debug Makefile target and the new DEBUG_LDFLAGS static-link/tag combination, and the distro test's removal of the Alpine skip. Confirmed build (the production envd binary) is untouched — only the debug binary used by the integration test host changes. Also considered the CI failures reported by codecov on this run, but they're in unrelated template-tag tests, not the modified distro_build_test.go, and match tests with pre-existing high flake rates on main (10-25%), so they don't appear caused by this change.

Extended reasoning...

Overview

This PR touches two files: packages/envd/Makefile (adds a DEBUG_LDFLAGS var with -linkmode external -extldflags=-static and switches build-debug to use it plus -tags osusergo,netgo) and tests/integration/internal/tests/api/templates/distro_build_test.go (removes the skip for the Alpine test case, since the underlying blocker is fixed). The production build target (CGO_ENABLED=0, PROD_LDFLAGS) is unchanged — this only affects the race-instrumented debug binary that the integration test host injects into guest VMs.

Security risks

None. This is a test/dev tooling change to how the debug envd binary is linked; it does not touch auth, crypto, permissions, or any production code path.

Level of scrutiny

Low-to-medium is appropriate. The change is confined to test infrastructure (the integration host's debug envd) rather than the shipped production binary, so the blast radius is limited to CI/dev workflows. The PR description shows solid manual verification: all four distro builds passing with the static binary on a KVM dev stack, confirmation the race detector still has its ~297 __tsan symbols and still catches a deliberately racy program on musl, and confirmation guest console output still reaches orchestrator.log for the workflow's data-race grep.

Other factors

The bug-hunting pass found only a cosmetic nit (a leftover unused LDFLAGS variable), which doesn't warrant blocking. I also checked the codecov failures posted on this PR — they're in template_tags_test.go and build_template_test.go, unrelated to the modified distro_build_test.go, and several are explicitly flagged with pre-existing flake rates in main (10-25%), so they read as ambient CI flakiness rather than a regression from this change. Cursor's bot flagged this as "Medium Risk" because it changes how every debug envd binary is built, but since build-debug is test/dev-only and the production build path is untouched, I don't think that risk rating changes the calculus here.

Comment thread packages/envd/Makefile
@@ -7,6 +7,7 @@ ENV := $(shell cat ../../.last_used_env || echo "not-set")
BUILD := $(shell git rev-parse HEAD | cut -c1-7)
LDFLAGS=-ldflags "-X=main.commitSHA=$(BUILD)"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 The LDFLAGS variable defined at packages/envd/Makefile:8 is now unused — this PR switches build-debug (its only consumer) over to the new DEBUG_LDFLAGS. It's dead code left over from the refactor and can be safely deleted.

Extended reasoning...

What happened: This PR adds a new DEBUG_LDFLAGS variable (line 10) with the extra -linkmode external -extldflags=-static flags needed to statically link the race-instrumented debug binary so it can run on musl guests. It correctly updates the build-debug target (line 80) to use ${DEBUG_LDFLAGS} instead of ${LDFLAGS}.

However, the original LDFLAGS=-ldflags \"-X=main.commitSHA=$(BUILD)\" definition on line 8 was left in place. Before this PR, build-debug was the sole consumer of LDFLAGS (the build target has always used the separate PROD_LDFLAGS). After this PR, nothing references LDFLAGS anywhere in the file.

Proof:

  1. Before the PR: grep LDFLAGS packages/envd/Makefile shows LDFLAGS defined on line 8 and consumed via ${LDFLAGS} on line 80 (build-debug).
  2. After the PR: line 80 now reads CGO_ENABLED=1 go build -race -tags osusergo,netgo -gcflags=all=\"-N -l\" -o bin/debug/envd ${DEBUG_LDFLAGS} — the only other ldflags usage is PROD_LDFLAGS in the unrelated build target (line 73).
  3. A boundary-aware grep for the bare LDFLAGS token (excluding PROD_LDFLAGS/DEBUG_LDFLAGS) matches only the definition on line 8, confirming it has zero remaining references.

Impact: None functionally — Make does not warn on unused variables, and there's no implicit rule here that would pick it up accidentally, so the build behaves identically whether or not the line is removed. It's purely a leftover from the refactor that muddies the file (a future reader might wonder if it's still used somewhere, or reintroduce a stale reference).

Fix: Delete line 8 (LDFLAGS=-ldflags \"-X=main.commitSHA=$(BUILD)\") since PROD_LDFLAGS and DEBUG_LDFLAGS now cover both build targets.

@tomassrnka
tomassrnka merged commit f18b271 into main Jul 29, 2026
44 checks passed
@tomassrnka
tomassrnka deleted the test/alpine-static-envd branch July 29, 2026 16:12
tomassrnka added a commit that referenced this pull request Jul 31, 2026
)

## What

Rebase + extension of #3235 (cc @dobrac), squashed onto current main
after #3462 landed. Opened as a **draft for a measuring run** — the
shard timings from this PR's CI decide the final shard balance.

Two things changed since #3235 was written, and this branch reconciles
both:

- #3462 already landed the coverage philosophy (uncompressed runs the
whole suite once; zstd1/lz4 only re-run the allow-list). This rebase
**keeps that decision** and applies #3235's sharding on top — PRs now
shard the *uncompressed* config, not zstd1.
- The distro-family template tests (#3437/#3444) landed into what is now
templates shard 2, so the name-prefix split needs rebalancing from this
run's junits.

## PR-path matrix (8 jobs; push to main unchanged: 3 unsharded configs)

| Job | Shard | Runs |
|---|---|---|
| uncompressed-templates-1/2 | `^TestTemplateBuild` split by name prefix
| 11 + 9 real-build tests |
| uncompressed-sandboxes | sandboxes + metrics + volumes + proxies | 121
tests |
| uncompressed-rest | everything else, `-skip '^TestTemplateBuild'` | 86
tests |
| zstd1-templates / lz4-templates | templates package ∩ allow-list | 6
snapshot-build entries |
| zstd1-other / lz4-other | package complement ∩ allow-list | 33 entries
|

Static partition verified: 227 top-level tests land exactly once across
the uncompressed shards; templates/no-templates is an exact package
complement. `TESTS_ONLY` composes only with package-pure shards (the
name-split shards' own `-run` would collide — guarded, fails loudly).
Unknown shard names fail loudly instead of silently running the whole
suite.

## Carried from #3235 (unchanged in spirit)

- DB containers start right after checkout and overlap the Go builds
(`start-databases`); `start-services` waits with bounded health loops.
Docker Hub pre-pull retries (#3410) folded in, now covering the pinned
tags + otel.
- ClickHouse migrations via goose on the host (`migrate-host`) — no
migrator image build.
- Salted optimized Go cache; `DEBUG_GCFLAGS` opt-out (race stays on, `-N
-l` off in CI). envd keeps the musl-static link from #3444, gcflags
parametrized.
- 7 metadata/authz template tests use `RequestTemplateWithoutBuild`;
envd process tests poll instead of fixed sleeps.

## Expected (to be confirmed by this run)

Baseline on main (run 30626752076): integration jobs 8.4–9.9 min, PR
wall ~10–12 min. Expected here: **~5–5.5 min wall**, long pole likely
uncompressed-templates-2 (it inherited the distro suite). Follow-ups
after measurement: rebalance `TEMPLATE_BUILDS_SHARD1_RE`, cache the
built sandbox template keyed on inputs.

Relationship to #3235: this supersedes the mechanical parts; adopt into
the original PR or take this one over — @dobrac's call.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
jakubno pushed a commit that referenced this pull request Aug 3, 2026
…vd (#3444)

`make build-debug` builds envd with `-race`, which needs cgo, so the
binary the integration host injects into every guest is dynamically
linked against glibc — and a musl guest has no loader for it. envd never
answers, the base layer times out and the build dies with a masked
internal error, which is why the Alpine case was skipped. Linking it
statically fixes that; `osusergo,netgo` keep user and DNS lookups pure
Go as in the `CGO_ENABLED=0` binary we ship, so only the race
instrumentation differs. The detector is intact (297 `__tsan` symbols; a
deliberate racy program built with the same flags still reports on
musl), and guest console output reaches `orchestrator.log`, so the
workflow's data-race grep keeps covering envd.

Checked on a KVM dev stack, cold cache each time: with the old binary
ubuntu/fedora/arch pass and alpine fails on `wait for envd: syncing took
too long`; with the static one all four pass (alpine 30s). `envd
-version` also runs under `alpine:3.24`, where the old binary gives
`exec: no such file or directory`.
jakubno pushed a commit that referenced this pull request Aug 3, 2026
)

## What

Rebase + extension of #3235 (cc @dobrac), squashed onto current main
after #3462 landed. Opened as a **draft for a measuring run** — the
shard timings from this PR's CI decide the final shard balance.

Two things changed since #3235 was written, and this branch reconciles
both:

- #3462 already landed the coverage philosophy (uncompressed runs the
whole suite once; zstd1/lz4 only re-run the allow-list). This rebase
**keeps that decision** and applies #3235's sharding on top — PRs now
shard the *uncompressed* config, not zstd1.
- The distro-family template tests (#3437/#3444) landed into what is now
templates shard 2, so the name-prefix split needs rebalancing from this
run's junits.

## PR-path matrix (8 jobs; push to main unchanged: 3 unsharded configs)

| Job | Shard | Runs |
|---|---|---|
| uncompressed-templates-1/2 | `^TestTemplateBuild` split by name prefix
| 11 + 9 real-build tests |
| uncompressed-sandboxes | sandboxes + metrics + volumes + proxies | 121
tests |
| uncompressed-rest | everything else, `-skip '^TestTemplateBuild'` | 86
tests |
| zstd1-templates / lz4-templates | templates package ∩ allow-list | 6
snapshot-build entries |
| zstd1-other / lz4-other | package complement ∩ allow-list | 33 entries
|

Static partition verified: 227 top-level tests land exactly once across
the uncompressed shards; templates/no-templates is an exact package
complement. `TESTS_ONLY` composes only with package-pure shards (the
name-split shards' own `-run` would collide — guarded, fails loudly).
Unknown shard names fail loudly instead of silently running the whole
suite.

## Carried from #3235 (unchanged in spirit)

- DB containers start right after checkout and overlap the Go builds
(`start-databases`); `start-services` waits with bounded health loops.
Docker Hub pre-pull retries (#3410) folded in, now covering the pinned
tags + otel.
- ClickHouse migrations via goose on the host (`migrate-host`) — no
migrator image build.
- Salted optimized Go cache; `DEBUG_GCFLAGS` opt-out (race stays on, `-N
-l` off in CI). envd keeps the musl-static link from #3444, gcflags
parametrized.
- 7 metadata/authz template tests use `RequestTemplateWithoutBuild`;
envd process tests poll instead of fixed sleeps.

## Expected (to be confirmed by this run)

Baseline on main (run 30626752076): integration jobs 8.4–9.9 min, PR
wall ~10–12 min. Expected here: **~5–5.5 min wall**, long pole likely
uncompressed-templates-2 (it inherited the distro suite). Follow-ups
after measurement: rebalance `TEMPLATE_BUILDS_SHARD1_RE`, cache the
built sandbox template keyed on inputs.

Relationship to #3235: this supersedes the mechanical parts; adopt into
the original PR or take this one over — @dobrac's call.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants