mirror of
https://github.com/binwiederhier/ntfy.git
synced 2026-11-10 22:50:59 +08:00
Merge pull request #2153 from binwiederhier/cluster-status-self
ntfy cluster status: this node as the table's first row
This commit is contained in:
4 files changed
+30
-15
No files matched your search
+9
-9
@@ -132,20 +132,17 @@ func clusterStatusBaseURL(listen string) (string, error) {
|
||||
}
|
||||
|
||||
func printClusterStatus(w io.Writer, status *cluster.Status) {
|
||||
leader, healthy := "not leader", "healthy"
|
||||
// This node first, then its peers; the link columns are the peers' only
|
||||
self, live := string(status.NodeID)+" (this node)", "yes"
|
||||
if status.Leader {
|
||||
leader = "leader"
|
||||
self = string(status.NodeID) + " (this node, leader)"
|
||||
}
|
||||
if !status.Healthy {
|
||||
healthy = "NOT healthy (registry heartbeat stale)"
|
||||
}
|
||||
fmt.Fprintf(w, "Node %s: %s, %s, %s\n\n", status.NodeID, status.Role, leader, healthy)
|
||||
if len(status.Peers) == 0 {
|
||||
fmt.Fprintln(w, "No peers.")
|
||||
return
|
||||
live = "no"
|
||||
}
|
||||
tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0)
|
||||
fmt.Fprintln(tw, "PEER\tROLE\tLIVE\tHEARTBEAT\tSENT OK\tFAILED\tQUEUED\tDROPPED\tLAST SENT\tRECEIVED\tLAST RECEIVED")
|
||||
fmt.Fprintln(tw, "NODE\tROLE\tLIVE\tHEARTBEAT\tSENT OK\tFAILED\tQUEUED\tDROPPED\tLAST SENT\tRECEIVED\tLAST RECEIVED")
|
||||
fmt.Fprintf(tw, "%s\t%s\t%s\t-\t-\t-\t-\t-\t-\t-\t-\n", self, status.Role, live)
|
||||
for _, p := range status.Peers {
|
||||
live, heartbeat := "no", "last seen "+clusterStatusAge(status.Time, p.LastSeen)
|
||||
if p.Live {
|
||||
@@ -156,6 +153,9 @@ func printClusterStatus(w io.Writer, status *cluster.Status) {
|
||||
}
|
||||
tw.Flush()
|
||||
problems := clusterStatusProblems(status, "")
|
||||
if !status.Healthy {
|
||||
problems = append([]string{fmt.Sprintf("%s (this node): not healthy, its registry heartbeat is stale", status.NodeID)}, problems...)
|
||||
}
|
||||
if len(problems) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -84,6 +84,21 @@ func TestCLI_Cluster_Status(t *testing.T) {
|
||||
require.Contains(t, out, "nyc3-app-1: no batch accepted") // The warning that matters
|
||||
}
|
||||
|
||||
func TestCLI_Cluster_Status_ThisNodeInTheList(t *testing.T) {
|
||||
// The node itself is the table's first row, not a line above it; a leader and an unhealthy node say so
|
||||
status := &cluster.Status{NodeID: "nyc3-app-1", Role: cluster.RoleActive, Leader: true, Healthy: false, Time: 1792330000, Peers: []*cluster.PeerStatus{{
|
||||
NodeID: "nyc3-app-2", Role: cluster.RolePassive, Live: true, HeartbeatAge: 1, Out: cluster.PeerOutbound{BatchesOK: 7, LastOK: 1792329999},
|
||||
}}}
|
||||
addr := newTestClusterStatusServerFor(t, func() *cluster.Status { return status })
|
||||
app, _, stdout, _ := newTestApp()
|
||||
require.Nil(t, app.Run([]string{"ntfy", "cluster", "--experimental-cluster-listen", addr, "--experimental-cluster-secret", "s3cret", "status"}))
|
||||
lines := strings.Split(strings.TrimSpace(stdout.String()), "\n")
|
||||
require.True(t, strings.HasPrefix(lines[0], "NODE "), "the table comes first:\n%s", stdout.String())
|
||||
require.Regexp(t, `^nyc3-app-1 \(this node, leader\)\s+active\s+no\s`, lines[1])
|
||||
require.Regexp(t, `^nyc3-app-2\s+passive\s+yes\s+1s ago\s+7\s`, lines[2])
|
||||
require.Contains(t, stdout.String(), "nyc3-app-1 (this node): not healthy, its registry heartbeat is stale")
|
||||
}
|
||||
|
||||
func TestCLI_Cluster_Status_JSON(t *testing.T) {
|
||||
addr := newTestClusterStatusServer(t)
|
||||
app, _, stdout, _ := newTestApp()
|
||||
|
||||
+5
-6
@@ -2376,15 +2376,14 @@ once.
|
||||
`/v1/health` only covers a node's own database heartbeat. A link between two nodes that works in one direction only,
|
||||
e.g. because a firewall drops the cluster port one way, keeps both nodes healthy while messages published on one of them
|
||||
reach the other only late, through the database. To see each node's view of its peers, run `ntfy cluster status` on the
|
||||
node. It reads `experimental-cluster-listen` and `experimental-cluster-secret` from the server config, and shows per
|
||||
peer the message batches sent there and received from there:
|
||||
node. It reads `experimental-cluster-listen` and `experimental-cluster-secret` from the server config, and lists the
|
||||
node itself (its role, and whether it leads), then per peer the message batches sent there and received from there:
|
||||
|
||||
```
|
||||
$ ntfy cluster status
|
||||
Node nyc3-app-2: passive, not leader, healthy
|
||||
|
||||
PEER ROLE LIVE HEARTBEAT SENT OK FAILED QUEUED DROPPED LAST SENT RECEIVED LAST RECEIVED
|
||||
nyc3-app-1 active yes 2s ago 0 219 0 0 never 5120 1s ago
|
||||
NODE ROLE LIVE HEARTBEAT SENT OK FAILED QUEUED DROPPED LAST SENT RECEIVED LAST RECEIVED
|
||||
nyc3-app-2 (this node) passive yes - - - - - - - -
|
||||
nyc3-app-1 active yes 2s ago 0 219 0 0 never 5120 1s ago
|
||||
|
||||
Problems:
|
||||
nyc3-app-1: no batch accepted, 219 failed; last error 10s ago: Post "http://10.108.0.2:2587/v1/cluster/message": context deadline exceeded
|
||||
|
||||
@@ -2356,6 +2356,7 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
|
||||
**Bug fixes + maintenance:**
|
||||
|
||||
* Do not send TCP keep-alive probes on the client listeners (`listen-http`, `listen-https`): Go sends one every 15 seconds on an idle connection by default, which, without a proxy in front of ntfy, woke phones holding a stream three times as often as ntfy's own keepalive event ([#2152](https://github.com/binwiederhier/ntfy/pull/2152))
|
||||
* `ntfy cluster status` lists the node itself as the first row of the table (marked "this node", and "leader" if it leads) instead of a line above it ([#2153](https://github.com/binwiederhier/ntfy/pull/2153))
|
||||
|
||||
### ntfy iOS app v1.8.0 (UNRELEASED)
|
||||
|
||||
|
||||
Reference in new issue
Block a user