Skip to content

feat(daemon): scaffold traffic_shaper monitor skeleton in solo-provisioner-daemon#792

Merged
brunodam merged 1 commit into
mainfrom
00746-traffic-shaper-monitor-skeleton
Jul 2, 2026
Merged

feat(daemon): scaffold traffic_shaper monitor skeleton in solo-provisioner-daemon#792
brunodam merged 1 commit into
mainfrom
00746-traffic-shaper-monitor-skeleton

Conversation

@alex-au

@alex-au alex-au commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Description

Stands up the block-node traffic_shaper monitor as a real daemonkit-supervised
component inside solo-provisioner-daemon — the structural skeleton that the rest of
epic TS_3 (#736) hangs off. Until now the monitor was a no-op stub that logged once
and blocked on ctx. It now runs its two long-lived responsibilities concurrently and
reports its health over the daemon socket.

Run(ctx) launches the pod-lifecycle watcher and the statusz poll loop as two
goroutines, each wrapped in a per-responsibility retry loop with 5 s → 5 min
exponential back-off. A fault in one responsibility is logged and retried in place and is
never propagated to Run, so a subsystem fault can't stop the other responsibility, trip
the top-level supervisor, or kill the daemon process. Monitor health (supervisor
running / backoff / stopped) is exposed via GET /block_node/traffic_shaper/status.

This is skeleton only: both responsibility bodies are stubs. The real veth resolution,
ingress HTB attach, statusz client, category→policy diff, nft membership apply, bootstrap
sequencing, and privileged-exec delegation land in sibling stories (#747#755, #774). The
degraded health state and alerting are owned by #750 — this PR reports only supervisor
state.

Files changed

File Change
internal/daemon/blocknode/traffic_shaper_monitor.go Export TrafficShaperMonitor; Run launches the two responsibilities via superviseResponsibility (5 s → 5 min back-off, faults swallowed); stub runPodWatcher / runStatuszPoll bodies
internal/daemon/blocknode/handler.go New BlockNodeHandler (daemonkit.ComponentHandler) serving GET /block_node/traffic_shaper/status; 503 when monitor disabled
internal/daemon/blocknode/component.go ComponentResult carries *TrafficShaperMonitor for handler wiring
internal/daemon/daemon.go Block-node block builds the trafficShaperStateFn closure over the component StatusTracker and registers BlockNodeHandler
internal/daemon/blocknode/traffic_shaper_monitor_test.go Monitor unit tests: returns on ctx cancel; faults retried without propagating; clean re-entry
internal/daemon/blocknode/handler_test.go Handler unit tests: 200 + state when enabled, 503 when disabled
internal/templates/files/weaver/solo-provisioner-daemon.service ReadWritePaths is now a Go template; paths beyond /opt/solo are rendered conditionally at install time
internal/workflows/daemon.go daemonExtraReadWritePaths(cfg) derives the per-component extra paths; passes them to InstallDaemonServiceStep
internal/workflows/steps/step_daemon.go InstallDaemonServiceStep accepts extraReadWritePaths []string; creates each path before rendering the service unit — fixes status=226/NAMESPACE on BN-only hosts where /opt/hgcapp is absent

Review guide

Code review checklist

  • traffic_shaper_monitor.gosuperviseResponsibility never returns a non-nil error: a faulting fn is logged + retried, and the loop exits only on ctx cancellation. This is the core invariant ("no process death on subsystem fault" from Story 3.1 — traffic_shaper monitor skeleton in solo-provisioner-daemon #746).
  • traffic_shaper_monitor.go — back-off floor is 5 s (issue-specified), cap 5 min, factor 2.0; responsibilityBackoffInitial/Max are var (not const) only so tests can shrink them — production never reassigns.
  • traffic_shaper_monitor.goRun waits on both responsibilities (sync.WaitGroup) and returns nil only after both stop on ctx cancel, keeping the top-level daemonkit.SupervisedMonitor contract intact.
  • daemon.gotrafficShaperStateFn closes over comp.tracker after comp is appended to the slice (value copy is fine — tracker is a pointer), mirroring the consensus-handler wiring; handler only added when result.TrafficShaperMonitor != nil.
  • handler.goGET /block_node/traffic_shaper/status returns 503 when mon == nil (disabled) and otherwise the supervisor MonitorState; no degraded field yet (deferred to Story 3.5 — Monitor health: degraded state, /status, and alerting #750). Covered by TestBlockNodeHandler_NilMonitor_Returns503.
  • component.goNewComponent returns the monitor reference; enablement is still gated by block_node.monitors.traffic_shaper in daemon.yaml (unchanged).
  • step_daemon.goInstallDaemonServiceStep takes extraReadWritePaths []string (no CN knowledge); daemonExtraReadWritePaths(cfg) in daemon.go is the single place that maps component config → required paths. Adding a future component's data root is a one-liner there with no touch to the step.
  • solo-provisioner-daemon.serviceReadWritePaths=/opt/solo{{range .ExtraReadWritePaths}} {{.}}{{end}} renders cleanly to ReadWritePaths=/opt/solo on BN-only installs and ReadWritePaths=/opt/solo /opt/hgcapp when CN is enabled. Any listed path that is absent at service start causes status=226/NAMESPACE — the MkdirAll loop in the step must run before installDaemonServiceFiles.

Unit tests

# macOS or CI — these packages have no Linux-only deps:
go test -race -tags='!integration' ./internal/daemon/blocknode/... ./internal/daemon/

# Or the full unit suite (run inside the UTM VM for full coverage):
task vm:test:unit

Manual UAT

Exercises the live /block_node/traffic_shaper/status endpoint over the daemon's unix
socket. Run inside the UTM VM (Linux, arm64).

Step 1 — Build and copy the daemon binary to the VM

# On the Mac host:
task build:daemon GOOS=linux GOARCH=arm64
task vm:sync   # or scp bin/solo-provisioner-daemon-linux-arm64 weaver@<vm-ip>:~

Step 2 — Install the daemon service

# On the VM (as root or via sudo):
sudo solo-provisioner daemon service install \
  --components block-node \
  --daemon-bin ~/solo-provisioner-daemon-linux-arm64

This single command:

  • writes /opt/solo/weaver/config/daemon.yaml with block_node.enabled: true and
    monitors.traffic_shaper: true
  • copies the binary to /opt/solo/weaver/bin/solo-provisioner-daemon
  • installs, enables, and starts the solo-provisioner-daemon.service systemd unit

Step 3 — Verify the service is healthy

solo-provisioner daemon service check

Expected: all checks pass.

Step 4 — Confirm startup log lines

journalctl -u solo-provisioner-daemon -n 50 --no-pager

Expected log lines (in order):

reason=TrafficShaperMonitorStarting   monitor=bn-traffic-shaper-monitor
reason=TrafficShaperPodWatcherStub    monitor=bn-traffic-shaper-monitor
reason=TrafficShaperStatuszPollStub   monitor=bn-traffic-shaper-monitor

Step 5 — Query the traffic-shaper status endpoint

The daemon socket (/opt/solo/weaver/daemon/daemon.sock) is owned by weaver:weaver
(srw-rw----). Use sudo or add your user to the weaver group before querying:

sudo curl --unix-socket /opt/solo/weaver/daemon/daemon.sock \
  http://localhost/block_node/traffic_shaper/status

Expected HTTP 200:

{"monitor":{"state":"running"}}

Disabled-path (503) coverage: reinstalling with --components consensus-node on a
BN-only VM isn't a realistic scenario (requires CN infrastructure). The 503 path is
covered by TestBlockNodeHandler_NilMonitor_Returns503 in handler_test.go — no live
step needed.

Risks / rollback

  • Additive and gated behind block_node.enabled + monitors.traffic_shaper; with the
    monitor disabled, daemon behavior is unchanged.
  • Main invariant to protect: superviseResponsibility must never propagate a fault to
    Run — a regression there lets a stub fault bubble up and trip the top-level supervisor.
    Covered by TestSuperviseResponsibility_RetriesFaultsWithoutPropagating.
  • The service unit template now uses {{range .ExtraReadWritePaths}} — any path added to
    the template data that does not exist at service start causes status=226/NAMESPACE. The
    MkdirAll loop in InstallDaemonServiceStep.Execute must always run before
    installDaemonServiceFiles. Covered by integration tests in step_daemon_it_test.go.
  • Rollback: revert the PR; the prior no-op stub returns and no other component is affected.

Related Issues

@alex-au alex-au requested a review from a team as a code owner June 30, 2026 05:47
@alex-au alex-au requested a review from JeffreyDallas June 30, 2026 05:47
@swirlds-automation

swirlds-automation commented Jun 30, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@brunodam brunodam force-pushed the 00746-traffic-shaper-monitor-skeleton branch 3 times, most recently from 4340bc3 to 7be88ae Compare July 2, 2026 20:20
…ioner-daemon

Turn the block-node traffic_shaper monitor from a no-op stub into the
daemonkit-supervised skeleton for epic TS_3. Run now launches the
pod-lifecycle watcher and statusz poll loop concurrently, each retried
in place with 5s->5min exponential back-off so a subsystem fault never
kills the daemon. Monitor health is exposed via
GET /block_node/traffic_shaper/status.

Both responsibility bodies remain stubs; the real veth/HTB/statusz/nft
logic lands in sibling stories (#747-#755, #774).

Closes #746

Signed-off-by: alex-au <alex.w.aus@gmail.com>
Signed-off-by: Bruno Marques <bruno.marques@swirldslabs.com>
@brunodam brunodam force-pushed the 00746-traffic-shaper-monitor-skeleton branch from 7be88ae to 09e8f65 Compare July 2, 2026 20:25

@brunodam brunodam left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@brunodam brunodam merged commit 652c1be into main Jul 2, 2026
17 checks passed
@brunodam brunodam deleted the 00746-traffic-shaper-monitor-skeleton branch July 2, 2026 20:42
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.

Story 3.1 — traffic_shaper monitor skeleton in solo-provisioner-daemon

3 participants