internal/cli/status.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package cli
import (
"bytes"
"flag"
"fmt"
"os"
"sort"
"text/tabwriter"
"time"
"git.j3s.sh/cascade/api"
)
type statusCommand struct {
flagAPIAddr string
}
func (c statusCommand) Usage() {
fmt.Printf(`usage: cascade status [flags]
high-level overview of the cascade cluster: member counts,
status breakdown, and agent reachability.
flags:
-api
address of the cascade http api to target (default = 127.0.0.1:8500)
`)
}
func (c *statusCommand) Init(args []string) {
flags := flag.NewFlagSet("", flag.ContinueOnError)
flags.Usage = c.Usage
flags.StringVar(&c.flagAPIAddr, "api", "", "")
if err := flags.Parse(args); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func RunStatus(args []string) {
c := statusCommand{}
c.Init(args)
cfg := api.DefaultConfig()
if c.flagAPIAddr != "" {
cfg.Address = c.flagAPIAddr
}
client, err := api.NewClient(cfg)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
agent := client.Agent()
start := time.Now()
members, err := agent.Members()
apiRTT := time.Since(start)
if err != nil {
fmt.Printf("error fetching members from %s: %s\n", cfg.Address, err)
os.Exit(1)
}
self, err := agent.SelfTyped()
nodeName := "<unknown>"
var dnsAddr, httpAddr, serfAddr string
if err == nil {
nodeName = self.Config.NodeName
dnsAddr = self.Config.DNSBindAddr
httpAddr = self.Config.HTTPBindAddr
serfAddr = self.Config.SerfBindAddr
}
statusCounts := map[string]int{}
tagKeyCounts := map[string]int{}
for _, m := range members {
statusCounts[m.StatusPretty()]++
for k := range m.Tags {
tagKeyCounts[k]++
}
}
total := len(members)
alive := statusCounts["alive"]
healthPct := 0.0
if total > 0 {
healthPct = 100 * float64(alive) / float64(total)
}
var b bytes.Buffer
tw := tabwriter.NewWriter(&b, 0, 2, 2, ' ', 0)
fmt.Fprintln(tw, "cluster")
fmt.Fprintf(tw, " agent\t%s\n", nodeName)
fmt.Fprintf(tw, " api\t%s\n", cfg.Address)
fmt.Fprintf(tw, " api rtt\t%s\n", apiRTT.Round(time.Microsecond))
fmt.Fprintf(tw, " members\t%d\n", total)
fmt.Fprintf(tw, " health\t%.1f%% alive (%d/%d)\n", healthPct, alive, total)
fmt.Fprintln(tw)
if dnsAddr != "" || httpAddr != "" || serfAddr != "" {
fmt.Fprintln(tw, "bind addrs")
fmt.Fprintf(tw, " dns\t%s\n", dnsAddr)
fmt.Fprintf(tw, " http\t%s\n", httpAddr)
fmt.Fprintf(tw, " serf\t%s\n", serfAddr)
fmt.Fprintln(tw)
}
fmt.Fprintln(tw, "status")
for _, s := range sortedKeys(statusCounts) {
fmt.Fprintf(tw, " %s\t%d\n", s, statusCounts[s])
}
if len(tagKeyCounts) > 0 {
fmt.Fprintln(tw)
fmt.Fprintln(tw, "tags")
for _, k := range sortedKeys(tagKeyCounts) {
fmt.Fprintf(tw, " %s\t%d nodes\n", k, tagKeyCounts[k])
}
}
if err := tw.Flush(); err != nil {
fmt.Printf("error flushing tabwriter: %s", err)
os.Exit(1)
}
fmt.Print(b.String())
if alive < total {
os.Exit(1)
}
}
func sortedKeys(m map[string]int) []string {
out := make([]string, 0, len(m))
for k := range m {
out = append(out, k)
}
sort.Strings(out)
return out
}