feat: probe() — health check that separates auth rejection from unreachable
Some checks failed
Security / Security check (push) Failing after 2s
Some checks failed
Security / Security check (push) Failing after 2s
healthy() collapses every failure to false, so a hub that is up but rejects the bearer token was indistinguishable from a dead endpoint. probe() returns serving / notServing / authRejected (UNAUTHENTICATED or PERMISSION_DENIED) / unreachable so client UIs can point the operator at the token instead of the wire. healthy() is unchanged. Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
9141a91314
commit
ec3b684e6e
1 changed files with 40 additions and 0 deletions
|
|
@ -43,6 +43,26 @@ class HubEndpoint {
|
|||
String toString() => '${secure ? "https" : "http"}://$host:$port';
|
||||
}
|
||||
|
||||
/// Outcome of a [HubClient.probe] health check. Distinguishes
|
||||
/// "the hub is down" from "the hub is up but rejected our
|
||||
/// credentials" — the two need different operator guidance.
|
||||
enum HubProbeResult {
|
||||
/// Hub responded with SERVING.
|
||||
serving,
|
||||
|
||||
/// Hub responded, but not with SERVING (starting up or
|
||||
/// shutting down).
|
||||
notServing,
|
||||
|
||||
/// Hub is reachable but rejected the call as UNAUTHENTICATED
|
||||
/// or PERMISSION_DENIED — the endpoint is fine, the token
|
||||
/// is missing, wrong, or lacks scope.
|
||||
authRejected,
|
||||
|
||||
/// Connection-level failure (refused, timeout, TLS, DNS).
|
||||
unreachable,
|
||||
}
|
||||
|
||||
/// Re-exports of generated protobuf types so callers don't have
|
||||
/// to import the `generated/` directory directly.
|
||||
typedef CapabilityEntry = pb.CapabilityEntry;
|
||||
|
|
@ -150,6 +170,26 @@ class HubClient {
|
|||
}
|
||||
}
|
||||
|
||||
/// Like [healthy], but keeps enough of the failure to tell an
|
||||
/// auth rejection apart from a dead endpoint, so UIs can say
|
||||
/// "check your token" instead of a misleading "unreachable".
|
||||
Future<HubProbeResult> probe() async {
|
||||
try {
|
||||
final r = await _hub.health(Empty());
|
||||
return r.state == pb.HealthStatus_State.SERVING
|
||||
? HubProbeResult.serving
|
||||
: HubProbeResult.notServing;
|
||||
} on grpc.GrpcError catch (e) {
|
||||
if (e.code == grpc.StatusCode.unauthenticated ||
|
||||
e.code == grpc.StatusCode.permissionDenied) {
|
||||
return HubProbeResult.authRejected;
|
||||
}
|
||||
return HubProbeResult.unreachable;
|
||||
} catch (_) {
|
||||
return HubProbeResult.unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
/// All capabilities provided by installed modules.
|
||||
Future<List<CapabilityEntry>> listCapabilities() async {
|
||||
final r = await _admin.listCapabilities(Empty());
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue