Skip to content

Commit 63ab004

Browse files
authored
Merge branch 'main' into main
2 parents fd507c5 + 86756f9 commit 63ab004

File tree

12 files changed

+190
-22
lines changed

12 files changed

+190
-22
lines changed

docker-compose.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,13 @@ services:
206206
ports:
207207
- "${TEST_MONGODB_STANDALONE_PORT:-27017}:27017"
208208
command: mongod --port 27017 --oplogSize 16
209+
210+
standalone-encrypted:
211+
container_name: "standalone-encrypted"
212+
image: percona/percona-server-mongodb:5.0.13-11
213+
ports:
214+
- "${TEST_MONGODB_STANDALONE_ENCRYPTED_PORT:-27027}:27017"
215+
volumes:
216+
- ./docker/secret/mongodb_secrets.txt:/secret/mongodb_secrets.txt
217+
- ./docker/scripts:/scripts
218+
command: /scripts/run-mongodb-encrypted.sh
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/bash
2+
3+
# set proper permissions for secret file, otherwise mongodb won't start
4+
chmod 600 /secret/mongodb_secrets.txt
5+
mongod --port 27017 --oplogSize 16 --bind_ip_all --enableEncryption --encryptionKeyFile /secret/mongodb_secrets.txt

docker/secret/mongodb_secrets.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
zN94kcTrN2CC/X1L9a54/VebKctZnJ/QIO3JdzUWKdY=

exporter/diagnostic_data_collector.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ import (
2626
"go.mongodb.org/mongo-driver/mongo"
2727
)
2828

