Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/unbounded-net-node/status_detail_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func TestNodeStatusDetailConfig(t *testing.T) {
name, yaml, flag, want string
invalid bool
}{
{name: "default", yaml: "node: {}", want: "full"},
{name: "default", yaml: "node: {}", want: "summary"},
{name: "summary", yaml: "node:\n statusDetailMode: summary", want: "summary"},
{name: "full", yaml: "node:\n statusDetailMode: full", want: "full"},
{name: "invalid YAML value", yaml: "node:\n statusDetailMode: invalid", invalid: true},
Expand Down
5 changes: 3 additions & 2 deletions cmd/unbounded-net-node/status_publication_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/coder/websocket"
"google.golang.org/protobuf/proto"

configpkg "github.com/Azure/unbounded/internal/net/config"
netstatus "github.com/Azure/unbounded/internal/net/status"
statusproto "github.com/Azure/unbounded/internal/net/status/proto"
statusv1alpha1 "github.com/Azure/unbounded/internal/net/status/v1alpha1"
Expand All @@ -28,7 +29,7 @@ func TestSummaryPublicationNeverCollectsDetails(t *testing.T) {
h := blockedBootstrapHealthState()
h.setStatusServer(s)

cfg := &config{StatusDetailMode: "summary", StatusPushDelta: true}
cfg := &config{StatusDetailMode: configpkg.DefaultStatusDetailMode, StatusPushDelta: true}
for _, force := range []bool{true, false} {
msg, base := collectPublication(h, cfg, &NodeStatusResponse{Peers: make([]WireGuardPeerStatus, 100)}, force, 42)
if base != nil || msg.Status != nil || msg.Delta != nil || msg.Type != statusv1alpha1.NodeStatusSummaryType || msg.Summary == nil {
Expand Down Expand Up @@ -131,7 +132,7 @@ func TestRoutineSummaryPublishers(t *testing.T) {
defer server.Close()

cfg := &config{
NodeName: "node-a", StatusDetailMode: "summary", StatusPushEnabled: transport == "HTTP",
NodeName: "node-a", StatusDetailMode: configpkg.DefaultStatusDetailMode, StatusPushEnabled: transport == "HTTP",
StatusWSEnabled: transport == "WS", StatusPushURL: server.URL, StatusWSURL: "ws" + strings.TrimPrefix(server.URL, "http"),
StatusPushInterval: 5 * time.Millisecond, StatusPushDelta: true, StatusWSAPIServerMode: statusWSAPIServerModeNever,
CriticalDeltaEvery: 5 * time.Millisecond, StatsDeltaEvery: 7 * time.Millisecond, FullSyncEvery: 9 * time.Millisecond,
Expand Down
4 changes: 2 additions & 2 deletions deploy/net/01-configmap.yaml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ data:
statusPushEnabled: {{ default "true" .NodeStatusPushEnabled }}
statusPushURL: "{{ default "" .NodeStatusPushURL }}"
statusPushDelta: {{ default "true" .NodeStatusPushDelta }}
# Preparatory, startup-only; keep full until summary rollout is activated.
statusDetailMode: "{{ default "full" .NodeStatusDetailMode }}"
# Startup-only; full restores legacy node publication, not bulk exports.
statusDetailMode: "{{ default "summary" .NodeStatusDetailMode }}"
statusPushInterval: "{{ default "60s" .NodeStatusPushInterval }}"
statusPushApiserverInterval: "{{ default "60s" .NodeStatusPushApiserverInterval }}"
healthCheckPort: "{{ default "9997" .NodeHealthCheckPort }}"
Expand Down
41 changes: 31 additions & 10 deletions docs/net/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,40 @@ Both binaries now load runtime settings from a shared YAML file mounted from the
- Startup behavior: fail-fast if the config file is missing or invalid
- CLI flags still work as explicit overrides when set

### Preparatory detail status settings
### Summary publication and explicit details

These startup-only settings prepare the lightweight-status rollout. They are
parsed and validated now; collection, caching, and request delivery are wired in
subsequent layers. Publication behavior remains unchanged, with `full` as the
default until final activation. Changing these settings requires a pod restart.
Routine node publications contain overview metadata and aggregate counts, not
peer lists, route entries, or BPF maps. Detailed data is collected for an explicit
single-node request and kept temporarily. Changing these settings requires a
restart of the affected controller or node-agent pods.

| Runtime setting | CLI override | Current default | Allowed values |
| Runtime setting | CLI override | Default | Allowed values |
|-----------------|--------------|-----------------|----------------|
| `node.statusDetailMode` | `--status-detail-mode` | `full` | `summary`, `full` |
| `node.statusDetailMode` | `--status-detail-mode` | `summary` | `summary`, `full` |
| `controller.statusDetailCacheTTL` | `--status-detail-cache-ttl` | `300s` | Strictly positive duration |
| `controller.statusDetailRequestTimeout` | `--status-detail-request-timeout` | `120s` | Strictly positive duration |

The intended cache lifetime starts when actual details arrive, not on summary
updates or reads. The request timeout covers all delivery attempts together.
Upgrade controllers before enabling summary publication in the completed rollout.
The cache lifetime starts when actual details arrive, not on summary updates or
reads. Continuous legacy full publications refresh it; on-demand duplicate or
late replies do not. The request timeout covers all delivery attempts together.

Upgrade controllers and their dashboard/CLI consumers before restarting agents
in summary mode. An incompatible controller is reported explicitly; agents do
not silently resume full streaming. For node-publishing rollback, set
`node.statusDetailMode: full` and restart agents. This restores full/delta
publication, not detailed bulk exports or the retired connectivity views.

Bulk APIs and global broadcasts remain summary-only in either mode. With no
active capable node WebSocket, an explicit request tries HTTP pull first,
regardless of the background-pull toggle. Failed pulls leave a command for the
next authenticated status POST response. Both publishers may remain disabled:
local HTTP diagnostics still work, but a failed pull has no polling fallback
until outbound POST publication resumes.

Requests and details are leader-local and in memory. Restart or leadership
change may require an explicit retry. TTL limits retention duration, not peak
memory during a burst of requests; releasing references permits garbage
collection but does not guarantee an immediate RSS decrease.

### Runtime config structure

Expand All @@ -53,6 +71,8 @@ controller:
statusStaleThreshold: 40s
statusWebsocketKeepaliveInterval: 10s
statusWsKeepaliveFailureCount: 2
statusDetailCacheTTL: 300s
statusDetailRequestTimeout: 120s
registerAggregatedAPIServer: true
# Optional. Selects local OIDC validation for node service account JWTs.
# When unset, discover from the controller's mounted token and fall back to
Expand Down Expand Up @@ -97,6 +117,7 @@ node:
statusPushEnabled: true
statusPushURL: ""
statusPushDelta: true
statusDetailMode: summary
statusPushInterval: 10s
statusPushApiserverInterval: 30s
healthCheckPort: 9997
Expand Down
8 changes: 3 additions & 5 deletions internal/net/config/runtime_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,9 @@ func ParsePositiveDurationField(raw, fieldName string) (time.Duration, error) {
}

const (
StatusDetailModeSummary = "summary"
StatusDetailModeFull = "full"
// DefaultStatusDetailMode preserves legacy publication during preparatory rollout.
// Summary becomes the default only after collectors and consumers are wired.
DefaultStatusDetailMode = StatusDetailModeFull
StatusDetailModeSummary = "summary"
StatusDetailModeFull = "full"
DefaultStatusDetailMode = StatusDetailModeSummary
DefaultStatusDetailCacheTTL = 300 * time.Second
DefaultStatusDetailRequestTimeout = 120 * time.Second
)
Expand Down
4 changes: 2 additions & 2 deletions internal/net/config/status_detail_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import (
)

func TestStatusDetailMode(t *testing.T) {
if DefaultStatusDetailMode != "full" {
t.Fatal("preparatory default must preserve full publication")
if DefaultStatusDetailMode != "summary" {
t.Fatal("routine publication must default to summaries")
}

for _, mode := range []string{"summary", "full", "", "SUMMARY", "other", " full "} {
Expand Down
Loading