| package scruffy |
| |
| import ( |
| "context" |
| "fmt" |
| "sort" |
| "strings" |
| "testing" |
| |
| "github.com/prometheus/client_golang/prometheus" |
| |
| "source.monogon.dev/cloud/bmaas/bmdb" |
| "source.monogon.dev/cloud/bmaas/bmdb/model" |
| "source.monogon.dev/cloud/lib/component" |
| ) |
| |
| func TestBMDBStats(t *testing.T) { |
| s := Server{ |
| Config: Config{ |
| BMDB: bmdb.BMDB{ |
| Config: bmdb.Config{ |
| Database: component.CockroachConfig{ |
| InMemory: true, |
| }, |
| }, |
| }, |
| }, |
| } |
| |
| registry := prometheus.NewRegistry() |
| runner := newBMDBStatsRunner(&s, registry) |
| |
| ctx, ctxC := context.WithCancel(context.Background()) |
| defer ctxC() |
| |
| expect := func(wantValues map[string]int64) { |
| t.Helper() |
| res, err := registry.Gather() |
| if err != nil { |
| t.Fatalf("Gather: %v", err) |
| } |
| gotValues := make(map[string]bool) |
| for _, mf := range res { |
| if len(mf.Metric) != 1 { |
| for _, m := range mf.Metric { |
| var lvs []string |
| for _, lp := range m.Label { |
| lvs = append(lvs, fmt.Sprintf("%s=%s", *lp.Name, *lp.Value)) |
| } |
| sort.Strings(lvs) |
| name := fmt.Sprintf("%s[%s]", *mf.Name, strings.Join(lvs, ",")) |
| gotValues[name] = true |
| if _, ok := wantValues[name]; !ok { |
| t.Errorf("MetricFamily %s: unexpected", name) |
| } |
| if want, got := wantValues[name], int64(*m.Gauge.Value); want != got { |
| t.Errorf("MetricFamily %s: wanted %d, got %d", *mf.Name, want, got) |
| } |
| } |
| } else { |
| m := mf.Metric[0] |
| gotValues[*mf.Name] = true |
| if want, got := wantValues[*mf.Name], int64(*m.Gauge.Value); want != got { |
| t.Errorf("MetricFamily %s: wanted %d, got %d", *mf.Name, want, got) |
| } |
| if _, ok := wantValues[*mf.Name]; !ok { |
| t.Errorf("MetricFamily %s: unexpected", *mf.Name) |
| } |
| } |
| } |
| for mf, _ := range wantValues { |
| if !gotValues[mf] { |
| t.Errorf("MetricFamily %s: missing", mf) |
| } |
| } |
| } |
| |
| expect(map[string]int64{ |
| "bmdb_stats_machines": 0, |
| "bmdb_stats_machines_provided": 0, |
| "bmdb_stats_machines_heartbeating": 0, |
| "bmdb_stats_machines_pending_installation": 0, |
| "bmdb_stats_machines_installed": 0, |
| "bmdb_stats_machines_pending_agent_start": 0, |
| "bmdb_stats_machines_pending_agent_recovery": 0, |
| "bmdb_stats_active_backoffs[process=ShepherdAccess]": 0, |
| "bmdb_stats_active_backoffs[process=ShepherdAgentStart]": 0, |
| "bmdb_stats_active_backoffs[process=ShepherdRecovery]": 0, |
| "bmdb_stats_active_work[process=ShepherdAccess]": 0, |
| "bmdb_stats_active_work[process=ShepherdAgentStart]": 0, |
| "bmdb_stats_active_work[process=ShepherdRecovery]": 0, |
| }) |
| |
| conn, err := s.Config.BMDB.Open(true) |
| if err != nil { |
| t.Fatalf("Open: %v", err) |
| } |
| sess, err := conn.StartSession(ctx) |
| if err != nil { |
| t.Fatalf("StartSession: %v", err) |
| } |
| |
| s.bmdb = conn |
| s.sessionC = make(chan *bmdb.Session) |
| go s.sessionWorker(ctx) |
| if err := runner.runOnce(ctx); err != nil { |
| t.Fatal(err) |
| } |
| |
| expect(map[string]int64{ |
| "bmdb_stats_machines": 0, |
| "bmdb_stats_machines_provided": 0, |
| "bmdb_stats_machines_heartbeating": 0, |
| "bmdb_stats_machines_pending_installation": 0, |
| "bmdb_stats_machines_installed": 0, |
| "bmdb_stats_machines_pending_agent_start": 0, |
| "bmdb_stats_machines_pending_agent_recovery": 0, |
| "bmdb_stats_active_backoffs[process=ShepherdAccess]": 0, |
| "bmdb_stats_active_backoffs[process=ShepherdAgentStart]": 0, |
| "bmdb_stats_active_backoffs[process=ShepherdRecovery]": 0, |
| "bmdb_stats_active_work[process=ShepherdAccess]": 0, |
| "bmdb_stats_active_work[process=ShepherdAgentStart]": 0, |
| "bmdb_stats_active_work[process=ShepherdRecovery]": 0, |
| }) |
| |
| f := fill(). |
| // Provided, needs installation. |
| machine().providedE("1").build(). |
| // Three machines needing recovery. |
| machine().providedE("2").agentNeverHeartbeat().build(). |
| machine().providedE("3").agentNeverHeartbeat().build(). |
| machine().providedE("4").agentNeverHeartbeat().build(). |
| // One machine correctly heartbeating. |
| machine().providedE("5").agentHealthy().build(). |
| // Two machines heartbeating and pending installation. |
| machine().providedE("6").agentHealthy().installRequested(10).build(). |
| machine().providedE("7").agentHealthy().installRequested(10).installReported(9).build(). |
| // Machine which is pending installation _and_ recovery. |
| machine().providedE("8").agentNeverHeartbeat().installRequested(10).build(). |
| // Machine which has been successfully installed. |
| machine().providedE("9").agentStoppedHeartbeating().installRequested(10).installReported(10).build() |
| |
| err = sess.Transact(ctx, func(q *model.Queries) error { |
| return f(ctx, q) |
| }) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| if err := runner.runOnce(ctx); err != nil { |
| t.Fatal(err) |
| } |
| |
| expect(map[string]int64{ |
| "bmdb_stats_machines": 9, |
| "bmdb_stats_machines_provided": 9, |
| "bmdb_stats_machines_heartbeating": 3, |
| "bmdb_stats_machines_pending_installation": 3, |
| "bmdb_stats_machines_installed": 1, |
| "bmdb_stats_machines_pending_agent_start": 1, |
| "bmdb_stats_machines_pending_agent_recovery": 4, |
| "bmdb_stats_active_backoffs[process=ShepherdAccess]": 0, |
| "bmdb_stats_active_backoffs[process=ShepherdAgentStart]": 0, |
| "bmdb_stats_active_backoffs[process=ShepherdRecovery]": 0, |
| "bmdb_stats_active_work[process=ShepherdAccess]": 0, |
| "bmdb_stats_active_work[process=ShepherdAgentStart]": 0, |
| "bmdb_stats_active_work[process=ShepherdRecovery]": 0, |
| }) |
| } |