Skip to content

[management, client] Add management-controlled client metrics push#5886

Open
pappz wants to merge 4 commits intomainfrom
feature/metrics-push-management-control
Open

[management, client] Add management-controlled client metrics push#5886
pappz wants to merge 4 commits intomainfrom
feature/metrics-push-management-control

Conversation

@pappz
Copy link
Copy Markdown
Collaborator

@pappz pappz commented Apr 14, 2026

Describe your changes

Allow enabling/disabling client metrics push from the dashboard via account settings instead of requiring env vars on every client.

  • Add MetricsConfig proto message to NetbirdConfig
  • Add MetricsPushEnabled to account Settings (DB-persisted)
  • Expose metrics_push_enabled in OpenAPI and dashboard API handler
  • Populate MetricsConfig in sync and login responses
  • Client dynamically starts/stops push based on management config
  • NB_METRICS_PUSH_ENABLED env var overrides management when explicitly set
  • Add activity events for metrics push enable/disable

netbirdio/dashboard#613
netbirdio/docs#701

Issue ticket number and link

Stack

Checklist

  • Is it a bug fix
  • Is a typo/documentation fix
  • Is a feature enhancement
  • It is a refactor
  • Created tests that fail without the change (if possible)

By submitting this pull request, you confirm that you have read and agree to the terms of the Contributor License Agreement.

Documentation

Select exactly one:

  • I added/updated documentation for this change
  • Documentation is not needed for this change (explain why)

Docs PR URL (required if "docs added" is checked)

Paste the PR link from https://github.com/netbirdio/docs here:

https://github.com/netbirdio/docs/pull/__

Summary by CodeRabbit

Release Notes

  • New Features
    • Introduced account-level metrics push setting enabling administrators to control whether client metrics are collected and pushed for all account peers.
    • Management server now distributes metrics push configuration to connected clients based on account settings.
    • Account activity logs track metrics push setting changes for audit and troubleshooting purposes.

Allow enabling/disabling client metrics push from the dashboard via
account settings instead of requiring env vars on every client.

- Add MetricsConfig proto message to NetbirdConfig
- Add MetricsPushEnabled to account Settings (DB-persisted)
- Expose metrics_push_enabled in OpenAPI and dashboard API handler
- Populate MetricsConfig in sync and login responses
- Client dynamically starts/stops push based on management config
- NB_METRICS_PUSH_ENABLED env var overrides management when explicitly set
- Add activity events for metrics push enable/disable
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 14, 2026

📝 Walkthrough

Walkthrough

This PR implements account-level metrics push configuration. The management server persists a MetricsPushEnabled setting, exposes it through HTTP API and gRPC interfaces, and communicates it to clients via NetbirdConfig.Metrics. The client receives this configuration during login and sync operations, updating metrics push behavior via a new UpdatePushFromMgm() method that respects environment variable overrides.

Changes

Cohort / File(s) Summary
Backend Account Settings
management/server/types/settings.go, management/server/account.go, management/server/activity/codes.go
Added persisted MetricsPushEnabled setting to account configuration, integrated change detection and event tracking for enable/disable transitions.
gRPC Proto and Conversion
shared/management/proto/management.proto, management/internals/shared/grpc/conversion.go, management/internals/shared/grpc/server.go
Introduced MetricsConfig message containing enabled field; updated toNetbirdConfig to conditionally populate metrics from settings and pass settings parameter through login/sync response flows.
HTTP API and Schema
shared/management/http/api/openapi.yml, shared/management/http/api/types.gen.go, management/server/http/handlers/accounts/accounts_handler.go, management/server/http/handlers/accounts/accounts_handler_test.go
Added metrics_push_enabled field to OpenAPI schema and generated types; updated account request/response handling and test expectations to include the new setting.
Client Metrics Control
client/internal/metrics/metrics.go, client/internal/metrics/env.go
Refactored push lifecycle with separate locked helpers; added UpdatePushFromMgm() to enable/disable based on management config while ignoring updates if NB_METRICS_PUSH_ENABLED environment variable is set; added StopPush() public method and environment variable detection utility.
Client Config Reception
client/internal/connect.go, client/internal/engine.go
Integrated metrics config handling: connect.go calls UpdatePushFromMgm() after successful login; engine.go processes sync-delivered metrics updates via new handleMetricsUpdate() helper.
Test Updates
management/server/peer_test.go
Updated test assertion to expect non-nil NetbirdConfig with nil sub-fields in peer update scenarios.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant Mgmt as Management Server
    participant Metrics as ClientMetrics
    Client->>Mgmt: Login Request
    Mgmt->>Client: LoginResponse + NetbirdConfig.Metrics
    Client->>Metrics: UpdatePushFromMgm(enabled)
    alt enabled && push not running
        Metrics->>Metrics: startPushLocked()
    else disabled && push running
        Metrics->>Metrics: stopPushLocked()
    end
