Skip to content

Commit befb83f

Browse files
authored
PMM-13243: add cluster role label for mongodb_up (#884)
* add cluster role label for mongodb_up * resolve multi target tests * fix metric test * drop commented blocks * Improve tests for mongodb_up metric - Adds more tests to cover the different instances (standalone, mongos, replica sets). - share node type with general collector(removes the extra db call, and allows the node type to persist after disconnection). * replace if with switch * make tests parallel * resolve tests
1 parent 3c3819b commit befb83f

7 files changed

+125
-55
lines changed

exporter/diagnostic_data_collector_test.go

-1
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,6 @@ func TestDiagnosticDataErrors(t *testing.T) {
270270
}
271271

272272
for _, tc := range cases {
273-
tc := tc
274273
t.Run(tc.name, func(t *testing.T) {
275274
t.Parallel()
276275
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)

exporter/exporter.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -130,18 +130,18 @@ func (e *Exporter) getTotalCollectionsCount() int {
130130
func (e *Exporter) makeRegistry(ctx context.Context, client *mongo.Client, topologyInfo labelsGetter, requestOpts Opts) *prometheus.Registry {
131131
registry := prometheus.NewRegistry()
132132

133-
gc := newGeneralCollector(ctx, client, e.opts.Logger)
133+
nodeType, err := getNodeType(ctx, client)
134+
if err != nil {
135+
e.logger.Errorf("Registry - Cannot get node type to check if this is a mongos : %s", err)
136+
}
137+
138+
gc := newGeneralCollector(ctx, client, nodeType, e.opts.Logger)
134139
registry.MustRegister(gc)
135140

136141
if client == nil {
137142
return registry
138143
}
139144

140-
nodeType, err := getNodeType(ctx, client)
141-
if err != nil {
142-
e.logger.Errorf("Registry - Cannot get node type to check if this is a mongos : %s", err)
143-
}
144-
145145
// Enable collectors like collstats and indexstats depending on the number of collections
146146
// present in the database.
147147
limitsOk := false

exporter/exporter_test.go

+15-8
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,8 @@ func TestMongoUp(t *testing.T) {
214214
assert.Error(t, err)
215215

216216
e := New(exporterOpts)
217-
218-
gc := newGeneralCollector(ctx, client, e.opts.Logger)
217+
nodeType, _ := getNodeType(ctx, client)
218+
gc := newGeneralCollector(ctx, client, nodeType, e.opts.Logger)
219219

220220
r := e.makeRegistry(ctx, client, new(labelsGetterMock), *e.opts)
221221

@@ -227,13 +227,18 @@ func TestMongoUpMetric(t *testing.T) {
227227
ctx := context.Background()
228228

229229
type testcase struct {
230-
URI string
231-
Want int
230+
URI string
231+
clusterRole string
232+
Want int
232233
}
233234

234235
testCases := []testcase{
235236
{URI: "mongodb://127.0.0.1:12345/admin", Want: 0},
236-
{URI: fmt.Sprintf("mongodb://127.0.0.1:%s/admin", tu.GetenvDefault("TEST_MONGODB_STANDALONE_PORT", "27017")), Want: 1},
237+
{URI: fmt.Sprintf("mongodb://127.0.0.1:%s/admin", tu.GetenvDefault("TEST_MONGODB_STANDALONE_PORT", "27017")), Want: 1, clusterRole: "mongod"},
238+
{URI: fmt.Sprintf("mongodb://127.0.0.1:%s/admin", tu.GetenvDefault("TEST_MONGODB_S1_PRIMARY_PORT", "27017")), Want: 1, clusterRole: "mongod"},
239+
{URI: fmt.Sprintf("mongodb://127.0.0.1:%s/admin", tu.GetenvDefault("TEST_MONGODB_S1_SECONDARY1_PORT", "27017")), Want: 1, clusterRole: "mongod"},
240+
{URI: fmt.Sprintf("mongodb://127.0.0.1:%s/admin", tu.GetenvDefault("TEST_MONGODB_S1_ARBITER_PORT", "27017")), Want: 1, clusterRole: "arbiter"},
241+
{URI: fmt.Sprintf("mongodb://127.0.0.1:%s/admin", tu.GetenvDefault("TEST_MONGODB_MONGOS_PORT", "27017")), Want: 1, clusterRole: "mongos"},
237242
}
238243

239244
for _, tc := range testCases {
@@ -254,13 +259,15 @@ func TestMongoUpMetric(t *testing.T) {
254259
}
255260

256261
e := New(exporterOpts)
257-
gc := newGeneralCollector(ctx, client, e.opts.Logger)
262+
nodeType, _ := getNodeType(ctx, client)
263+
gc := newGeneralCollector(ctx, client, nodeType, e.opts.Logger)
258264
r := e.makeRegistry(ctx, client, new(labelsGetterMock), *e.opts)
259265

260-
expected := strings.NewReader(`
266+
expected := strings.NewReader(fmt.Sprintf(`
261267
# HELP mongodb_up Whether MongoDB is up.
262268
# TYPE mongodb_up gauge
263-
mongodb_up ` + strconv.Itoa(tc.Want) + "\n")
269+
mongodb_up {cluster_role="%s"} %s`, tc.clusterRole, strconv.Itoa(tc.Want)) + "\n")
270+
264271
filter := []string{
265272
"mongodb_up",
266273
}

exporter/general_collector.go

+23-11
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,19 @@ import (
2424
"go.mongodb.org/mongo-driver/mongo/readpref"
2525
)
2626

27-
// This collector is always enabled and it is not directly related to any particular MongoDB
28-
// command to gather stats.
27+
// This collector is always enabled and collects general MongoDB connectivity status.
2928
type generalCollector struct {
30-
ctx context.Context
31-
base *baseCollector
29+
ctx context.Context
30+
base *baseCollector
31+
nodeType mongoDBNodeType
3232
}
3333

3434
// newGeneralCollector creates a collector for MongoDB connectivity status.
35-
func newGeneralCollector(ctx context.Context, client *mongo.Client, logger *logrus.Logger) *generalCollector {
35+
func newGeneralCollector(ctx context.Context, client *mongo.Client, nodeType mongoDBNodeType, logger *logrus.Logger) *generalCollector {
3636
return &generalCollector{
37-
ctx: ctx,
38-
base: newBaseCollector(client, logger.WithFields(logrus.Fields{"collector": "general"})),
37+
ctx: ctx,
38+
nodeType: nodeType,
39+
base: newBaseCollector(client, logger.WithFields(logrus.Fields{"collector": "general"})),
3940
}
4041
}
4142

@@ -49,21 +50,32 @@ func (d *generalCollector) Collect(ch chan<- prometheus.Metric) {
4950

5051
func (d *generalCollector) collect(ch chan<- prometheus.Metric) {
5152
defer measureCollectTime(ch, "mongodb", "general")()
52-
ch <- mongodbUpMetric(d.ctx, d.base.client, d.base.logger)
53+
ch <- mongodbUpMetric(d.ctx, d.base.client, d.nodeType, d.base.logger)
5354
}
5455

55-
func mongodbUpMetric(ctx context.Context, client *mongo.Client, log *logrus.Entry) prometheus.Metric { //nolint:ireturn
56+
func mongodbUpMetric(ctx context.Context, client *mongo.Client, nodeType mongoDBNodeType, log *logrus.Entry) prometheus.Metric { //nolint:ireturn
5657
var value float64
58+
var clusterRole mongoDBNodeType
5759

5860
if client != nil {
5961
if err := client.Ping(ctx, readpref.PrimaryPreferred()); err == nil {
6062
value = 1
6163
} else {
62-
log.Errorf("error while checking mongodb connection: %s. mongo_up is set to 0", err)
64+
log.Errorf("error while checking mongodb connection: %s. mongo_up is set to 0", err.Error())
65+
}
66+
67+
switch nodeType { //nolint:exhaustive
68+
case typeMongos:
69+
clusterRole = typeMongos
70+
case typeArbiter:
71+
clusterRole = typeArbiter
72+
default:
73+
clusterRole = typeMongod
6374
}
6475
}
6576

66-
d := prometheus.NewDesc("mongodb_up", "Whether MongoDB is up.", nil, nil)
77+
labels := map[string]string{"cluster_role": string(clusterRole)}
78+
d := prometheus.NewDesc("mongodb_up", "Whether MongoDB is up.", nil, labels)
6779

6880
return prometheus.MustNewConstMetric(d, prometheus.GaugeValue, value)
6981
}

exporter/general_collector_test.go

+74-25
Original file line numberDiff line numberDiff line change
@@ -30,40 +30,89 @@ import (
3030
)
3131

3232
func TestGeneralCollector(t *testing.T) {
33-
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
34-
defer cancel()
33+
t.Parallel()
34+
t.Run("mongod cluster role", func(t *testing.T) {
35+
t.Parallel()
36+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
37+
defer cancel()
3538

36-
client := tu.DefaultTestClient(ctx, t)
37-
c := newGeneralCollector(ctx, client, logrus.New())
39+
client := tu.DefaultTestClient(ctx, t)
40+
nodeType, _ := getNodeType(ctx, client)
41+
c := newGeneralCollector(ctx, client, nodeType, logrus.New())
3842

39-
filter := []string{
40-
"collector_scrape_time_ms",
41-
}
42-
count := testutil.CollectAndCount(c, filter...)
43-
assert.Equal(t, len(filter), count, "Meta-metric for collector is missing")
43+
filter := []string{
44+
"collector_scrape_time_ms",
45+
}
46+
count := testutil.CollectAndCount(c, filter...)
47+
assert.Equal(t, len(filter), count, "Meta-metric for collector is missing")
4448

45-
// The last \n at the end of this string is important
46-
expected := strings.NewReader(`
49+
// The last \n at the end of this string is important
50+
expected := strings.NewReader(`
4751
# HELP mongodb_up Whether MongoDB is up.
4852
# TYPE mongodb_up gauge
49-
mongodb_up 1
53+
mongodb_up {cluster_role="mongod"} 1
5054
` + "\n")
51-
filter = []string{
52-
"mongodb_up",
53-
}
54-
err := testutil.CollectAndCompare(c, expected, filter...)
55-
require.NoError(t, err)
55+
filter = []string{
56+
"mongodb_up",
57+
}
58+
err := testutil.CollectAndCompare(c, expected, filter...)
59+
require.NoError(t, err)
5660

57-
assert.NoError(t, client.Disconnect(ctx))
61+
assert.NoError(t, client.Disconnect(ctx))
5862

59-
expected = strings.NewReader(`
63+
expected = strings.NewReader(`
6064
# HELP mongodb_up Whether MongoDB is up.
6165
# TYPE mongodb_up gauge
62-
mongodb_up 0
66+
mongodb_up {cluster_role="mongod"} 0
6367
` + "\n")
64-
filter = []string{
65-
"mongodb_up",
66-
}
67-
err = testutil.CollectAndCompare(c, expected, filter...)
68-
require.NoError(t, err)
68+
filter = []string{
69+
"mongodb_up",
70+
}
71+
err = testutil.CollectAndCompare(c, expected, filter...)
72+
require.NoError(t, err)
73+
})
74+
75+
t.Run("mongos cluster role", func(t *testing.T) {
76+
t.Parallel()
77+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
78+
defer cancel()
79+
80+
port, err := tu.PortForContainer("mongos")
81+
require.NoError(t, err)
82+
client := tu.TestClient(ctx, port, t)
83+
84+
nodeType, _ := getNodeType(ctx, client)
85+
c := newGeneralCollector(ctx, client, nodeType, logrus.New())
86+
87+
filter := []string{
88+
"collector_scrape_time_ms",
89+
}
90+
count := testutil.CollectAndCount(c, filter...)
91+
assert.Equal(t, len(filter), count, "Meta-metric for collector is missing")
92+
93+
// The last \n at the end of this string is important
94+
expected := strings.NewReader(`
95+
# HELP mongodb_up Whether MongoDB is up.
96+
# TYPE mongodb_up gauge
97+
mongodb_up {cluster_role="mongos"} 1
98+
` + "\n")
99+
filter = []string{
100+
"mongodb_up",
101+
}
102+
err = testutil.CollectAndCompare(c, expected, filter...)
103+
require.NoError(t, err)
104+
105+
assert.NoError(t, client.Disconnect(ctx))
106+
107+
expected = strings.NewReader(`
108+
# HELP mongodb_up Whether MongoDB is up.
109+
# TYPE mongodb_up gauge
110+
mongodb_up {cluster_role="mongos"} 0
111+
` + "\n")
112+
filter = []string{
113+
"mongodb_up",
114+
}
115+
err = testutil.CollectAndCompare(c, expected, filter...)
116+
require.NoError(t, err)
117+
})
69118
}

exporter/multi_target_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,10 @@ func TestMultiTarget(t *testing.T) {
5959
serverMap := buildServerMap(exporters, log)
6060

6161
expected := []string{
62-
"mongodb_up 1\n",
63-
"mongodb_up 1\n",
64-
"mongodb_up 1\n",
65-
"mongodb_up 0\n",
62+
"mongodb_up{cluster_role=\"mongod\"} 1\n",
63+
"mongodb_up{cluster_role=\"mongod\"} 1\n",
64+
"mongodb_up{cluster_role=\"mongod\"} 1\n",
65+
"mongodb_up{cluster_role=\"\"} 0\n",
6666
}
6767

6868
// Test all targets

exporter/topology_info.go

+3
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ func (t *topologyInfo) loadLabels(ctx context.Context) error {
138138
}
139139

140140
func getNodeType(ctx context.Context, client *mongo.Client) (mongoDBNodeType, error) {
141+
if client == nil {
142+
return "", errors.New("cannot get mongo node type from an empty client")
143+
}
141144
md := proto.MasterDoc{}
142145
if err := client.Database("admin").RunCommand(ctx, primitive.M{"isMaster": 1}).Decode(&md); err != nil {
143146
return "", err

0 commit comments

Comments
 (0)