29+
const (
30+
kmipEncryption = "kmip"
31+
vaultEncryption = "vault"
32+
localKeyFileEncryption = "localKeyFile"
33+
)
34+
2935
type diagnosticDataCollector struct {
3036
ctx context.Context
3137
base *baseCollector
@@ -89,6 +95,13 @@ func (d *diagnosticDataCollector) collect(ch chan<- prometheus.Metric) {
8995
metrics := makeMetrics("", m, d.topologyInfo.baseLabels(), d.compatibleMode)
9096
metrics = append(metrics, locksMetrics(logger, m)...)
9197

98+
securityMetric, err := d.getSecurityMetricFromLineOptions(client)
99+
if err != nil {
100+
logger.Errorf("cannot decode getCmdLineOtpions: %s", err)
101+
} else if securityMetric != nil {
102+
metrics = append(metrics, securityMetric)
103+
}
104+
92105
if d.compatibleMode {
93106
metrics = append(metrics, specialMetrics(d.ctx, client, m, logger)...)
94107

@@ -111,5 +124,67 @@ func (d *diagnosticDataCollector) collect(ch chan<- prometheus.Metric) {
111124
}
112125
}
113126

127+
func (d *diagnosticDataCollector) getSecurityMetricFromLineOptions(client *mongo.Client) (prometheus.Metric, error) {
128+
var cmdLineOpionsBson bson.M
129+
cmdLineOptions := bson.D{{Key: "getCmdLineOpts", Value: "1"}}
130+
resCmdLineOptions := client.Database("admin").RunCommand(d.ctx, cmdLineOptions)
131+
if resCmdLineOptions.Err() != nil {
132+
return nil, errors.Wrap(resCmdLineOptions.Err(), "cannot execute getCmdLineOpts command")
133+
}
134+
if err := resCmdLineOptions.Decode(&cmdLineOpionsBson); err != nil {
135+
return nil, errors.Wrap(err, "cannot parse response of the getCmdLineOpts command")
136+
}
137+
138+
if cmdLineOpionsBson == nil || cmdLineOpionsBson["parsed"] == nil {
139+
return nil, errors.New("cmdlined options is empty")
140+
}
141+
parsedOptions, ok := cmdLineOpionsBson["parsed"].(bson.M)
142+
if !ok {
143+
return nil, errors.New("cannot cast parsed options to BSON")
144+
}
145+
securityOptions, ok := parsedOptions["security"].(bson.M)
146+
if !ok {
147+
return nil, nil
148+
}
149+
150+
metric, err := d.retrieveSecurityEncryptionMetric(securityOptions)
151+
if err != nil {
152+
return nil, err
153+
}
154+
155+
return metric, nil
156+
}
157+
158+
func (d *diagnosticDataCollector) retrieveSecurityEncryptionMetric(securityOptions bson.M) (prometheus.Metric, error) {
159+
_, ok := securityOptions["enableEncryption"]
160+
if !ok {
161+
return nil, nil
162+
}
163+
164+
var encryptionType string
165+
_, ok = securityOptions["kmip"]
166+
if ok {
167+
encryptionType = kmipEncryption
168+
}
169+
_, ok = securityOptions["vault"]
170+
if ok {
171+
encryptionType = vaultEncryption
172+
}
173+
_, ok = securityOptions["encryptionKeyFile"]
174+
if ok {
175+
encryptionType = localKeyFileEncryption
176+
}
177+
178+
labels := map[string]string{"type": encryptionType}
179+
desc := prometheus.NewDesc("mongodb_security_encryption_enabled", "Shows that encryption is enabled",
180+
nil, labels)
181+
metric, err := prometheus.NewConstMetric(desc, prometheus.GaugeValue, float64(1))
182+
if err != nil {
183+
return nil, errors.Wrap(err, "cannot create metric mongodb_security_encryption_enabled")
184+
}
185+
186+
return metric, nil
187+
}
188+
114189
// check interface.
115190
var _ prometheus.Collector = (*diagnosticDataCollector)(nil)

exporter/diagnostic_data_collector_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ func TestDisconnectedDiagnosticDataCollector(t *testing.T) {
292292
mongodb_mongod_storage_engine{engine="Engine is unavailable"} 1
293293
# HELP mongodb_version_info The server version
294294
# TYPE mongodb_version_info gauge
295-
mongodb_version_info{edition="",mongodb="server version is unavailable",vendor=""} 1` + "\n")
295+
mongodb_version_info{edition="",mongodb="",vendor=""} 1` + "\n")
296296
// Filter metrics for 2 reasons:
297297
// 1. The result is huge
298298
// 2. We need to check against know values. Don't use metrics that return counters like uptime

exporter/encryption_info_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// mongodb_exporter
2+
// Copyright (C) 2017 Percona LLC
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
package exporter
18+
19+
import (
20+
"context"
21+
"io"
22+
"strings"
23+
"testing"
24+
"time"
25+
26+
"github.com/prometheus/client_golang/prometheus/testutil"
27+
"github.com/sirupsen/logrus"
28+
"github.com/stretchr/testify/assert"
29+
30+
"github.com/percona/mongodb_exporter/internal/tu"
31+
)
32+
33+
func TestGetEncryptionInfo(t *testing.T) {
34+
t.Parallel()
35+
36+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
37+
defer cancel()
38+
39+
client := tu.TestClient(ctx, tu.MongoDBStandAloneEncryptedPort, t)
40+
t.Cleanup(func() {
41+
err := client.Disconnect(ctx)
42+
assert.NoError(t, err)
43+
})
44+
logger := logrus.New()
45+
logger.Out = io.Discard // disable logs in tests
46+
47+
ti := labelsGetterMock{}
48+
49+
c := newDiagnosticDataCollector(ctx, client, logger, true, ti)
50+
51+
// The last \n at the end of this string is important
52+
expected := strings.NewReader(`
53+
# HELP mongodb_security_encryption_enabled Shows that encryption is enabled
54+
# TYPE mongodb_security_encryption_enabled gauge
55+
mongodb_security_encryption_enabled{type="localKeyFile"} 1
56+
# HELP mongodb_version_info The server version
57+
# TYPE mongodb_version_info gauge
58+
mongodb_version_info{edition="Community",mongodb="5.0.13-11",vendor="Percona"} 1` + "\n")
59+
60+
filter := []string{
61+
"mongodb_security_encryption_enabled",
62+
"mongodb_version_info",
63+
}
64+
65+
err := testutil.CollectAndCompare(c, expected, filter...)
66+
assert.NoError(t, err)
67+
}

exporter/v1_compatibility.go

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -799,7 +799,7 @@ func specialMetrics(ctx context.Context, client *mongo.Client, m bson.M, l *logr
799799
}
800800

801801
metrics = append(metrics, storageEngine(m))
802-
metrics = append(metrics, serverVersion(m, buildInfo))
802+
metrics = append(metrics, serverVersion(buildInfo))
803803
metrics = append(metrics, myState(ctx, client))
804804

805805
if mm := replSetMetrics(m); mm != nil {
@@ -825,7 +825,6 @@ func retrieveMongoDBBuildInfo(ctx context.Context, client *mongo.Client, l *logr
825825
return buildInfo{}, errors.Wrap(err, "Failed to run buildInfo command")
826826
}
827827

828-
var edition string
829828
modules, ok := buildInfoDoc["modules"].(bson.A)
830829
if !ok {
831830
return buildInfo{}, errors.Wrap(err, "Failed to cast module information variable")
@@ -837,7 +836,7 @@ func retrieveMongoDBBuildInfo(ctx context.Context, client *mongo.Client, l *logr
837836
} else {
838837
bi.Edition = CommunityEdition
839838
}
840-
l.Debug("MongoDB edition: ", edition)
839+
l.Debug("MongoDB edition: ", bi.Edition)
841840

842841
_, ok = buildInfoDoc["psmdbVersion"]
843842
if ok {
@@ -846,6 +845,11 @@ func retrieveMongoDBBuildInfo(ctx context.Context, client *mongo.Client, l *logr
846845
bi.Vendor = MongoDBVendor
847846
}
848847

848+
bi.Version, ok = buildInfoDoc["version"].(string)
849+
if !ok {
850+
return buildInfo{}, errors.Wrap(err, "Failed to cast version information variable")
851+
}
852+
849853
return bi, nil
850854
}
851855

@@ -866,16 +870,11 @@ func storageEngine(m bson.M) prometheus.Metric {
866870
return metric
867871
}
868872

869-
func serverVersion(m bson.M, bi buildInfo) prometheus.Metric { //nolint:ireturn
870-
v := walkTo(m, []string{"serverStatus", "version"})
873+
func serverVersion(bi buildInfo) prometheus.Metric { //nolint:ireturn
871874
name := "mongodb_version_info"
872875
help := "The server version"
873876

874-
serverVersion, ok := v.(string)
875-
if !ok {
876-
serverVersion = "server version is unavailable"
877-
}
878-
labels := map[string]string{"mongodb": serverVersion, "edition": bi.Edition, "vendor": bi.Vendor}
877+
labels := map[string]string{"mongodb": bi.Version, "edition": bi.Edition, "vendor": bi.Vendor}
879878

880879
d := prometheus.NewDesc(name, help, nil, labels)
881880
metric, _ := prometheus.NewConstMetric(d, prometheus.GaugeValue, float64(1))
@@ -1053,7 +1052,11 @@ func mongosMetrics(ctx context.Context, client *mongo.Client, l *logrus.Logger)
10531052
metrics = append(metrics, metric)
10541053
}
10551054

1056-
metrics = append(metrics, balancerEnabled(ctx, client))
1055+
if metric, err := balancerEnabled(ctx, client); err != nil {
1056+
l.Debugf("cannot create metric for balancer is enabled: %s", err)
1057+
} else {
1058+
metrics = append(metrics, metric)
1059+
}
10571060

10581061
metric, err := chunksTotal(ctx, client)
10591062
if err != nil {
@@ -1164,17 +1167,19 @@ func chunksBalanced(ctx context.Context, client *mongo.Client) (prometheus.Metri
11641167
return prometheus.NewConstMetric(d, prometheus.GaugeValue, value)
11651168
}
11661169

1167-
func balancerEnabled(ctx context.Context, client *mongo.Client) prometheus.Metric {
1170+
func balancerEnabled(ctx context.Context, client *mongo.Client) (prometheus.Metric, error) {
11681171
type bss struct {
1169-
stopped bool `bson:"stopped"`
1172+
Stopped bool `bson:"stopped"`
11701173
}
11711174
var bs bss
11721175
enabled := 0
11731176

11741177
err := client.Database("config").Collection("settings").FindOne(ctx, bson.M{"_id": "balancer"}).Decode(&bs)
11751178
if err != nil {
1176-
enabled = 1
1177-
} else if !bs.stopped {
1179+
return nil, err
1180+
}
1181+
1182+
if !bs.Stopped {
11781183
enabled = 1
11791184
}
11801185

@@ -1184,7 +1189,7 @@ func balancerEnabled(ctx context.Context, client *mongo.Client) prometheus.Metri
11841189
d := prometheus.NewDesc(name, help, nil, nil)
11851190
metric, _ := prometheus.NewConstMetric(d, prometheus.GaugeValue, float64(enabled))
11861191

1187-
return metric
1192+
return metric, nil
11881193
}
11891194

11901195
func chunksTotal(ctx context.Context, client *mongo.Client) (prometheus.Metric, error) {
@@ -1348,6 +1353,7 @@ type rawStatus struct {
13481353
}
13491354

13501355
type buildInfo struct {
1356+
Version string
13511357
Edition string
13521358
Vendor string
13531359
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ require (
2020
github.com/prometheus/exporter-toolkit v0.7.1
2121
github.com/shirou/gopsutil v3.21.8+incompatible // indirect
2222
github.com/sirupsen/logrus v1.9.0
23-
github.com/stretchr/testify v1.8.0
23+
github.com/stretchr/testify v1.8.1
2424
go.mongodb.org/mongo-driver v1.10.3
2525
)
2626

go.sum

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,15 +321,17 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
321321
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
322322
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
323323
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
324+
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
324325
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
325326
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
326327
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
327328
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
328329
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
329330
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
330331
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
331-
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
332332
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
333+
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
334+
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
333335
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
334336
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
335337
github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo=

internal/tu/testutils.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ const (
5050
MongoDBStandAlonePort = "27017"
5151
// MongoDBConfigServer1Port MongoDB config server primary Port.
5252
MongoDBConfigServer1Port = "17009"
53+
// MongoDBStandAloneEncryptedPort MongoDB standalone encrypted instance Port.
54+
MongoDBStandAloneEncryptedPort = "27027"
5355
)
5456

5557
// GetenvDefault gets a variable from the environment and returns its value or the

0 commit comments

Comments
 (0)