Loading
sequenceDiagram
    participant Engine as Engine
    participant Mgmt as Management Server
    participant Metrics as ClientMetrics
    Engine->>Mgmt: Request Sync Update
    Mgmt->>Engine: SyncResponse + NetbirdConfig.Metrics
    Engine->>Engine: handleMetricsUpdate(config)
    Engine->>Metrics: UpdatePushFromMgm(config.Enabled)
    alt enabled && push not running
        Metrics->>Metrics: startPushLocked()
    else disabled && push running
        Metrics->>Metrics: stopPushLocked()
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

Suggested reviewers

  • pascal-fischer
  • mlsmaycon

Poem

🐰 Metrics now flow where management decides,
Settings cascade through the gRPC tides,
Clients receive the push-enable call,
Unless env vars override it all! ✨📊

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and accurately describes the main feature: management-controlled client metrics push, covering both management and client components.
Description check ✅ Passed The description comprehensively covers the changes, includes issue tracking links, selects the correct checklist item (feature enhancement), confirms documentation updates, and provides the docs PR URL.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/metrics-push-management-control

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

pappz added 3 commits April 14, 2026 17:22
Update TestUpdateAccountPeers assertions: NetbirdConfig is no longer
nil in peer update responses since it now carries MetricsConfig even
when STUN/TURN config is absent.
@sonarqubecloud
Copy link
Copy Markdown

@pappz pappz marked this pull request as ready for review April 14, 2026 16:09
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
management/server/types/settings.go (1)

82-103: ⚠️ Potential issue | 🟡 Minor

Clone JWTAllowGroups in Copy() too.

The copy still shares the original backing array for JWTAllowGroups, so mutating the clone can leak back into the source settings. PeerExposeGroups is already cloned; this slice should be handled the same way.

♻️ Proposed fix
 	settings := &Settings{
 		PeerLoginExpirationEnabled: s.PeerLoginExpirationEnabled,
 		PeerLoginExpiration:        s.PeerLoginExpiration,
 		JWTGroupsEnabled:           s.JWTGroupsEnabled,
 		JWTGroupsClaimName:         s.JWTGroupsClaimName,
 		GroupsPropagationEnabled:   s.GroupsPropagationEnabled,
-		JWTAllowGroups:             s.JWTAllowGroups,
+		JWTAllowGroups:             slices.Clone(s.JWTAllowGroups),
 		RegularUsersViewBlocked:    s.RegularUsersViewBlocked,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@management/server/types/settings.go` around lines 82 - 103, The copied
Settings still shares the backing slice for JWTAllowGroups, so update the
Settings copy logic (where settings := &Settings{...} in the Settings.Copy()
routine) to clone JWTAllowGroups the same way PeerExposeGroups is cloned (use
slices.Clone or equivalent) to avoid mutations of the copy affecting the
original; reference the JWTAllowGroups and PeerExposeGroups fields and the
Settings.Copy() creation block when making the change.
🧹 Nitpick comments (3)
management/server/peer_test.go (1)

1065-1069: Assert NetbirdConfig.Metrics explicitly to lock in the new contract.

This validates NetbirdConfig presence, but it won’t catch regressions where Metrics is missing or mismatched. Add a direct assertion for Metrics.Enabled.

Suggested test hardening
 			for _, channel := range peerChannels {
 				update := <-channel
-				assert.NotNil(t, update.Update.NetbirdConfig)
+				require.NotNil(t, update.Update)
+				require.NotNil(t, update.Update.NetbirdConfig)
+				require.NotNil(t, update.Update.NetbirdConfig.Metrics)
+				require.NotNil(t, account.Settings)
+				assert.Equal(t, account.Settings.MetricsPushEnabled, update.Update.NetbirdConfig.Metrics.Enabled)
 				assert.Nil(t, update.Update.NetbirdConfig.Stuns)
 				assert.Nil(t, update.Update.NetbirdConfig.Turns)
 				assert.Nil(t, update.Update.NetbirdConfig.Signal)
 				assert.Nil(t, update.Update.NetbirdConfig.Relay)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@management/server/peer_test.go` around lines 1065 - 1069, The test currently
checks NetbirdConfig presence and several nil fields but doesn't assert Metrics;
add an explicit assertion that update.Update.NetbirdConfig.Metrics is not nil
and that update.Update.NetbirdConfig.Metrics.Enabled (or the boolean field name
used) equals the expected value to lock in the new contract; update the test in
peer_test.go around the existing assertions for update.Update.NetbirdConfig to
include checks for Metrics and Metrics.Enabled to catch regressions.
management/server/http/handlers/accounts/accounts_handler_test.go (1)

107-286: Add one round-trip case with metrics_push_enabled=true.

All new assertions only cover the default false value, so these tests still pass if the handler silently drops the incoming field. A PUT case that sets metrics_push_enabled: true and asserts it in the response would exercise the new mapping end to end.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@management/server/http/handlers/accounts/accounts_handler_test.go` around
lines 107 - 286, Add a PUT test case that round-trips metrics_push_enabled=true:
in the test cases slice used by the accounts handler tests add a new entry
(similar to the "PutAccount OK" cases) with requestType http.MethodPut,
requestPath "/api/accounts/"+accountID, requestBody containing {"settings":
{"metrics_push_enabled": true, ...}} and expectedStatus http.StatusOK, and set
expectedSettings.MetricsPushEnabled to br(true) so the assertion on the response
verifies the handler preserves the metrics_push_enabled field; locate the table
by the fields requestBody and expectedSettings to add this new case next to the
other PutAccount cases.
client/internal/engine.go (1)

969-975: Lower this to debug or log only on transitions.

handleSync can run often, so logging every received metrics config at info level will get noisy quickly. Debugf, or logging only when UpdatePushFromMgm actually changes state, would keep routine syncs out of normal logs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/internal/engine.go` around lines 969 - 975, Change the noisy
Info-level log in handleMetricsUpdate to a Debug-level log or make it
conditional on an actual state transition: before calling
e.clientMetrics.UpdatePushFromMgm(ctx, enabled) read the current push state (via
whatever getter exists on clientMetrics) and call UpdatePushFromMgm only if the
enabled value differs; if you keep the call, change log.Infof to log.Debugf
and/or log only when the pre- and post-update states differ so UpdatePushFromMgm
is the reference point for whether to emit a log message.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@management/server/types/settings.go`:
- Around line 82-103: The copied Settings still shares the backing slice for
JWTAllowGroups, so update the Settings copy logic (where settings :=
&Settings{...} in the Settings.Copy() routine) to clone JWTAllowGroups the same
way PeerExposeGroups is cloned (use slices.Clone or equivalent) to avoid
mutations of the copy affecting the original; reference the JWTAllowGroups and
PeerExposeGroups fields and the Settings.Copy() creation block when making the
change.

---

Nitpick comments:
In `@client/internal/engine.go`:
- Around line 969-975: Change the noisy Info-level log in handleMetricsUpdate to
a Debug-level log or make it conditional on an actual state transition: before
calling e.clientMetrics.UpdatePushFromMgm(ctx, enabled) read the current push
state (via whatever getter exists on clientMetrics) and call UpdatePushFromMgm
only if the enabled value differs; if you keep the call, change log.Infof to
log.Debugf and/or log only when the pre- and post-update states differ so
UpdatePushFromMgm is the reference point for whether to emit a log message.

In `@management/server/http/handlers/accounts/accounts_handler_test.go`:
- Around line 107-286: Add a PUT test case that round-trips
metrics_push_enabled=true: in the test cases slice used by the accounts handler
tests add a new entry (similar to the "PutAccount OK" cases) with requestType
http.MethodPut, requestPath "/api/accounts/"+accountID, requestBody containing
{"settings": {"metrics_push_enabled": true, ...}} and expectedStatus
http.StatusOK, and set expectedSettings.MetricsPushEnabled to br(true) so the
assertion on the response verifies the handler preserves the
metrics_push_enabled field; locate the table by the fields requestBody and
expectedSettings to add this new case next to the other PutAccount cases.

In `@management/server/peer_test.go`:
- Around line 1065-1069: The test currently checks NetbirdConfig presence and
several nil fields but doesn't assert Metrics; add an explicit assertion that
update.Update.NetbirdConfig.Metrics is not nil and that
update.Update.NetbirdConfig.Metrics.Enabled (or the boolean field name used)
equals the expected value to lock in the new contract; update the test in
peer_test.go around the existing assertions for update.Update.NetbirdConfig to
include checks for Metrics and Metrics.Enabled to catch regressions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dfc3ef48-5242-4457-bf77-82b5a74dafad

📥 Commits

Reviewing files that changed from the base of the PR and between e804a70 and 55177da.

⛔ Files ignored due to path filters (1)
  • shared/management/proto/management.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (15)
  • client/internal/connect.go
  • client/internal/engine.go
  • client/internal/metrics/env.go
  • client/internal/metrics/metrics.go
  • management/internals/shared/grpc/conversion.go
  • management/internals/shared/grpc/server.go
  • management/server/account.go
  • management/server/activity/codes.go
  • management/server/http/handlers/accounts/accounts_handler.go
  • management/server/http/handlers/accounts/accounts_handler_test.go
  • management/server/peer_test.go
  • management/server/types/settings.go
  • shared/management/http/api/openapi.yml
  • shared/management/http/api/types.gen.go
  • shared/management/proto/management.proto

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.

3 participants