Skip to content

feat(trace-topology): Rust trace-fixture generator (netns + TSN, v0.11.0)#233

Merged
avrabe merged 1 commit into
mainfrom
feat/v0.11.0-trace-fixtures
May 21, 2026
Merged

feat(trace-topology): Rust trace-fixture generator (netns + TSN, v0.11.0)#233
avrabe merged 1 commit into
mainfrom
feat/v0.11.0-trace-fixtures

Conversation

@avrabe
Copy link
Copy Markdown
Contributor

@avrabe avrabe commented May 21, 2026

Summary

v0.11.0 PR 1. A Rust binary — gen-fixtures, in the spar-trace-topology crate — that produces the four runtime artefacts the reconciliation engine will test against, from a synthetic 3-node TSN topology (grandmaster / switch / endpoint) built in Linux network namespaces.

Why Rust, not a shell script

The maintainer's standing preference — and it earns its keep here:

  • Typed config; Result error propagation with context on every fallible step.
  • NetnsGuard RAIIDrop runs ip netns del, so a panic or ?-propagated error still tears the topology down. A shell trap silently no-ops on a crash and leaves stale /run/netns handles for the next run to collide with.
  • The two transform steps are pure, unit-tested functions.

What it generates

Fixture Source
capture.pcapng tcpdump on the GM veth
lldp.json lldpd -H 0 + lldpctl -f json
qcc-yang.json tc taprio/cbstc -j qdisc show → transformed to the QccYangSwitchConfigSource shape
gptp.json ptp4l + pmc → transformed to the GptpJsonPtpTimeSource shape

Two gotchas baked in from smithy's live testing: veths are created multi-queue (numtxqueues/numrxqueues 4) because sch_taprio rejects single-queue devices; taprio runs in software mode (flags 0x0) — no TSN NIC.

gPTP timestamp_ns is replaced by a deterministic synthetic counter (fixed epoch + step) so fixture diffs aren't noisy; sync_error_ns (abs master offset) is kept as observed. Qci stream-filters have no Linux kernel equivalent — synthesised from a static config, documented as such in the code.

Scope

Tool only. The 9 transformer unit tests run on any host (no netns dependency) and every generated JSON round-trips through the ingest.rs parsers. The CI harness that runs the netns-touching path is deliberately a follow-up: per the smithy QEMU enablement it'll be a KVM guest, not the netns runner — building that workflow now would be rework.

Test plan

  • cargo build -p spar-trace-topology --bins clean
  • cargo test -p spar-trace-topology — 34 tests pass (9 new transformer tests)
  • cargo clippy -p spar-trace-topology --all-targets -- -D warnings clean
  • cargo fmt --check clean
  • rivet validate PASS
  • CI green

Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

…1.0)

First PR of v0.11.0. A Rust binary — `gen-fixtures`, in the
spar-trace-topology crate — that produces the four runtime artefacts the
reconciliation engine will test against, from a synthetic 3-node TSN
topology (grandmaster / switch / endpoint) built in Linux network
namespaces.

Why Rust, not a shell script:
- typed config and `Result` error propagation with context on every
  fallible step;
- `NetnsGuard` RAII — `Drop` runs `ip netns del`, so a panic or
  `?`-propagated error still tears the topology down. A shell `trap`
  silently no-ops on a crash and leaves stale `/run/netns` handles;
- the two transform steps are pure, unit-tested functions.

What it generates:
- PCAPNG L2 capture (tcpdump on the GM veth);
- LLDP neighbour JSON (lldpd -H 0 + lldpctl -f json);
- Qcc YANG switch-config JSON — `tc taprio`/`cbs` read via `tc -j qdisc
  show` and transformed to the shape QccYangSwitchConfigSource ingests.
  veths are created multi-queue (numtxqueues/numrxqueues 4) because
  `sch_taprio` rejects single-queue devices; software mode (flags 0x0);
- gPTP sync-error JSON — ptp4l + pmc, with `timestamp_ns` replaced by a
  deterministic synthetic counter (fixed epoch + step) so fixture diffs
  aren't noisy; `sync_error_ns` (abs master offset) is kept as observed.

Qci stream-filters have no Linux kernel equivalent — they are synthesised
from a static topology config and documented as such in the code.

9 transformer unit tests (tc->Qcc, pmc->gPTP, LLDP validation) run on any
host with no netns dependency; all generated JSON round-trips through the
ingest.rs parsers. The CI harness that runs the netns-touching path — a
KVM guest, per the smithy QEMU enablement — lands in a follow-up PR.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@avrabe avrabe enabled auto-merge (squash) May 21, 2026 03:56
@github-actions
Copy link
Copy Markdown

Rivet verification gate

20/20 passed

count
Passed 20
Failed 0
Skipped (no steps) 0

Filter: (and (= type "feature") (or (has-tag "v093") (has-tag "v0100")))

Failed artifacts

(none)

Updated automatically by tools/post_verification_comment.py. Source of truth: artifacts/verification.yaml.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 21, 2026

@avrabe avrabe merged commit 90b8052 into main May 21, 2026
16 of 18 checks passed
@avrabe avrabe deleted the feat/v0.11.0-trace-fixtures branch May 21, 2026 09:02
avrabe added a commit that referenced this pull request May 26, 2026
Workspace bump from 0.10.0 → 0.11.0 across all 22 spar crates (via
[workspace.package].version), plus the VS Code extension's
package.json — the two version surfaces the release workflow's
check-versions job compares against the tag.

What v0.11.0 ships, since v0.10.0:

  trace-topology reconciliation engine (incremental rollout):
    #239  IdentityUnknown check (PR 3a — component-borne MAC + chassis-id)
    #241  GptpOutOfBudget check  (PR 3b — single-budget case)

  trace-topology fixture pipeline:
    #233  Rust gen-fixtures tool (netns + TSN, RAII teardown)
    #234  NixOS guest + QEMU harness for fixture generation
    #238  corrected nixos/nix container digest (fd7a5c67…, multi-arch index)
    #240  podman runner label so workflows schedule on the only
          rootless-podman-capable runner in the fleet (runner9)

  codegen:
    #232  --format wit emits only WIT (strict-filter; no Rust/Bazel
          workspace leakage)
    #242  per-category file-count summary + hint when WIT was
          requested but the model has no `process` subcomponents
          (in-flight at bump time; auto-merge armed)

  release flow:
    #244  standardise on the synth reference — actions/attest-build-
          provenance@v2 + sigstore cosign sign-blob (v2.4.1) over
          SHA256SUMS.txt, build-env.txt; the v0.11.0 release is the
          first one to exercise the standardised cosign + SLSA chain.

Verification after release:
  cosign verify-blob \\
    --certificate-identity-regexp \\
      'https://github.com/pulseengine/spar/.github/workflows/release.yml@.*' \\
    --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \\
    --bundle SHA256SUMS.txt.cosign.bundle SHA256SUMS.txt
  gh attestation verify spar-v0.11.0-<triple>.tar.gz --repo pulseengine/spar

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant