First Version of E2E Validation Infrastructure - #809
First Version of E2E Validation Infrastructure#809Elliot (theelliotm) wants to merge 98 commits into
Conversation
…com/microsoft/mxc into user/emichlin/validation-infra-lite
Verifies microsoft/mxc can dispatch a job to the 1ES Hosted GitHub Runner pool 1es-mxc-e2e-windows-25h2-pro-x64 (org-level, runner group 145). Pickup-only; nested-virt/WHP checks intentionally omitted. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
- Trigger only on push to this throwaway branch (not pull_request) so it doesn't re-run the full mxc CI matrix. - Omit 1ES.ImageOverride (single-image pool defaults) to isolate an image-match failure from an org-level authorization failure. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Multiple recent microsoft-org migrations (vscode, msquic, netperf, ebpf-for-windows) add a unique JobId=<run_id> label to their 1ES runs-on. Some pool configs require it to route/schedule the job; a missing JobId can leave the job queued with no runner. Testing this cheaper hypothesis before escalating the org-level authorization theory. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Root cause of the eternal queue: the pool has tag UseScaleSet=true, so it uses the 1ES Scale Set API, which is targeted by the pool NAME ALONE. The prior webhook-mode labels (self-hosted + 1ES.Pool= + JobId=) never match a scale-set pool, so jobs queued forever. Per 1ES docs, scale-set pools use: runs-on: <PoolName> with no other labels. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Pool tag UseScaleSet=true was removed (reverts pool to webhook mode), so target it with the standard proven format: self-hosted + 1ES.Pool= + JobId=. Benchmark: ebpf 1ES pool picks up in ~2.5-3 min. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
New pool 1es-mxc-e2e-windows-25h2-pro-x64-wh created cleanly via the 1ES Hub wizard: webhook mode (no UseScaleSet tag), SKU Standard_D8s_v7, its own auto-created runner group 146. Tests whether the original pool's eternal queue was due to its scale-set/half-registered state. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1es-mxc-test-2 is a REPOSITORY-level 1ES pool (organizationProfile level=Repository, repositoryName=mxc) bound directly to microsoft/mxc. Repo-level runners register straight to the repo, bypassing the org runner-group public-repo gate that org-level pools depend on. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…ol CONFIRMED working Fresh-eyes RCA: the repo-level pool 1es-mxc-test-2 DID pick up run 30931840357 (runner 21 / bf347e45c000000, group 1/Default) after a ~10.5-min cold start. Prior 'eternal queue' was a too-short (3-4 min) patience window vs ebpf's ~2.5-min WARM-pool benchmark. Only failure was pwsh not found: the raw windows-25h2-pro-x64 client image ships PS 5.1 (powershell.exe), not PS7 (pwsh.exe). Switched both steps to powershell. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…ng to powershell 7 commands.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated 2 comments.
Suppressed comments (2)
scripts/ci/resolve-validation-test-matrix.mjs:66
- A missing
poolcurrently passes validation becausenull/undefinedbypasses this condition, thenexpandPlansilently skips that architecture at line 138. A typo or omitted pool therefore removes scheduled coverage instead of producing the promised early catalog-validation failure. Require the field to be a string; an explicit empty string can still represent a dormant entry.
if (platform.family === 'macos') {
assertNonEmptyString(details.runner, `${platform.id}.${architecture}.runner`);
} else if (details.pool != null && typeof details.pool !== 'string') {
throw new Error(`${platform.id}.${architecture}.pool must be a string`);
sdk/node/tests/integration/test-helpers.ts:52
- These binaries are now mandatory npm package contents, but the local
build.batSDK staging block copies neither one (build.bat:94-120). Consequently, a local full build producessdk/node/bin/<arch>without these newly required files and the package integration checks fail, while CI passes only because its packaging job downloads the entire Windows artifact. Add both binaries to the local SDK staging path as well, or do not make them package requirements.
'wxc-ui-probe.exe',
'wxc-test-driver.exe',
| & ./scripts/ci/prepare-windows-host.ps1 ` | ||
| -Backend '${{ matrix.backend }}' ` | ||
| -BinaryDirectory (Join-Path $env:GITHUB_WORKSPACE 'artifacts\bin') *>&1 | | ||
| Tee-Object -FilePath (Join-Path $env:RUNNER_TEMP 'mxc-ci.log') -Append |
| #################### | ||
| # E2E Tests are being moved to the Validation.Tests.Matrix.Job.yml workflow, which runs cross-platform validation | ||
| ############### |
There was a problem hiding this comment.
issue: lets remove these since we'll have the PR as paper trail of where they went.
| run: cargo test --locked --release --target ${{ matrix.target }} | ||
| -p wxc_e2e_tests | ||
|
|
||
| ############### |
There was a problem hiding this comment.
note: same here
| run: cargo build --locked --release --target ${{ matrix.target }} | ||
| -p unix_test_proxy | ||
|
|
||
| # MICROVM NOT WORKING CURRENTLY |
There was a problem hiding this comment.
issue: By not working what do we mean here? If it's something to do with the tests then I don't think we need to comment out the whole block.
There was a problem hiding this comment.
MicroVM currently isn't working correctly on the test machines, so I took it out of the test matrix but left in the code that packages the microvm binaries with MXC. After re-enabling the Cargo Test and SDK jobs, I was getting a lot of errors about "unexpected binaries". Option 1 was comment out everything until MicroVM issues are resolved, and option 2 was add the MicroVM binaries to the Cargo Test and SDK jobs for no actual benefit (and could lead to regressions). I suppose option 3 is just delete this for now 🙂
| if ! sudo "$package_manager" install -y epel-release; then | ||
| sudo "$package_manager" install -y \ | ||
| https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm | ||
| fi |
There was a problem hiding this comment.
issue: So from a supply chain attack perspective we'd want to download the package from the package manager as our first option and then fallback to downloading directly from fedora.
We'd also want verify the signature of this download (see https://docs.fedoraproject.org/en-US/security/cryptography/signatures/). In this case we should probably add the pgp file to our repo instead of downloading so it can be used during the verification.
| test -x "$binary_directory/lxc-exec" | ||
| test -f "$binary_directory/unix-test-proxy" | ||
| mkdir -p "$release_directory" | ||
| cp -a "$binary_directory/." "$release_directory/" | ||
| chmod +x "$release_directory/lxc-exec" "$release_directory/unix-test-proxy" | ||
| bash "$script_root/run_lxc_all_tests.sh" |
There was a problem hiding this comment.
note: one thing to keep in mind is whether we want artifacts from one run to stay on the runner. I assume we don't and that we'd want artifacts to be removed per run. Let me know if this happens automatically, but if not then we'd want to place them into temporary directories that get blown away after the run.
There was a problem hiding this comment.
It happens automatically. Runners use a fresh image for each run.
| **Skip semantics.** Several suites degrade gracefully on an unsupported host: | ||
| the IsolationSession suites decide availability from a single `wxc-exec --probe` | ||
| read of `probes.isolationSessionAvailable` (covering both a host that cannot | ||
| activate the API and a binary built without `--features isolation_session`), | ||
| print `SKIPPED`, and exit 0. |
There was a problem hiding this comment.
question (non-blocking): Anyway for us to surface that some of the tests were skipped? Just want to make sure even if we're all green we still have that extra info.
There was a problem hiding this comment.
In my view, the test suite is responsible for how it wants to show pass/fail/skip. Currently Isolation Session gives a green check but sends a "SKIPPED: . . ." message in stdout (viewable in logs). Also, future plans include a more robust way to view testing results internally.
| if command -v systemctl >/dev/null 2>&1; then | ||
| if systemctl list-unit-files lxc-net.service >/dev/null 2>&1 && | ||
| systemctl cat lxc-net.service >/dev/null 2>&1; then | ||
| sudo systemctl start lxc-net |
There was a problem hiding this comment.
question: If this fails would this cause the script to abort? Just wondering if we need an error message here.
There was a problem hiding this comment.
Won't abort but yes, it's missing a warning.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 153 out of 271 changed files in this pull request and generated no new comments.
Suppressed comments (1)
scripts/ci/validation-test-matrix.json:330
- The PR description says the full matrix runs nightly and lists macOS 26/15 with Seatbelt support, but this plan contains no macOS trigger. Since the Seatbelt dispatcher also exits 2, the resolver emits no macOS validation jobs at all. Either wire the Seatbelt suite and add these platforms to
nightly, or update the PR description to state that macOS entries are declared but dormant.
"nightly": [
f2d996c to
f18c791
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 26 out of 26 changed files in this pull request and generated 2 comments.
Suppressed comments (2)
scripts/ci/resolve-validation-test-matrix.mjs:65
- A missing
poolcurrently passes validation because only non-null values are type-checked, butexpandPlanthen silently drops that architecture at line 149. An explicit empty string is the documented dormant state; omitting the field is malformed and likely a catalog typo. Require every non-macOS architecture to declare a string pool (which may still be empty).
} else if (details.pool != null && typeof details.pool !== 'string') {
throw new Error(`${platform.id}.${architecture}.pool must be a string`);
.github/workflows/Validation.Tests.Scheduled.yml:6
- The PR description says the schedule is fully configurable in
validation-test-matrix.json, but the cron expressions and dispatch choices are hardcoded in this workflow (anddocs/ci-validation-infrastructure.md:285-290correctly says schedule changes belong here). Please update the PR description so it does not direct maintainers to the catalog for schedule changes.
schedule:
- cron: '0 8 * * 1-6'
- cron: '0 8 * * 0'
7438d24 to
f18c791
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 27 changed files in this pull request and generated no new comments.
Suppressed comments (3)
sdk/node/tests/integration/test-helpers.ts:52
- These new required package binaries are only added to the GitHub artifact manifest. The Azure build still copies Windows files exclusively through
.azure-pipelines/templates/Rust.Build.Job.yml'ssignPattern, which omits both executables, while its SDK integration job runs this same package test on Windows. Official Azure builds will therefore fail with both files missing. Add them to the Azure signing/copy manifest as well, or keep validation-only binaries out of the SDK package expectations consistently.
'wxc-ui-probe.exe',
'wxc-test-driver.exe',
scripts/ci/resolve-validation-test-matrix.mjs:25
- The new catalog validator and expansion logic has no automated tests, despite controlling every scheduled combination and containing important branches for malformed catalogs, dormant pools, architecture suppression, and empty family matrices. Similar JavaScript infrastructure under
scripts/versioning/tests/is unit-tested. Add Node tests forvalidateCatalog,expandPlan, andresolvePlan, and run them in CI so matrix regressions are caught before a scheduled workflow consumes them.
export function validateCatalog(catalog) {
.github/workflows/Validation.Tests.Scheduled.yml:6
- The PR description says the schedule is fully configurable in
validation-test-matrix.json, but the actual timing is hard-coded here (and the repository documentation correctly says schedule changes belong in this workflow). Please update the PR description to distinguish the cron schedule from the catalog-controlled plans, pools, and backend combinations.
- cron: '0 8 * * 1-6'
- cron: '0 8 * * 0'
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 27 changed files in this pull request and generated 1 comment.
Suppressed comments (2)
sdk/node/tests/integration/test-helpers.ts:52
- These new required package binaries are only added to the GitHub artifact manifest. The official Azure build still copies its Windows artifact from
signPatternin.azure-pipelines/templates/Rust.Build.Job.yml:49-58,185-191, which omits both files, whilePackage.NpmSdk.Job.yml:51-57packages that whole artifact. GitHub and official Azure npm tarballs will therefore have different payloads. Add both executables to the Azure build/signing manifest as part of this change (or keep them out of the SDK expectation).
'wxc-ui-probe.exe',
'wxc-test-driver.exe',
tests/scripts/run_ci_backend_tests.sh:51
MXC_LXC_TESTS_REQUIRE_EXECUTIONis not read anywhere byrun_lxc_all_tests.sh(the only repository occurrence is this assignment). That suite counts exit-77 cases as skips, and even its all-skipped path only warns before exiting 0 (run_lxc_all_tests.sh:73-85), so an LXC matrix job can still be green without the required coverage. Implement the flag in the aggregate suite (and fail on the intended skip condition), or have this dispatcher inspect the summary and return nonzero.
MXC_LXC_TESTS_REQUIRE_EXECUTION=1 bash "$script_root/run_lxc_all_tests.sh"
…epel key verification.
…com/microsoft/mxc into user/emichlin/validation-infra-lite
…epel key verification
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 26 out of 26 changed files in this pull request and generated 1 comment.
Suppressed comments (4)
scripts/ci/resolve-validation-test-matrix.mjs:65
- A missing
poolis accepted here even though it is required for Windows/Linux.expandPlanthen treats the missing value like a dormant empty pool and silently emits no job, so a typo in this field can make requested validation disappear while the resolver succeeds. Require the field to be a string; keep""as the explicit dormant value.
if (platform.family === 'macos') {
assertNonEmptyString(details.runner, `${platform.id}.${architecture}.runner`);
} else if (details.pool != null && typeof details.pool !== 'string') {
throw new Error(`${platform.id}.${architecture}.pool must be a string`);
}
scripts/ci/prepare-linux-host.sh:177
- With
set -euo pipefail, this unguarded assignment exits host preparation whenipfails (notably afterstart_lxc_bridgedeliberately warns and returns because the bridge is absent). That contradicts this function's diagnostic-only contract and prevents the backend suite from reporting the actual failure. Make the probe tolerate an absent bridge.
local bridge_ip
bridge_ip="$(ip -4 -o addr show "$bridge" 2>/dev/null |
awk '{print $4}' | cut -d/ -f1 | head -n 1)"
.github/workflows/Build.Windows.Job.yml:119
- These test-only executables are now part of the GitHub product artifact, and
Package.NpmSdk.Job.yml:22-49copies that whole artifact intosdk/node/bin, so GitHub-built npm packages ship internal harnesses that consumers do not need. The Azure artifact excludes them via.azure-pipelines/templates/Rust.Build.Job.yml:49-58, making package contents producer-dependent. Publish a separate validation artifact or explicitly strip these files beforenpm pack.
src/target/${{ matrix.target }}/release/wxc-ui-probe.exe
src/target/${{ matrix.target }}/release/wxc-test-driver.exe
scripts/ci/resolve-validation-test-matrix.mjs:25
- This scheduling-critical resolver has no automated tests; resolving the checked-in nightly catalog only exercises one happy path. Add Node tests for malformed catalogs, empty versus missing pools, ARM64 suppression, duplicate requests, and per-family output, following the existing
scripts/versioning/testspattern.
export function validateCatalog(catalog) {
📖 Description
This PR introduces an E2E validation testing matrix that uses self-hosted runners to run tests across multiple OS versions. Currently includes support for:
x64
arm64
As of now, there are no Azure VMs that support nested virt on ARM CPUs.
Current Backend Support
Schedule
The full test matrix runs nightly on main. Once we get bugs fixed and a clearer idea of cost, the schedule will be adjusted.
The testing matrixes and all self-hosted runners are fully configurable in
/scripts/ci/validation-test-matrix.json.The schedule timing is configurable in
/.github/workflows/Validation.Tests.Scheduled.yml.🔗 References
🔍 Validation
No MXC code was modified. All newly created workflows can run and provide feedback on resolving issues.
✅ Checklist
📋 Issue Type
Microsoft Reviewers: Open in CodeFlow