Skip to content

Commit 069997e

Browse files
committed
CLOUDP-321076: expose prometheus metrics settings in MongoDBSearch
1 parent 2c8dc84 commit 069997e

File tree

8 files changed

+331
-20
lines changed

8 files changed

+331
-20
lines changed

api/v1/search/mongodbsearch_types.go

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ import (
1717
)
1818

1919
const (
20-
MongotDefaultWireprotoPort = 27027
21-
MongotDefaultGrpcPort = 27028
22-
MongotDefaultMetricsPort = 9946
23-
MongotDefautHealthCheckPort = 8080
24-
MongotDefaultSyncSourceUsername = "search-sync-source"
20+
MongotDefaultWireprotoPort int32 = 27027
21+
MongotDefaultGrpcPort int32 = 27028
22+
MongotDefaultPrometheusPort int32 = 9946
23+
MongotDefautHealthCheckPort int32 = 8080
24+
MongotDefaultSyncSourceUsername = "search-sync-source"
2525

2626
ForceWireprotoAnnotation = "mongodb.com/v1.force-search-wireproto"
2727
)
@@ -30,6 +30,14 @@ func init() {
3030
v1.SchemeBuilder.Register(&MongoDBSearch{}, &MongoDBSearchList{})
3131
}
3232

33+
type Prometheus struct {
34+
// Port where metrics endpoint will be exposed on. Defaults to 9216.
35+
// +optional
36+
// +kubebuilder:validation:Minimum=0
37+
// +kubebuilder:validation:Maximum=65535
38+
Port int `json:"port,omitempty"`
39+
}
40+
3341
type MongoDBSearchSpec struct {
3442
// Optional version of MongoDB Search component (mongot). If not set, then the operator will set the most appropriate version of MongoDB Search.
3543
// +optional
@@ -54,6 +62,9 @@ type MongoDBSearchSpec struct {
5462
// +kubebuilder:validation:Enum=TRACE;DEBUG;INFO;WARN;ERROR
5563
// +optional
5664
LogLevel mdb.LogLevel `json:"logLevel,omitempty"`
65+
// Configure prometheus metrics endpoint in mongot. If not set, the metrics endpoint will be disabled.
66+
// +optional
67+
Prometheus *Prometheus `json:"prometheus,omitempty"`
5768
}
5869

5970
type MongoDBSource struct {
@@ -219,7 +230,11 @@ func (s *MongoDBSearch) GetMongotGrpcPort() int32 {
219230
}
220231

221232
func (s *MongoDBSearch) GetMongotMetricsPort() int32 {
222-
return MongotDefaultMetricsPort
233+
if s.Spec.Prometheus == nil || s.Spec.Prometheus.Port == 0 {
234+
return MongotDefaultPrometheusPort
235+
}
236+
237+
return int32(s.Spec.Prometheus.Port)
223238
}
224239

225240
// TLSSecretNamespacedName will get the namespaced name of the Secret containing the server certificate and key
@@ -263,3 +278,7 @@ func (s *MongoDBSearch) GetEffectiveMongotPort() int32 {
263278
}
264279
return s.GetMongotGrpcPort()
265280
}
281+
282+
func (s *MongoDBSearch) GetPrometheus() *Prometheus {
283+
return s.Spec.Prometheus
284+
}

config/crd/bases/mongodb.com_mongodbsearch.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,17 @@ spec:
106106
type: string
107107
type: object
108108
type: object
109+
prometheus:
110+
description: Configure prometheus metrics endpoint in mongot. If not
111+
set, the metrics endpoint will be disabled.
112+
properties:
113+
port:
114+
description: Port where metrics endpoint will be exposed on. Defaults
115+
to 9216.
116+
maximum: 65535
117+
minimum: 0
118+
type: integer
119+
type: object
109120
resourceRequirements:
110121
description: Configure resource requests and limits for the MongoDB
111122
Search pods.

controllers/searchcontroller/mongodbsearch_reconcile_helper.go

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func (r *MongoDBSearchReconcileHelper) Reconcile(ctx context.Context, log *zap.S
7676
if _, err := commoncontroller.UpdateStatus(ctx, r.client, r.mdbSearch, workflowStatus, log); err != nil {
7777
return workflow.Failed(err)
7878
}
79-
return workflow.OK()
79+
return workflowStatus
8080
}
8181

8282
func (r *MongoDBSearchReconcileHelper) reconcile(ctx context.Context, log *zap.SugaredLogger) workflow.Status {
@@ -312,7 +312,7 @@ func buildSearchHeadlessService(search *searchv1.MongoDBSearch) corev1.Service {
312312
SetLabels(labels).
313313
SetServiceType(corev1.ServiceTypeClusterIP).
314314
SetClusterIP("None").
315-
SetPublishNotReadyAddresses(true).
315+
SetPublishNotReadyAddresses(false).
316316
SetOwnerReferences(search.GetOwnerReferences())
317317

318318
if search.IsWireprotoEnabled() {
@@ -331,12 +331,14 @@ func buildSearchHeadlessService(search *searchv1.MongoDBSearch) corev1.Service {
331331
TargetPort: intstr.FromInt32(search.GetMongotGrpcPort()),
332332
})
333333

334-
serviceBuilder.AddPort(&corev1.ServicePort{
335-
Name: "metrics",
336-
Protocol: corev1.ProtocolTCP,
337-
Port: search.GetMongotMetricsPort(),
338-
TargetPort: intstr.FromInt32(search.GetMongotMetricsPort()),
339-
})
334+
if search.GetPrometheus() != nil {
335+
serviceBuilder.AddPort(&corev1.ServicePort{
336+
Name: "prometheus",
337+
Protocol: corev1.ProtocolTCP,
338+
Port: search.GetMongotMetricsPort(),
339+
TargetPort: intstr.FromInt32(search.GetMongotMetricsPort()),
340+
})
341+
}
340342

341343
serviceBuilder.AddPort(&corev1.ServicePort{
342344
Name: "healthcheck",
@@ -385,10 +387,18 @@ func createMongotConfig(search *searchv1.MongoDBSearch, db SearchSourceDBResourc
385387
},
386388
}
387389
}
388-
config.Metrics = mongot.ConfigMetrics{
389-
Enabled: true,
390-
Address: fmt.Sprintf("0.0.0.0:%d", search.GetMongotMetricsPort()),
390+
391+
if prometheus := search.GetPrometheus(); prometheus != nil {
392+
port := search.GetMongotMetricsPort()
393+
if prometheus.Port != 0 {
394+
port = int32(prometheus.Port)
395+
}
396+
config.Metrics = mongot.ConfigMetrics{
397+
Enabled: true,
398+
Address: fmt.Sprintf("0.0.0.0:%d", port),
399+
}
391400
}
401+
392402
config.HealthCheck = mongot.ConfigHealthCheck{
393403
Address: fmt.Sprintf("0.0.0.0:%d", search.GetMongotHealthCheckPort()),
394404
}

controllers/searchcontroller/mongodbsearch_reconcile_helper_test.go

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,104 @@
11
package searchcontroller
22

33
import (
4+
"context"
45
"fmt"
6+
"github.com/stretchr/testify/require"
7+
corev1 "k8s.io/api/core/v1"
58
"strings"
69
"testing"
710

811
"github.com/stretchr/testify/assert"
12+
"go.uber.org/zap"
913
"sigs.k8s.io/controller-runtime/pkg/client"
1014

1115
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1216

1317
searchv1 "github.com/mongodb/mongodb-kubernetes/api/v1/search"
1418
userv1 "github.com/mongodb/mongodb-kubernetes/api/v1/user"
1519
"github.com/mongodb/mongodb-kubernetes/controllers/operator/mock"
20+
"github.com/mongodb/mongodb-kubernetes/controllers/operator/workflow"
1621
mdbcv1 "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/api/v1"
1722
kubernetesClient "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/kube/client"
1823
)
1924

25+
func init() {
26+
logger, _ := zap.NewDevelopment()
27+
zap.ReplaceGlobals(logger)
28+
}
29+
30+
func newTestMongoDBSearch(name, namespace string, modifications ...func(*searchv1.MongoDBSearch)) *searchv1.MongoDBSearch {
31+
mdbSearch := &searchv1.MongoDBSearch{
32+
ObjectMeta: metav1.ObjectMeta{
33+
Name: name,
34+
Namespace: namespace,
35+
},
36+
Spec: searchv1.MongoDBSearchSpec{
37+
Source: &searchv1.MongoDBSource{
38+
MongoDBResourceRef: &userv1.MongoDBResourceRef{
39+
Name: "test-mongodb",
40+
},
41+
},
42+
},
43+
}
44+
45+
for _, modify := range modifications {
46+
modify(mdbSearch)
47+
}
48+
49+
return mdbSearch
50+
}
51+
52+
func newTestMongoDBCommunity(name, namespace string, modifications ...func(*mdbcv1.MongoDBCommunity)) *mdbcv1.MongoDBCommunity {
53+
mdbc := &mdbcv1.MongoDBCommunity{
54+
ObjectMeta: metav1.ObjectMeta{
55+
Name: name,
56+
Namespace: namespace,
57+
},
58+
Spec: mdbcv1.MongoDBCommunitySpec{
59+
Version: "8.2.0",
60+
Members: 3,
61+
},
62+
}
63+
64+
for _, modify := range modifications {
65+
modify(mdbc)
66+
}
67+
68+
return mdbc
69+
}
70+
71+
func newTestOperatorSearchConfig() OperatorSearchConfig {
72+
config := OperatorSearchConfig{
73+
SearchRepo: "test-repo",
74+
SearchName: "mongot",
75+
SearchVersion: "0.0.0",
76+
}
77+
78+
return config
79+
}
80+
81+
func newTestFakeClient(objects ...client.Object) kubernetesClient.Client {
82+
clientBuilder := mock.NewEmptyFakeClientBuilder()
83+
clientBuilder.WithIndex(&searchv1.MongoDBSearch{}, MongoDBSearchIndexFieldName, func(obj client.Object) []string {
84+
mdbResource := obj.(*searchv1.MongoDBSearch).GetMongoDBResourceRef()
85+
return []string{mdbResource.Namespace + "/" + mdbResource.Name}
86+
})
87+
clientBuilder.WithObjects(objects...)
88+
return kubernetesClient.NewClient(clientBuilder.Build())
89+
}
90+
91+
func reconcileMongoDBSearch(ctx context.Context, fakeClient kubernetesClient.Client, mdbSearch *searchv1.MongoDBSearch, mdbc *mdbcv1.MongoDBCommunity, operatorConfig OperatorSearchConfig) workflow.Status {
92+
helper := NewMongoDBSearchReconcileHelper(
93+
fakeClient,
94+
mdbSearch,
95+
NewCommunityResourceSearchSource(mdbc),
96+
operatorConfig,
97+
)
98+
99+
return helper.Reconcile(ctx, zap.S())
100+
}
101+
20102
func TestMongoDBSearchReconcileHelper_ValidateSingleMongoDBSearchForSearchSource(t *testing.T) {
21103
mdbSearchSpec := searchv1.MongoDBSearchSpec{
22104
Source: &searchv1.MongoDBSource{
@@ -152,3 +234,96 @@ func TestGetMongodConfigParameters_TransportAndPorts(t *testing.T) {
152234
})
153235
}
154236
}
237+
238+
func assertServiceBasicProperties(t *testing.T, svc corev1.Service, mdbSearch *searchv1.MongoDBSearch) {
239+
t.Helper()
240+
svcName := mdbSearch.SearchServiceNamespacedName()
241+
242+
assert.Equal(t, svcName.Name, svc.Name)
243+
assert.Equal(t, svcName.Namespace, svc.Namespace)
244+
assert.Equal(t, "ClusterIP", string(svc.Spec.Type))
245+
assert.Equal(t, "None", svc.Spec.ClusterIP)
246+
assert.False(t, svc.Spec.PublishNotReadyAddresses)
247+
248+
expectedAppLabel := svcName.Name
249+
assert.Equal(t, expectedAppLabel, svc.Labels["app"])
250+
assert.Equal(t, expectedAppLabel, svc.Spec.Selector["app"])
251+
}
252+
253+
func assertServicePorts(t *testing.T, svc corev1.Service, expectedPorts map[string]int32) {
254+
t.Helper()
255+
256+
portMap := make(map[string]int32)
257+
for _, port := range svc.Spec.Ports {
258+
portMap[port.Name] = port.Port
259+
}
260+
261+
assert.Len(t, svc.Spec.Ports, len(expectedPorts), "Expected %d ports but got %d", len(expectedPorts), len(svc.Spec.Ports))
262+
263+
for portName, expectedPort := range expectedPorts {
264+
actualPort, exists := portMap[portName]
265+
assert.True(t, exists, "Expected port %s to exist", portName)
266+
assert.Equal(t, expectedPort, actualPort, "Port %s has wrong value", portName)
267+
}
268+
}
269+
270+
func TestMongoDBSearchReconcileHelper_ServiceCreation(t *testing.T) {
271+
cases := []struct {
272+
name string
273+
modifySearch func(*searchv1.MongoDBSearch)
274+
expectedPorts map[string]int32
275+
}{
276+
{
277+
name: "Default configuration with prometheus enabled",
278+
modifySearch: func(search *searchv1.MongoDBSearch) {
279+
search.Spec.Prometheus = &searchv1.Prometheus{}
280+
},
281+
expectedPorts: map[string]int32{
282+
"mongot-grpc": searchv1.MongotDefaultGrpcPort,
283+
"prometheus": searchv1.MongotDefaultPrometheusPort,
284+
"healthcheck": searchv1.MongotDefautHealthCheckPort,
285+
},
286+
},
287+
{
288+
name: "Prometheus enabled with custom port",
289+
modifySearch: func(search *searchv1.MongoDBSearch) {
290+
search.Spec.Prometheus = &searchv1.Prometheus{
291+
Port: 9999,
292+
}
293+
},
294+
expectedPorts: map[string]int32{
295+
"mongot-grpc": searchv1.MongotDefaultGrpcPort,
296+
"prometheus": 9999,
297+
"healthcheck": searchv1.MongotDefautHealthCheckPort,
298+
},
299+
},
300+
{
301+
name: "Prometheus disabled",
302+
modifySearch: func(search *searchv1.MongoDBSearch) {
303+
search.Spec.Prometheus = nil
304+
},
305+
expectedPorts: map[string]int32{
306+
"mongot-grpc": searchv1.MongotDefaultGrpcPort,
307+
"healthcheck": searchv1.MongotDefautHealthCheckPort,
308+
},
309+
},
310+
}
311+
312+
for _, tc := range cases {
313+
t.Run(tc.name, func(t *testing.T) {
314+
mdbSearch := newTestMongoDBSearch("test-mongodb-search", "test", tc.modifySearch)
315+
mdbc := newTestMongoDBCommunity("test-mongodb", "test")
316+
fakeClient := newTestFakeClient(mdbSearch, mdbc)
317+
318+
reconcileMongoDBSearch(t.Context(), fakeClient, mdbSearch, mdbc, newTestOperatorSearchConfig())
319+
320+
svcName := mdbSearch.SearchServiceNamespacedName()
321+
svc, err := fakeClient.GetService(t.Context(), svcName)
322+
require.NoError(t, err)
323+
require.NotNil(t, svc)
324+
325+
assertServiceBasicProperties(t, svc, mdbSearch)
326+
assertServicePorts(t, svc, tc.expectedPorts)
327+
})
328+
}
329+
}

0 commit comments

Comments
 (0)