blob: 89d5b5eb2daf25730d43e92a9ccaabc3f0929452 [file] [log] [blame]
Serge Bazanski6f599512023-04-26 19:08:19 +02001package scruffy
2
3import (
4 "context"
5 "fmt"
6 "sort"
7 "strings"
8 "testing"
9
10 "github.com/prometheus/client_golang/prometheus"
11
12 "source.monogon.dev/cloud/bmaas/bmdb"
13 "source.monogon.dev/cloud/bmaas/bmdb/model"
14 "source.monogon.dev/cloud/lib/component"
15)
16
17func TestBMDBStats(t *testing.T) {
18 s := Server{
19 Config: Config{
20 BMDB: bmdb.BMDB{
21 Config: bmdb.Config{
22 Database: component.CockroachConfig{
23 InMemory: true,
24 },
25 },
26 },
27 },
28 }
29
30 registry := prometheus.NewRegistry()
31 runner := newBMDBStatsRunner(&s, registry)
32
33 ctx, ctxC := context.WithCancel(context.Background())
34 defer ctxC()
35
36 expect := func(wantValues map[string]int64) {
37 t.Helper()
38 res, err := registry.Gather()
39 if err != nil {
40 t.Fatalf("Gather: %v", err)
41 }
42 gotValues := make(map[string]bool)
43 for _, mf := range res {
44 if len(mf.Metric) != 1 {
45 for _, m := range mf.Metric {
46 var lvs []string
47 for _, lp := range m.Label {
48 lvs = append(lvs, fmt.Sprintf("%s=%s", *lp.Name, *lp.Value))
49 }
50 sort.Strings(lvs)
51 name := fmt.Sprintf("%s[%s]", *mf.Name, strings.Join(lvs, ","))
52 gotValues[name] = true
53 if _, ok := wantValues[name]; !ok {
54 t.Errorf("MetricFamily %s: unexpected", name)
55 }
56 if want, got := wantValues[name], int64(*m.Gauge.Value); want != got {
57 t.Errorf("MetricFamily %s: wanted %d, got %d", *mf.Name, want, got)
58 }
59 }
60 } else {
61 m := mf.Metric[0]
62 gotValues[*mf.Name] = true
63 if want, got := wantValues[*mf.Name], int64(*m.Gauge.Value); want != got {
64 t.Errorf("MetricFamily %s: wanted %d, got %d", *mf.Name, want, got)
65 }
66 if _, ok := wantValues[*mf.Name]; !ok {
67 t.Errorf("MetricFamily %s: unexpected", *mf.Name)
68 }
69 }
70 }
71 for mf, _ := range wantValues {
72 if !gotValues[mf] {
73 t.Errorf("MetricFamily %s: missing", mf)
74 }
75 }
76 }
77
78 expect(map[string]int64{
79 "bmdb_stats_machines": 0,
80 "bmdb_stats_machines_provided": 0,
81 "bmdb_stats_machines_heartbeating": 0,
82 "bmdb_stats_machines_pending_installation": 0,
83 "bmdb_stats_machines_installed": 0,
84 "bmdb_stats_machines_pending_agent_start": 0,
85 "bmdb_stats_machines_pending_agent_recovery": 0,
86 "bmdb_stats_active_backoffs[process=ShepherdAccess]": 0,
87 "bmdb_stats_active_backoffs[process=ShepherdAgentStart]": 0,
88 "bmdb_stats_active_backoffs[process=ShepherdRecovery]": 0,
89 "bmdb_stats_active_work[process=ShepherdAccess]": 0,
90 "bmdb_stats_active_work[process=ShepherdAgentStart]": 0,
91 "bmdb_stats_active_work[process=ShepherdRecovery]": 0,
92 })
93
94 conn, err := s.Config.BMDB.Open(true)
95 if err != nil {
96 t.Fatalf("Open: %v", err)
97 }
98 sess, err := conn.StartSession(ctx)
99 if err != nil {
100 t.Fatalf("StartSession: %v", err)
101 }
102
103 s.bmdb = conn
104 s.sessionC = make(chan *bmdb.Session)
105 go s.sessionWorker(ctx)
106 if err := runner.runOnce(ctx); err != nil {
107 t.Fatal(err)
108 }
109
110 expect(map[string]int64{
111 "bmdb_stats_machines": 0,
112 "bmdb_stats_machines_provided": 0,
113 "bmdb_stats_machines_heartbeating": 0,
114 "bmdb_stats_machines_pending_installation": 0,
115 "bmdb_stats_machines_installed": 0,
116 "bmdb_stats_machines_pending_agent_start": 0,
117 "bmdb_stats_machines_pending_agent_recovery": 0,
118 "bmdb_stats_active_backoffs[process=ShepherdAccess]": 0,
119 "bmdb_stats_active_backoffs[process=ShepherdAgentStart]": 0,
120 "bmdb_stats_active_backoffs[process=ShepherdRecovery]": 0,
121 "bmdb_stats_active_work[process=ShepherdAccess]": 0,
122 "bmdb_stats_active_work[process=ShepherdAgentStart]": 0,
123 "bmdb_stats_active_work[process=ShepherdRecovery]": 0,
124 })
125
126 f := fill().
127 // Provided, needs installation.
128 machine().providedE("1").build().
129 // Three machines needing recovery.
130 machine().providedE("2").agentNeverHeartbeat().build().
131 machine().providedE("3").agentNeverHeartbeat().build().
132 machine().providedE("4").agentNeverHeartbeat().build().
133 // One machine correctly heartbeating.
134 machine().providedE("5").agentHealthy().build().
135 // Two machines heartbeating and pending installation.
136 machine().providedE("6").agentHealthy().installRequested(10).build().
137 machine().providedE("7").agentHealthy().installRequested(10).installReported(9).build().
138 // Machine which is pending installation _and_ recovery.
139 machine().providedE("8").agentNeverHeartbeat().installRequested(10).build().
140 // Machine which has been successfully installed.
141 machine().providedE("9").agentStoppedHeartbeating().installRequested(10).installReported(10).build()
142
143 err = sess.Transact(ctx, func(q *model.Queries) error {
144 return f(ctx, q)
145 })
146 if err != nil {
147 t.Fatal(err)
148 }
149
150 if err := runner.runOnce(ctx); err != nil {
151 t.Fatal(err)
152 }
153
154 expect(map[string]int64{
155 "bmdb_stats_machines": 9,
156 "bmdb_stats_machines_provided": 9,
157 "bmdb_stats_machines_heartbeating": 3,
158 "bmdb_stats_machines_pending_installation": 3,
159 "bmdb_stats_machines_installed": 1,
160 "bmdb_stats_machines_pending_agent_start": 1,
161 "bmdb_stats_machines_pending_agent_recovery": 4,
162 "bmdb_stats_active_backoffs[process=ShepherdAccess]": 0,
163 "bmdb_stats_active_backoffs[process=ShepherdAgentStart]": 0,
164 "bmdb_stats_active_backoffs[process=ShepherdRecovery]": 0,
165 "bmdb_stats_active_work[process=ShepherdAccess]": 0,
166 "bmdb_stats_active_work[process=ShepherdAgentStart]": 0,
167 "bmdb_stats_active_work[process=ShepherdRecovery]": 0,
168 })
169}