mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-12 17:59:06 +02:00
[client] Classify the daemon's refusals in the GUI (review item 3)
FailedPrecondition reached the classifier unmatched, so a refusal showed as "Operation failed". It is the code both of the daemon's deliberate refusals carry: the update-settings kill switch, and a field an MDM policy manages. Both are now named — settings_locked and settings_managed_by_mdm, matched on the message the daemon composes — and FailedPrecondition itself falls back to change_refused, so a refusal the daemon grows later still reads as a refusal rather than a failure. Only the English strings are added. Bundle.Translate falls back to the default language for a missing key, so other locales show English until the usual translation pass, rather than the bare "error.<code>" the classifier would otherwise surface. Note: the package needs GTK4/WebKit to build, which this machine has not, so the test is type-checked (go vet, GOOS=windows) but was not executed locally; CI's Linux job runs it.
This commit is contained in:
@@ -1795,6 +1795,18 @@
|
||||
"message": "The NetBird daemon is not responding. Please check that the service is running.",
|
||||
"description": "Error: the NetBird background service isn't responding. 'daemon' = the background service."
|
||||
},
|
||||
"error.settings_locked": {
|
||||
"message": "Settings cannot be changed on this device: an administrator has locked them.",
|
||||
"description": "Error: the local daemon was started with update-settings disabled, so it refuses configuration changes."
|
||||
},
|
||||
"error.settings_managed_by_mdm": {
|
||||
"message": "This setting is managed by your organization and cannot be changed.",
|
||||
"description": "Error: the setting is enforced by an MDM policy. 'MDM' = mobile device management, the organization's device-management system."
|
||||
},
|
||||
"error.change_refused": {
|
||||
"message": "The NetBird service refused this change.",
|
||||
"description": "Error: the daemon answered the request and declined it. Generic fallback for a refusal with no more specific cause."
|
||||
},
|
||||
"error.unknown": {
|
||||
"message": "Operation failed.",
|
||||
"description": "Generic fallback error message used when no specific error applies."
|
||||
|
||||
@@ -134,6 +134,10 @@ func (c errorClassifier) classify(err error) *ClientError {
|
||||
strings.Contains(lower, "connection refused"),
|
||||
strings.Contains(lower, "context deadline exceeded"):
|
||||
code = "daemon_unreachable"
|
||||
case strings.Contains(lower, "update settings are disabled"):
|
||||
code = "settings_locked"
|
||||
case strings.Contains(lower, "managed by mdm"):
|
||||
code = "settings_managed_by_mdm"
|
||||
}
|
||||
|
||||
// Fall back to the gRPC status code when the message didn't match a known
|
||||
@@ -145,6 +149,11 @@ func (c errorClassifier) classify(err error) *ClientError {
|
||||
code = "permission_denied"
|
||||
case gcodes.Unavailable, gcodes.DeadlineExceeded:
|
||||
code = "daemon_unreachable"
|
||||
case gcodes.FailedPrecondition:
|
||||
// The daemon answered and refused. The two refusals it composes
|
||||
// are matched above; anything else that reaches here is still a
|
||||
// refusal, so say that rather than "operation failed".
|
||||
code = "change_refused"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,28 @@ func TestErrorClassifier_Classify(t *testing.T) {
|
||||
require.Equal(t, "session_expired", ce.Code)
|
||||
})
|
||||
|
||||
t.Run("the update-settings kill switch is a refusal, not a failure", func(t *testing.T) {
|
||||
err := gstatus.Error(gcodes.FailedPrecondition,
|
||||
"update settings are disabled, you cannot use this feature without update settings enabled")
|
||||
|
||||
ce := c.classify(err)
|
||||
require.NotNil(t, ce)
|
||||
require.Equal(t, "settings_locked", ce.Code)
|
||||
})
|
||||
|
||||
t.Run("an MDM-managed field is named as such", func(t *testing.T) {
|
||||
err := gstatus.Error(gcodes.FailedPrecondition,
|
||||
"fields managed by MDM cannot be modified: [managementURL]")
|
||||
|
||||
require.Equal(t, "settings_managed_by_mdm", c.classify(err).Code)
|
||||
})
|
||||
|
||||
t.Run("any other refusal is still a refusal", func(t *testing.T) {
|
||||
// FailedPrecondition means the daemon answered and declined; falling
|
||||
// through to "unknown" showed "Operation failed" instead.
|
||||
require.Equal(t, "change_refused", c.classify(gstatus.Error(gcodes.FailedPrecondition, "something else")).Code)
|
||||
})
|
||||
|
||||
t.Run("unavailable code maps to daemon_unreachable", func(t *testing.T) {
|
||||
ce := c.classify(gstatus.Error(gcodes.Unavailable, "transport closing"))
|
||||
require.Equal(t, "daemon_unreachable", ce.Code)
|
||||
|
||||
Reference in New Issue
Block a user