Repository navigation
ci: run frr's image jobs on arc-dind, and fix the two tests that kept them red - #134
Conversation
… lane Build, Build-LTTng and Unit-Test in github-ci.yml, and all four buildcache-seed.yml jobs, move from `runs-on: default` to `arc-dind`. GitHub hands queued `default` jobs to runners org-wide and not oldest-first. On 2026-10-02 frr's Builds waited 12-37h there while about 1,300 newer `default` jobs from other repos started (3 from frr), so master CI never finished and buildcache-seed never refreshed the registry caches. arc-dind's queue averaged 2.5 jobs (max 12) over the same 24h, against ~17 for arc-default. Moving there changes no shared ARC template, so no lane re-roll. The ARC capacity owner proposed it and will size arc-dind from measurement if its queue degrades. arc-dind uses the same runner image. BuildKit compiles frr in the dind sidecar, whose limits there are twice arc-default's (4 CPU, 16Gi memory, 40Gi ephemeral storage, against 2, 8Gi and 20Gi), and frr's gtm-image-build already runs buildx there. The one smaller piece is the runner's work dir (node-local emptyDir instead of a 40Gi Ceph volume); Build pushes its image to the registry and writes no tarball, so the work dir holds only the checkout and test results. HTML documentation stays on `default`: it runs only on doc changes and needs sudo apt plus more memory than an arc-light runner has. The other `default` workflows (behind-base, conflicts, size-label, stale, ...) are gated on github.repository == 'frrouting/frr' and never run here. Signed-off-by: Omar Ramadan <omar@blockcast.net> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Ally — Consolidated PR Review
Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: d34d3c3
Looks good. The lane move is consistent, precedented in this repo, and I could not find a way it breaks the moved jobs. Verified against the files at this head rather than the PR body:
- No orphaned
default.buildcache-seed.ymlhas fourruns-on:and all four moved.github-ci.ymlkeepsdefaultonly onDocumentation-HTML:256, which is the one job that callssudo apt-get(:267-268) — the stated exception matches the onlysudoin a moved job's reach (the other hits,:941+, are theTestlane onarc-e2e, untouched). - dind-safe. The classic dind trap is a workspace bind-mount that does not resolve in the sidecar.
Unit-Testhas none:docker runatgithub-ci.yml:626passes no-v, and results come out viadocker cp(:667,:670), which is daemon-side. The two-v /lib/modulesmounts (:989,:1251) are inTest, still onarc-e2e. - No tarball, so the smaller work dir holds.
Build's only exporter istype=image,...,push=true(github-ci.yml:396) and seed's istype=cacheonly(buildcache-seed.yml:196); both check out atfetch-depth: 1. The PR body's claim checks out. - Guards still pass for the right reason, not by luck.
test_same_runner_gate_and_timeoutcompares_header(lttng) == _header(build), and_codedrops comment-only lines — so the asymmetric comments added toBuild(9 lines) andBuild-LTTng(1 line) are invisible to it and the tworuns-on:values moved together.check-runner-labels.pyonly rejects hosted labels (HOSTED_LABEL,:8), soarc-dindis unconstrained by it. - Precedent confirmed at this head:
gtm-image-build.yml:32isarc-dindand runssetup-buildx-actionthere (:49);nbg6817-openwrt-ipk.yml:111likewise.
Critical Issues (0)
Important Issues (0)
Suggestions (3)
- [gstack/review]
.github/workflows/buildcache-seed.yml:87,:206,:260— probe / verify / freshness-gate are purepython3+ registry-HTTP jobs (buildcache_freshness.pyimports onlyurllib/json/base64; nosubprocess, no docker, nosudo), each capped attimeout-minutes: 10. They currently take a 4-CPU/16Gi dind pod to read manifest timestamps.arc-lightalready runs this exact class in-repo —CI-Verdict(github-ci.yml:1527) shellspython3 .github/scripts/topotest_verdict.pythere. Moving the three would free 3 of the 4 arc-dind slots a seed cycle consumes and leave only the realSeedmatrix (:152) on the heavy lane. Not a blocker: the PR's goal is achieved either way. - [comments]
.github/workflows/github-ci.yml:291-299— the comment records every dimension that grows (4 CPU, 16Gi, 40Gi) and omits the one that shrinks: runner work dir goes from a 40Gi Ceph PVC to a node-local emptyDir at a 24Gi runner limit. That is in the PR description but not in the durable record, and this file's convention is that the number lives next to the decision. It is safe today only because offetch-depth: 1and the single pushing exporter — exactly the two things a future change would alter without reading the PR. Same comment's closing clause ("more memory than an arc-light runner has") justifies not movingDocumentation-HTMLtoarc-light, but the job it describes stays ondefault; naming both alternatives would stop the next reader re-deriving it. - [code] Capacity, for the record rather than for this PR: arc-dind
maxRunnersis 14 against an observed max of 12, and frr's peak demand is ~5-6 concurrent (Build×2 +Build-LTTng, thenUnit-Test×2; or probe →Seed×3 → verify → gate). A scheduled seed overlapping a master push can reach the cap. Still strictly better thandefault, where the wait is 12-37h and unbounded by any queue discipline — worth a follow-up measurement after a week, not a change here.
Strengths
- Every
runs-on:change carries a rationale comment pointing at a single canonical explanation rather than duplicating it, which is what keepstest_same_runner_gate_and_timeouthonest instead of brittle. - The exception (
Documentation-HTML) is justified by a property I could verify independently from the file — it is the only moved-adjacent job invokingsudo apt-get. - Touching no shared ARC template means no lane re-roll for other repos; the blast radius really is this repo's two workflow files.
- The PR body states what was not run (
TestBoundedErrorBodyOverRealSocket) and why it is irrelevant to the diff, which is the right shape for a test claim.
Recommended Action
- No blocking changes requested.
- Merge once the remaining required CI checks finish green.
|
Ubuntu 22.04 Test shard 2/2 failed on |
…O-36555) (#135) _parse_jp accepted a Join only as an inline entry ("Join: 10.10.10.10/32") or under a heading that ends in a colon, and explicitly skipped "Num Joins: 1". But "Num Joins" is the heading Wireshark prints. 3.6.2 (Ubuntu 22.04) and 4.2.2 (24.04) give byte-identical output: Num Joins: 1 IP address: 10.10.10.10/32 (S) Num Prunes: 0 So every Join/Prune parsed to no entries. F9 (test_jp_upstream_is_hello_neighbor) failed on every run ("no Join(10.10.10.10,232.1.1.10) decoded from the capture"), and topotest skipped F10 behind it. Had F10 run, its "no Join was sent" check would have passed whatever pimd sent. #134's 22.04 shard 2/2 failed this way, in both the parallel run and the serial rerun. Fixes: - A "Num Joins" / "Num Prunes" heading now sets the kind for the "IP address:" lines under it. - test_parse_jp_reads_tsharks_layout pins the parser to that layout. Its fixture is the two releases' output for one J/P with a join group and a prune group. - The failure messages now quote the dissection from the PIM header on. The Ethernet/IP/GRE layers used up the old 4000-character excerpt before the first Join entry, which is why the CI log never showed the layout. Signed-off-by: Omar Ramadan <omar@blockcast.net> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
probe-before, verify and freshness-gate are a checkout plus buildcache_freshness.py, which reads Harbor over urllib: no docker, no sudo, 10-minute timeouts. On arc-dind each one took a 4-CPU/16Gi dind pod to read manifest timestamps, and three of the four arc-dind slots a seed cycle used. arc-light already runs this class of job here (CI-Verdict runs topotest_verdict.py there). Only the `seed` matrix, which builds, stays on arc-dind. Build's comment now also records the dimension that shrinks on arc-dind, the runner work dir (node-local emptyDir under a 24Gi limit instead of default's 40Gi Ceph volume), and the two properties that keep it safe: a fetch-depth 1 checkout and no tarball exporter. It also says why Documentation-HTML goes to neither arc-dind nor arc-light. Both are suggestions from Ally's review of #134. Signed-off-by: Omar Ramadan <omar@blockcast.net> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… interface up The OSPF authentication tests (tc28, tc29, tc30, tc32) were flaky because authentication was configured AFTER bringing the interface up. This created a window where R1 sent unauthenticated Hello packets that R2 (with authentication configured) would reject. The sequence was: 1. reset_config_on_routers() - removes R1's authentication config 2. shutdown_bringup_interface(False) - interface down 3. shutdown_bringup_interface(True) - interface up (R1 starts sending unauthenticated Hellos) 4. config_ospf_interface() - configure authentication (too late) 5. clear_ospf() - restart OSPF Fix by moving authentication configuration between interface shutdown and bringup: 1. reset_config_on_routers() - removes R1's authentication config 2. shutdown_bringup_interface(False) - interface down 3. config_ospf_interface() - configure authentication while down 4. shutdown_bringup_interface(True) - interface up (R1 sends authenticated Hellos from the start) 5. clear_ospf() - restart OSPF This ensures R1 sends authenticated packets from the first Hello after the interface comes up, eliminating the authentication mismatch window. Signed-off-by: Enke Chen <enchen@paloaltonetworks.com> (cherry picked from commit 062ce56bcaf60188a6087e7e01be7dbca51446ed) Signed-off-by: Omar Ramadan <omar@blockcast.net> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Updated: this PR now carries four commits, all needed for frr's CI to finish green on its new lanes. I'll merge it with rebase, not squash, so each commit lands on master on its own.
How #135 got here: I merged #135 through Evidence:
|
default laneThere was a problem hiding this comment.
Ally — Consolidated PR Review
Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 7007411
The three functional changes since my last pass are all correct, and I verified each against the files at this head rather than the PR body:
- The
arc-lightsplit is sound.probe-before,verifyandfreshness-gate(buildcache-seed.yml:87,:210,:265) areactions/checkout+python3 .github/scripts/buildcache_freshness.py, which imports onlyurllib/json/base64— no docker, nosudo.seed(:155) keepsarc-dind, which is the only job that builds.check-runner-labels.pyonly rejects hosted labels (HOSTED_LABEL,:8), so neither label is constrained by it. - The guard still passes for the right reason.
test_same_runner_gate_and_timeoutcompares_header(lttng) == _header(build), and_header→_codedrops comment-only lines (test_buildcache_freshness.py:510-514), so Build's comment growing to 17 lines against Build-LTTng's 2 stays invisible to it.test_buildcache_freshness.pyasserts nothing aboutruns-on, so the seed split does not touch it. - The parser fix is right, and I ran it. Extracted
_parse_jpandJP_TSHARK_TEXTat this head and executed them standalone:joins == [('10.99.0.1','232.1.1.10','10.10.10.10')],prunes == [('10.99.0.1','232.1.1.11','10.10.10.10')]— the new test passes. The inline shape the docstring promises is still accepted (fedJoin 0:/Prune 0:separately; both parsed).Num Groups: 2does not matchheading_re(^Num\s+(Join|Prune)s\b), andGroup: 232.1.1.10does not matchgroup_re(^Group\s+\d+\bneeds whitespace, not:), so neither nested line corrupts the state machine. - The OSPF fix is scoped to the actual root cause, all four sites, nothing missed. The four changed blocks are exactly the four
reset_config_on_routers(tgen, routerName="r1")sites (:293,:506,:740,:980) — the only places r1's auth is wiped and re-applied, so the only places an unauthenticated-Hello window exists. The four unchanged shut/no-shut pairs (:257/270,:468/481,:702/715,:942/955) already have auth configured on both sides beforehand, so they are correctly left alone.tc35has no mid-test r1 reset and needs no equivalent change.
Both findings below are durable-record defects this diff creates in the file it is about. No runtime behaviour is wrong.
Critical Issues (0)
Important Issues (2)
- [comments]
.github/workflows/github-ci.yml:38-41— the cost model justifyingcancel-in-progress: falsestill reads "Each such run puts 5 jobs ondefault(the 2 Build legs and Build-LTTng, the 2 Unit-Test legs, plus Documentation-HTML when docs change) … next to adefaultbacklog of about 800 queued jobs org-wide". After this diff a master run puts one job ondefault(Documentation-HTML, docs-only) and five onarc-dind. This is not a description that drifted — it is the written justification for leaving master uncancellable, and its "adds to PR queue waits" premise is about thedefaultfootprint that this PR removes. The paragraph has already been curated once for exactly this dimension ("the path filter runs on arc-light, see doc-path-filter"), so the convention is clearly to keep the count current.- Restate the split as 1 on
default/ 5 onarc-dind/ 4 on arc-e2e, and either re-state or drop thedefault-backlog clause, which no longer bears on this workflow's own cost.
- Restate the split as 1 on
- [comments]
.github/workflows/github-ci.yml:129-131— "Were the two images ever to differ on zstd, Build, Build-LTTng and buildcache-seed.yml, all ondefault, could restore no entry this job saves". All three move in this PR: Build and Build-LTTng toarc-dind, andbuildcache-seed.ymltoarc-dind(seed) plusarc-light(the other three). The conclusion survives — the comment's own premise is thatrunner-image-pins.rbpins every pool to one image — but the named consumer set is wrong and "the two images" is now three pools. This is specifically the comment a future reader consults to decide whether anarc-light-saved MIB entry restores on the pool that consumes it, and that pool changed here.- Name
arc-dindas the consumer and drop "all ondefault"; the pinning argument itself needs no change.
- Name
Suggestions (2)
- [tests]
tests/topotests/pim_dimt_tunnel_normal/test_pim_dimt_tunnel_normal.py:458— "Pure parsing, so it runs without the topology" is true of the test body and not of its collection:setup_module(:124) callsTopogen(...).start_topology()andtgen.start_router()before any test in this module, so a topology failure errors this guard along with everything else. The parser regression it exists to catch is therefore not observable independently of the thing it is meant to be independent of. Either soften the claim, or move the fixture and this one assertion into a sibling module with nosetup_moduleif you want the guard to survive a broken topology — which is the case where a silent parse failure is hardest to spot. - [gstack/review]
.github/workflows/buildcache-seed.yml:87,:210,:265— these three now reachregistry.blockcast.netfromarc-light, and no existingarc-lightjob does: the two in-repo users aredoc-path-filter(github-ci.yml:134, GitHub API + IANA/IEEE) andCI-Verdict(:1533, artifacts only). Ifarc-light's pod template carries tighter egress thanarc-dind's, all three fail on the first cycle. That fails closed and loudly (verifyrefuses to certify,freshness-gatepropagates), so it is a revert rather than a risk — but the next natural proof iscron: '23 2,14 * * *'.workflow_dispatchis already wired (:47); one manual run proves Harbor is reachable from that lane in about a minute rather than waiting for the schedule.
Strengths
- The
arc-lightsplit is the sharper version of what I suggested: it leaves only the job that actually builds on the heavy lane, and the three light jobs point at one canonical rationale instead of duplicating it — the same discipline that keepstest_same_runner_gate_and_timeouthonest rather than brittle. JP_TSHARK_TEXTis real captured output pinned to two named Wireshark versions, and the new test asserts the exact triples rather than a truthiness check, so a future layout change fails with the actual data in the message instead of silently parsing to nothing. That directly closes the failure the docstring describes — F9 unable to pass and F10 unable to fail.pim_text()fixes a diagnostic that was quietly useless: a 4000-character excerpt from offset 0 was spent on Ethernet/IP/GRE and cut off before the first Join, so the one message designed to explain a parse failure never showed the parse. Themax(find(...), 0)fallback degrades to the old behaviour rather than throwing.- The OSPF change moves one call rather than adding a sleep or a retry, and the rewritten comment states the actual ordering constraint ("BEFORE bringing the interface up") instead of the previous one that named
clear_ospf— which was no longer what the code was ordering against.
Recommended Action
- Address Important issues this cycle.
- Consider Suggestions opportunistically.
Two comments still described the old lanes. Both are fixed from Ally's review of 7007411. - The `cancel-in-progress: false` cost model said a master run puts 5 jobs on `default`. It now puts 5 on arc-dind, 4 on arc-e2e and 1 on `default` (Documentation-HTML, docs changes only). The `default` backlog clause no longer bears on this workflow and is dropped. - doc-path-filter's MIB-cache note said every consumer of the entry is on `default`. Build, Build-LTTng and buildcache-seed's `seed` all restore it on arc-dind, which shares arc-light's pinned runner image. Also, test_parse_jp_reads_tsharks_layout no longer claims to run without the topology. Its body is pure parsing, but setup_module still builds the topology first. Signed-off-by: Omar Ramadan <omar@blockcast.net> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Round 2 at 1d27120:
|
There was a problem hiding this comment.
Ally — Consolidated PR Review
Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 1d27120
Looks good. The single commit since my last pass (1d271206, "restate github-ci.yml's lane counts") is comment-and-docstring only — git compare 70074119...1d271206 is 12+/12- in github-ci.yml and 2+/2- in the pim test, with no executable line touched — and it closes both Important findings I raised at 70074119. I re-derived the new counts from the files at this head rather than taking the commit message's word:
- The new lane split is arithmetically right.
github-ci.yml:39-40now claims 5 on arc-dind / 4 on arc-e2e / 1 ondefault. Counted from the matrices at this head: Build has twocfgrows (:341-342), Build-LTTng one (:485), Unit-Test two (:603-604) — 5 onarc-dind(:306,:473,:535). Test is 2cfg×shard: [1, 2](:876-879) — 4 onarc-e2e(:733).Documentation-HTML(:256) is the onlyruns-on: defaultleft, gated onneeds.doc-path-filter.outputs.docs == 'true'(:255), which is exactly the "only when docs change" the comment now states. - The MIB-cache consumer set is now the real one. Grepping
actions/cacheat this head: one save (github-ci.yml:212, indoc-path-filteronarc-light) and three restores —Build(:353),Build-LTTng(:496) andbuildcache-seed.yml:176, which falls inside theseedjob (:130-:202), notverify(:203). All three restorers are onarc-dind. So "buildcache-seed.yml's seed, all on arc-dind" names the set precisely, and "no other job saves one" still holds — there is exactly oneactions/cache@v5. - "Both pools" → "arc-light and arc-dind" resolves a referent that the split had broken. With
buildcache-seed.ymlnow spanning two lanes, "the two images" in the next sentence had no antecedent; naming the saver's pool and the restorers' pool fixes that without touching the pinning argument, which was always the load-bearing part. - No new guard exposure.
test_same_runner_gate_and_timeoutcompares_header(lttng) == _header(build)and_header→_codedrops comment-only lines (test_buildcache_freshness.py:510-514); this commit changed only the file-level concurrency comment and a comment insidedoc-path-filter, neither of which is in either job's header. Nothing in this commit moves aruns-on:.
Prior Findings Dispositioned (2)
- prior:7007411 important 1 — fixed —
.github/workflows/github-ci.yml:37-44— the cost model now reads "5 jobs on arc-dind … 4 Test legs on arc-e2e, and 1 ondefault(Documentation-HTML, only when docs change)", and the staledefault-backlog clause is gone rather than restated. Counts verified against the four matrices above. - prior:7007411 important 2 — fixed —
.github/workflows/github-ci.yml:130— "all ondefault" is replaced by "buildcache-seed.yml's seed, all on arc-dind", and:125now namesarc-light and arc-dindinstead of "Both pools". Consumer set verified from the threeactions/cache/restore@v5sites and their jobs'runs-on.
Critical Issues (0)
Important Issues (0)
Suggestions (1)
- [gstack/review]
.github/workflows/buildcache-seed.yml:87,:210,:265— carried forward unchanged from my last pass, since it is the one claim in this diff still resting on an untested assumption. These three now reachregistry.blockcast.netfromarc-light, and no existingarc-lightjob does: the two in-repo users aredoc-path-filter(github-ci.yml:134, GitHub API + IANA/IEEE) andCI-Verdict(:1533, artifacts only). Ifarc-light's pod template carries tighter egress thanarc-dind's, all three fail on the first cycle. That fails closed and loudly (verifyrefuses to certify,freshness-gatepropagates), so it is a revert rather than a risk — but the next natural proof iscron: '23 2,14 * * *'.workflow_dispatchis already wired (:47); one manual run proves Harbor is reachable from that lane in about a minute rather than waiting for the schedule.
Strengths
- The restated paragraph keeps the convention that made the original worth reading: it carries the number, not a hand-wave, and it points at
Build's runs-onfor the rationale rather than duplicating it — so the next lane move has exactly one place to update and one place to check. - Dropping the
default-backlog clause outright was the better of the two options I offered. Re-stating it would have preserved a figure that no longer bears on this workflow's own cost, and a number that is true-but-irrelevant ages worse than one that is absent. - The pim docstring now separates the test body from its collection ("The body is pure parsing; setup_module still builds the topology before it runs",
test_pim_dimt_tunnel_normal.py:458-459) instead of softening the claim into vagueness. That is the honest version: it tells a reader both what the guard isolates and what it still depends on. - The whole functional surface of this PR — the lane moves, the
_parse_jpheading fix withJP_TSHARK_TEXTas real pinned capture output,pim_text(), and the four OSPF auth-before-no-shut reorderings — is unchanged since I verified it at70074119, including executing the parser standalone. Holding the functional diff still while fixing the durable record is what makes this head cheap to re-review.
Recommended Action
- No blocking changes requested.
- Merge once the remaining required CI checks finish green.
|
Ally's carried suggestion (arc-light egress to Harbor) is now proven on the wire:
This also refreshes |
|
Merged by kkroo drive session 7ee27e at |
|
Merged as squash commit 68f9499 (by another session, 17:43Z). Its tree is identical to the reviewed head 1d27120. The squash reused the original PR description as its message, so for the record, 68f9499 contains five commits:
Final CI on this head: run 37204045917, 20 pass / 5 skipped, first attempt. |
Why
frr's Build legs sit in the org-wide
defaultqueue for 12-37 hours. GitHub hands queueddefaultjobs to runners org-wide, and not oldest-first. On 2026-10-02 about 1,300 newerdefaultjobs from other repos started while 3 from frr did.The effects:
buildcache-seed.ymlnever completes, so the registry caches (cache/frr-ci:*-buildcache) have been stale since docker: declare ENABLE_LTTNG at the LTTng layer so the 24.04 legs can share the base layers #126;The ARC capacity owner diagnosed this and proposed this lever: give frr's image jobs a lane outside
default. It changes no shared ARC template, so there is no lane re-roll.What
Documentation-HTML stays on
default. It runs only on doc changes, and it needs sudo apt plus more memory than an arc-light runner has.The remaining
defaultworkflows are behind-base, conflicts, size-label, stale, base-branch-label, mergifyio_backport and docker-daily-master. All are gated ongithub.repository == 'frrouting/frr', so they never run on this fork. freeze runs only on labelled PRs.Fit on arc-dind
Live AutoscalingRunnerSet specs, read 2026-10-03:
gtm-image-build.ymlandnbg6817-openwrt-ipk.ymlalready run on arc-dind.Testing
check-runner-labels.pypasses..github/scriptsunittests pass: 531 tests. Not run:TestBoundedErrorBodyOverRealSocket, which hangs on macOS sockets and touches no file in this diff.test_buildcache_freshness's same-runner check (Build-LTTng mirrors Build) passes.🤖 Generated with Claude Code