From 069997e86a6ff5d3350a31bcecd648cfb9048b8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Sierant?= Date: Fri, 7 Nov 2025 16:10:03 +0100 Subject: [PATCH 1/9] CLOUDP-321076: expose prometheus metrics settings in MongoDBSearch --- api/v1/search/mongodbsearch_types.go | 31 +++- .../crd/bases/mongodb.com_mongodbsearch.yaml | 11 ++ .../mongodbsearch_reconcile_helper.go | 32 ++-- .../mongodbsearch_reconcile_helper_test.go | 175 ++++++++++++++++++ .../tests/search/search_enterprise_tls.py | 75 +++++++- .../crds/mongodb.com_mongodbsearch.yaml | 11 ++ main.go | 5 +- public/crds.yaml | 11 ++ 8 files changed, 331 insertions(+), 20 deletions(-) diff --git a/api/v1/search/mongodbsearch_types.go b/api/v1/search/mongodbsearch_types.go index 602cf467f..b5f1fe9cb 100644 --- a/api/v1/search/mongodbsearch_types.go +++ b/api/v1/search/mongodbsearch_types.go @@ -17,11 +17,11 @@ import ( ) const ( - MongotDefaultWireprotoPort = 27027 - MongotDefaultGrpcPort = 27028 - MongotDefaultMetricsPort = 9946 - MongotDefautHealthCheckPort = 8080 - MongotDefaultSyncSourceUsername = "search-sync-source" + MongotDefaultWireprotoPort int32 = 27027 + MongotDefaultGrpcPort int32 = 27028 + MongotDefaultPrometheusPort int32 = 9946 + MongotDefautHealthCheckPort int32 = 8080 + MongotDefaultSyncSourceUsername = "search-sync-source" ForceWireprotoAnnotation = "mongodb.com/v1.force-search-wireproto" ) @@ -30,6 +30,14 @@ func init() { v1.SchemeBuilder.Register(&MongoDBSearch{}, &MongoDBSearchList{}) } +type Prometheus struct { + // Port where metrics endpoint will be exposed on. Defaults to 9216. + // +optional + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Maximum=65535 + Port int `json:"port,omitempty"` +} + type MongoDBSearchSpec struct { // Optional version of MongoDB Search component (mongot). If not set, then the operator will set the most appropriate version of MongoDB Search. // +optional @@ -54,6 +62,9 @@ type MongoDBSearchSpec struct { // +kubebuilder:validation:Enum=TRACE;DEBUG;INFO;WARN;ERROR // +optional LogLevel mdb.LogLevel `json:"logLevel,omitempty"` + // Configure prometheus metrics endpoint in mongot. If not set, the metrics endpoint will be disabled. + // +optional + Prometheus *Prometheus `json:"prometheus,omitempty"` } type MongoDBSource struct { @@ -219,7 +230,11 @@ func (s *MongoDBSearch) GetMongotGrpcPort() int32 { } func (s *MongoDBSearch) GetMongotMetricsPort() int32 { - return MongotDefaultMetricsPort + if s.Spec.Prometheus == nil || s.Spec.Prometheus.Port == 0 { + return MongotDefaultPrometheusPort + } + + return int32(s.Spec.Prometheus.Port) } // TLSSecretNamespacedName will get the namespaced name of the Secret containing the server certificate and key @@ -263,3 +278,7 @@ func (s *MongoDBSearch) GetEffectiveMongotPort() int32 { } return s.GetMongotGrpcPort() } + +func (s *MongoDBSearch) GetPrometheus() *Prometheus { + return s.Spec.Prometheus +} diff --git a/config/crd/bases/mongodb.com_mongodbsearch.yaml b/config/crd/bases/mongodb.com_mongodbsearch.yaml index 15153ba25..ce46291f7 100644 --- a/config/crd/bases/mongodb.com_mongodbsearch.yaml +++ b/config/crd/bases/mongodb.com_mongodbsearch.yaml @@ -106,6 +106,17 @@ spec: type: string type: object type: object + prometheus: + description: Configure prometheus metrics endpoint in mongot. If not + set, the metrics endpoint will be disabled. + properties: + port: + description: Port where metrics endpoint will be exposed on. Defaults + to 9216. + maximum: 65535 + minimum: 0 + type: integer + type: object resourceRequirements: description: Configure resource requests and limits for the MongoDB Search pods. diff --git a/controllers/searchcontroller/mongodbsearch_reconcile_helper.go b/controllers/searchcontroller/mongodbsearch_reconcile_helper.go index d3a5c32a0..e20822942 100644 --- a/controllers/searchcontroller/mongodbsearch_reconcile_helper.go +++ b/controllers/searchcontroller/mongodbsearch_reconcile_helper.go @@ -76,7 +76,7 @@ func (r *MongoDBSearchReconcileHelper) Reconcile(ctx context.Context, log *zap.S if _, err := commoncontroller.UpdateStatus(ctx, r.client, r.mdbSearch, workflowStatus, log); err != nil { return workflow.Failed(err) } - return workflow.OK() + return workflowStatus } func (r *MongoDBSearchReconcileHelper) reconcile(ctx context.Context, log *zap.SugaredLogger) workflow.Status { @@ -312,7 +312,7 @@ func buildSearchHeadlessService(search *searchv1.MongoDBSearch) corev1.Service { SetLabels(labels). SetServiceType(corev1.ServiceTypeClusterIP). SetClusterIP("None"). - SetPublishNotReadyAddresses(true). + SetPublishNotReadyAddresses(false). SetOwnerReferences(search.GetOwnerReferences()) if search.IsWireprotoEnabled() { @@ -331,12 +331,14 @@ func buildSearchHeadlessService(search *searchv1.MongoDBSearch) corev1.Service { TargetPort: intstr.FromInt32(search.GetMongotGrpcPort()), }) - serviceBuilder.AddPort(&corev1.ServicePort{ - Name: "metrics", - Protocol: corev1.ProtocolTCP, - Port: search.GetMongotMetricsPort(), - TargetPort: intstr.FromInt32(search.GetMongotMetricsPort()), - }) + if search.GetPrometheus() != nil { + serviceBuilder.AddPort(&corev1.ServicePort{ + Name: "prometheus", + Protocol: corev1.ProtocolTCP, + Port: search.GetMongotMetricsPort(), + TargetPort: intstr.FromInt32(search.GetMongotMetricsPort()), + }) + } serviceBuilder.AddPort(&corev1.ServicePort{ Name: "healthcheck", @@ -385,10 +387,18 @@ func createMongotConfig(search *searchv1.MongoDBSearch, db SearchSourceDBResourc }, } } - config.Metrics = mongot.ConfigMetrics{ - Enabled: true, - Address: fmt.Sprintf("0.0.0.0:%d", search.GetMongotMetricsPort()), + + if prometheus := search.GetPrometheus(); prometheus != nil { + port := search.GetMongotMetricsPort() + if prometheus.Port != 0 { + port = int32(prometheus.Port) + } + config.Metrics = mongot.ConfigMetrics{ + Enabled: true, + Address: fmt.Sprintf("0.0.0.0:%d", port), + } } + config.HealthCheck = mongot.ConfigHealthCheck{ Address: fmt.Sprintf("0.0.0.0:%d", search.GetMongotHealthCheckPort()), } diff --git a/controllers/searchcontroller/mongodbsearch_reconcile_helper_test.go b/controllers/searchcontroller/mongodbsearch_reconcile_helper_test.go index affc30c52..d6885bafa 100644 --- a/controllers/searchcontroller/mongodbsearch_reconcile_helper_test.go +++ b/controllers/searchcontroller/mongodbsearch_reconcile_helper_test.go @@ -1,11 +1,15 @@ package searchcontroller import ( + "context" "fmt" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" "strings" "testing" "github.com/stretchr/testify/assert" + "go.uber.org/zap" "sigs.k8s.io/controller-runtime/pkg/client" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -13,10 +17,88 @@ import ( searchv1 "github.com/mongodb/mongodb-kubernetes/api/v1/search" userv1 "github.com/mongodb/mongodb-kubernetes/api/v1/user" "github.com/mongodb/mongodb-kubernetes/controllers/operator/mock" + "github.com/mongodb/mongodb-kubernetes/controllers/operator/workflow" mdbcv1 "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/api/v1" kubernetesClient "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/kube/client" ) +func init() { + logger, _ := zap.NewDevelopment() + zap.ReplaceGlobals(logger) +} + +func newTestMongoDBSearch(name, namespace string, modifications ...func(*searchv1.MongoDBSearch)) *searchv1.MongoDBSearch { + mdbSearch := &searchv1.MongoDBSearch{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: searchv1.MongoDBSearchSpec{ + Source: &searchv1.MongoDBSource{ + MongoDBResourceRef: &userv1.MongoDBResourceRef{ + Name: "test-mongodb", + }, + }, + }, + } + + for _, modify := range modifications { + modify(mdbSearch) + } + + return mdbSearch +} + +func newTestMongoDBCommunity(name, namespace string, modifications ...func(*mdbcv1.MongoDBCommunity)) *mdbcv1.MongoDBCommunity { + mdbc := &mdbcv1.MongoDBCommunity{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: mdbcv1.MongoDBCommunitySpec{ + Version: "8.2.0", + Members: 3, + }, + } + + for _, modify := range modifications { + modify(mdbc) + } + + return mdbc +} + +func newTestOperatorSearchConfig() OperatorSearchConfig { + config := OperatorSearchConfig{ + SearchRepo: "test-repo", + SearchName: "mongot", + SearchVersion: "0.0.0", + } + + return config +} + +func newTestFakeClient(objects ...client.Object) kubernetesClient.Client { + clientBuilder := mock.NewEmptyFakeClientBuilder() + clientBuilder.WithIndex(&searchv1.MongoDBSearch{}, MongoDBSearchIndexFieldName, func(obj client.Object) []string { + mdbResource := obj.(*searchv1.MongoDBSearch).GetMongoDBResourceRef() + return []string{mdbResource.Namespace + "/" + mdbResource.Name} + }) + clientBuilder.WithObjects(objects...) + return kubernetesClient.NewClient(clientBuilder.Build()) +} + +func reconcileMongoDBSearch(ctx context.Context, fakeClient kubernetesClient.Client, mdbSearch *searchv1.MongoDBSearch, mdbc *mdbcv1.MongoDBCommunity, operatorConfig OperatorSearchConfig) workflow.Status { + helper := NewMongoDBSearchReconcileHelper( + fakeClient, + mdbSearch, + NewCommunityResourceSearchSource(mdbc), + operatorConfig, + ) + + return helper.Reconcile(ctx, zap.S()) +} + func TestMongoDBSearchReconcileHelper_ValidateSingleMongoDBSearchForSearchSource(t *testing.T) { mdbSearchSpec := searchv1.MongoDBSearchSpec{ Source: &searchv1.MongoDBSource{ @@ -152,3 +234,96 @@ func TestGetMongodConfigParameters_TransportAndPorts(t *testing.T) { }) } } + +func assertServiceBasicProperties(t *testing.T, svc corev1.Service, mdbSearch *searchv1.MongoDBSearch) { + t.Helper() + svcName := mdbSearch.SearchServiceNamespacedName() + + assert.Equal(t, svcName.Name, svc.Name) + assert.Equal(t, svcName.Namespace, svc.Namespace) + assert.Equal(t, "ClusterIP", string(svc.Spec.Type)) + assert.Equal(t, "None", svc.Spec.ClusterIP) + assert.False(t, svc.Spec.PublishNotReadyAddresses) + + expectedAppLabel := svcName.Name + assert.Equal(t, expectedAppLabel, svc.Labels["app"]) + assert.Equal(t, expectedAppLabel, svc.Spec.Selector["app"]) +} + +func assertServicePorts(t *testing.T, svc corev1.Service, expectedPorts map[string]int32) { + t.Helper() + + portMap := make(map[string]int32) + for _, port := range svc.Spec.Ports { + portMap[port.Name] = port.Port + } + + assert.Len(t, svc.Spec.Ports, len(expectedPorts), "Expected %d ports but got %d", len(expectedPorts), len(svc.Spec.Ports)) + + for portName, expectedPort := range expectedPorts { + actualPort, exists := portMap[portName] + assert.True(t, exists, "Expected port %s to exist", portName) + assert.Equal(t, expectedPort, actualPort, "Port %s has wrong value", portName) + } +} + +func TestMongoDBSearchReconcileHelper_ServiceCreation(t *testing.T) { + cases := []struct { + name string + modifySearch func(*searchv1.MongoDBSearch) + expectedPorts map[string]int32 + }{ + { + name: "Default configuration with prometheus enabled", + modifySearch: func(search *searchv1.MongoDBSearch) { + search.Spec.Prometheus = &searchv1.Prometheus{} + }, + expectedPorts: map[string]int32{ + "mongot-grpc": searchv1.MongotDefaultGrpcPort, + "prometheus": searchv1.MongotDefaultPrometheusPort, + "healthcheck": searchv1.MongotDefautHealthCheckPort, + }, + }, + { + name: "Prometheus enabled with custom port", + modifySearch: func(search *searchv1.MongoDBSearch) { + search.Spec.Prometheus = &searchv1.Prometheus{ + Port: 9999, + } + }, + expectedPorts: map[string]int32{ + "mongot-grpc": searchv1.MongotDefaultGrpcPort, + "prometheus": 9999, + "healthcheck": searchv1.MongotDefautHealthCheckPort, + }, + }, + { + name: "Prometheus disabled", + modifySearch: func(search *searchv1.MongoDBSearch) { + search.Spec.Prometheus = nil + }, + expectedPorts: map[string]int32{ + "mongot-grpc": searchv1.MongotDefaultGrpcPort, + "healthcheck": searchv1.MongotDefautHealthCheckPort, + }, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + mdbSearch := newTestMongoDBSearch("test-mongodb-search", "test", tc.modifySearch) + mdbc := newTestMongoDBCommunity("test-mongodb", "test") + fakeClient := newTestFakeClient(mdbSearch, mdbc) + + reconcileMongoDBSearch(t.Context(), fakeClient, mdbSearch, mdbc, newTestOperatorSearchConfig()) + + svcName := mdbSearch.SearchServiceNamespacedName() + svc, err := fakeClient.GetService(t.Context(), svcName) + require.NoError(t, err) + require.NotNil(t, svc) + + assertServiceBasicProperties(t, svc, mdbSearch) + assertServicePorts(t, svc, tc.expectedPorts) + }) + } +} diff --git a/docker/mongodb-kubernetes-tests/tests/search/search_enterprise_tls.py b/docker/mongodb-kubernetes-tests/tests/search/search_enterprise_tls.py index b6870b507..0bf5c7b93 100644 --- a/docker/mongodb-kubernetes-tests/tests/search/search_enterprise_tls.py +++ b/docker/mongodb-kubernetes-tests/tests/search/search_enterprise_tls.py @@ -1,5 +1,5 @@ import yaml -from kubetester import create_or_update_secret, run_periodically, try_load +from kubetester import create_or_update_secret, run_periodically, try_load, get_service, kubetester from kubetester.certs import create_mongodb_tls_certs, create_tls_certs from kubetester.kubetester import KubernetesTester from kubetester.kubetester import fixture as yaml_fixture @@ -212,6 +212,38 @@ def check_mongod_parameters(): run_periodically(check_mongod_parameters, timeout=600) +@mark.e2e_search_enterprise_tls +def test_search_verify_prometheus_disabled_initially(mdbs: MongoDBSearch): + assert_search_service_prometheus_port(mdbs, should_exist=False) + assert_search_pod_prometheus_endpoint(mdbs, should_be_accessible=False) + + +@mark.e2e_search_enterprise_tls +def test_search_enable_prometheus_on_default_port(mdbs: MongoDBSearch): + mdbs["spec"]["prometheus"] = {} + mdbs.update() + mdbs.assert_reaches_phase(Phase.Running, timeout=300) + + +@mark.e2e_search_enterprise_tls +def test_search_verify_prometheus_enabled(mdbs: MongoDBSearch): + assert_search_service_prometheus_port(mdbs, should_exist=True, expected_port=9946) + assert_search_pod_prometheus_endpoint(mdbs, should_be_accessible=True, port=9946) + + +@mark.e2e_search_enterprise_tls +def test_search_change_prometheus_to_custom_port(mdbs: MongoDBSearch): + mdbs["spec"]["prometheus"] = {"port": 10000} + mdbs.update() + mdbs.assert_reaches_phase(Phase.Running, timeout=300) + + +@mark.e2e_search_enterprise_tls +def test_search_verify_prometheus_enabled_on_custom_port(mdbs: MongoDBSearch): + assert_search_service_prometheus_port(mdbs, should_exist=True, expected_port=10000) + assert_search_pod_prometheus_endpoint(mdbs, should_be_accessible=True, port=10000) + + @mark.e2e_search_enterprise_tls def test_search_restore_sample_database(mdb: MongoDB): get_admin_sample_movies_helper(mdb).restore_sample_database() @@ -247,3 +279,44 @@ def get_user_sample_movies_helper(mdb): get_connection_string(mdb, USER_NAME, USER_PASSWORD), use_ssl=True, ca_path=get_issuer_ca_filepath() ) ) + + +def assert_search_service_prometheus_port(mdbs: MongoDBSearch, should_exist: bool, expected_port: int = 9946): + service_name = f"{mdbs.name}-search-svc" + service = get_service(mdbs.namespace, service_name) + + assert service is not None, f"Service {service_name} should exist" + + ports = {p.name: p.port for p in service.spec.ports} + + if should_exist: + assert "prometheus" in ports, "Prometheus port should be in service when prometheus is enabled" + assert ports["prometheus"] == expected_port, f"Prometheus port should be {expected_port} but was {ports['prometheus']}" + else: + assert "prometheus" not in ports, "Prometheus port should not be in service when prometheus is disabled" + + +def assert_search_pod_prometheus_endpoint(mdbs: MongoDBSearch, should_be_accessible: bool, port: int = 9946): + pod_name = f"{mdbs.name}-search-0" + url = f"http://localhost:{port}/metrics" + + if should_be_accessible: + result = KubernetesTester.run_command_in_pod_container( + pod_name, + mdbs.namespace, + ["curl", "-f", url], + container="mongot" + ) + assert "# HELP" in result or "# TYPE" in result, f"Prometheus metrics endpoint should return valid metrics, got: {result[:200]}" + logger.info(f"Prometheus endpoint is accessible and returning metrics") + else: + try: + result = KubernetesTester.run_command_in_pod_container( + pod_name, + mdbs.namespace, + ["curl", "-f", url], + container_name="mongot" + ) + assert False, f"Prometheus endpoint should not be accessible but got: {result}" + except Exception as e: + logger.info(f"Expected failure: Prometheus endpoint is not accessible: {e}") diff --git a/helm_chart/crds/mongodb.com_mongodbsearch.yaml b/helm_chart/crds/mongodb.com_mongodbsearch.yaml index 15153ba25..ce46291f7 100644 --- a/helm_chart/crds/mongodb.com_mongodbsearch.yaml +++ b/helm_chart/crds/mongodb.com_mongodbsearch.yaml @@ -106,6 +106,17 @@ spec: type: string type: object type: object + prometheus: + description: Configure prometheus metrics endpoint in mongot. If not + set, the metrics endpoint will be disabled. + properties: + port: + description: Port where metrics endpoint will be exposed on. Defaults + to 9216. + maximum: 65535 + minimum: 0 + type: integer + type: object resourceRequirements: description: Configure resource requests and limits for the MongoDB Search pods. diff --git a/main.go b/main.go index 5e5185825..dd538fdea 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "context" "flag" "fmt" + golog "log" "os" "runtime/debug" "slices" @@ -528,8 +529,8 @@ func getOperatorEnv() util.OperatorEnvironment { operatorEnv := util.OperatorEnvironment(operatorFromEnv) if !validateOperatorEnv(operatorEnv) { operatorEnvOnce.Do(func() { - log.Infof("Configured environment %s, not recognized. Must be one of %v", operatorEnv, operatorEnvironments) - log.Infof("Using default environment, %s, instead", util.OperatorEnvironmentDev) + golog.Printf("Configured environment %s, not recognized. Must be one of %v", operatorEnv, operatorEnvironments) + golog.Printf("Using default environment, %s, instead", util.OperatorEnvironmentDev) }) operatorEnv = util.OperatorEnvironmentDev } diff --git a/public/crds.yaml b/public/crds.yaml index beeaf741d..086c2dfe4 100644 --- a/public/crds.yaml +++ b/public/crds.yaml @@ -4128,6 +4128,17 @@ spec: type: string type: object type: object + prometheus: + description: Configure prometheus metrics endpoint in mongot. If not + set, the metrics endpoint will be disabled. + properties: + port: + description: Port where metrics endpoint will be exposed on. Defaults + to 9216. + maximum: 65535 + minimum: 0 + type: integer + type: object resourceRequirements: description: Configure resource requests and limits for the MongoDB Search pods. From f17e896702550e8efed0293d49c4abf503c4e11a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Sierant?= Date: Fri, 7 Nov 2025 16:19:46 +0100 Subject: [PATCH 2/9] Updated release notes for search --- ...move_legacy_search_coordinator_polyfill.md | 6 ----- ...ature_update_mongodb_search_2nd_preview.md | 14 ++++++++++ ...mongodb_search_to_use_grpc_and_mtls_for.md | 7 ----- ...dbsearch_mongodb_deployments_using_x509.md | 7 ----- ...arch_updated_the_default_mongodbmongodb.md | 2 -- .../mongodbsearch_reconcile_helper_test.go | 4 +-- .../tests/search/search_enterprise_tls.py | 26 +++++++++++-------- main.go | 2 +- 8 files changed, 32 insertions(+), 36 deletions(-) delete mode 100644 changelog/20251015_other_remove_legacy_search_coordinator_polyfill.md create mode 100644 changelog/20251030_feature_update_mongodb_search_2nd_preview.md delete mode 100644 changelog/20251030_feature_update_mongodb_search_to_use_grpc_and_mtls_for.md delete mode 100644 changelog/20251103_feature_mongodbsearch_mongodb_deployments_using_x509.md diff --git a/changelog/20251015_other_remove_legacy_search_coordinator_polyfill.md b/changelog/20251015_other_remove_legacy_search_coordinator_polyfill.md deleted file mode 100644 index 0616cc062..000000000 --- a/changelog/20251015_other_remove_legacy_search_coordinator_polyfill.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -kind: other -date: 2025-10-15 ---- - -* Simplified MongoDB Search setup: Removed the custom Search Coordinator polyfill (a piece of compatibility code previously needed to add the required permissions), as MongoDB 8.2.0 and later now include the necessary permissions via the built-in searchCoordinator role. diff --git a/changelog/20251030_feature_update_mongodb_search_2nd_preview.md b/changelog/20251030_feature_update_mongodb_search_2nd_preview.md new file mode 100644 index 000000000..61a61849f --- /dev/null +++ b/changelog/20251030_feature_update_mongodb_search_2nd_preview.md @@ -0,0 +1,14 @@ +--- +kind: feature +date: 2025-10-30 +--- + +* **MongoDBSearch**: + * Switched to gRPC and mTLS for internal communication between mongod and mongot. + * Since MCK 1.4 the `mongod` and `mongot` processess communicated using the MongoDB Wire Protocol and used keyfile authentication. This release switches that to gRPC with mTLS authentication. gRPC will allow for load-balancing search queries against multiple `mongot` processes in the future, and mTLS decouples the internal cluster authentication mode and credentials among `mongod` processes from the connection to the `mongot` process. The Operator will automatically enable gRPC for existing and new workloads, and will enable mTLS authentication if both Database Server and `MongoDBSearch` resource are configured for TLS. + * Exposed configuration settings for mongot's prometheus metrics endpoint. + * By default, if `spec.prometheus` field is not provided then metrics endpoint in mongot is disabled. **This is a breaking change**. Previously the metrics endpoing was always enabled on port 9946. + * To enable prometheus metrics endpoint specify empty `spec.prometheus:` field. It will enable metrics endpoint on a default port (9946). To change the port, set it in `spec.prometheus.port` field. + * Simplified MongoDB Search setup: Removed the custom Search Coordinator polyfill (a piece of compatibility code previously needed to add the required permissions), as MongoDB 8.2.0 and later now include the necessary permissions via the built-in searchCoordinator role. + * Updated the default `mongodb/mongodb-search` image version to 0.55.0. This is the version MCK uses if `.spec.version` is not specified. + * MongoDB deployments using X509 internal cluster authentication are now supported. Previously MongoDB Search required SCRAM authentication among members of a MongoDB replica set. Note: SCRAM client authentication is still required, this change merely relaxes the requirements on internal cluster authentication. diff --git a/changelog/20251030_feature_update_mongodb_search_to_use_grpc_and_mtls_for.md b/changelog/20251030_feature_update_mongodb_search_to_use_grpc_and_mtls_for.md deleted file mode 100644 index 1bcecd340..000000000 --- a/changelog/20251030_feature_update_mongodb_search_to_use_grpc_and_mtls_for.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -kind: feature -date: 2025-10-30 ---- - -* **MongoDBSearch**: Switch to gRPC and mTLS for internal communication - Since MCK 1.4 the `mongod` and `mongot` processess communicated using the MongoDB Wire Protocol and used keyfile authentication. This release switches that to gRPC with mTLS authentication. gRPC will allow for load-balancing search queries against multiple `mongot` processes in the future, and mTLS decouples the internal cluster authentication mode and credentials among `mongod` processes from the connection to the `mongot` process. The Operator will automatically enable gRPC for existing and new workloads, and will enable mTLS authentication if both Database Server and `MongoDBSearch` resource are configured for TLS. \ No newline at end of file diff --git a/changelog/20251103_feature_mongodbsearch_mongodb_deployments_using_x509.md b/changelog/20251103_feature_mongodbsearch_mongodb_deployments_using_x509.md deleted file mode 100644 index 01362c4c0..000000000 --- a/changelog/20251103_feature_mongodbsearch_mongodb_deployments_using_x509.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -kind: feature -date: 2025-11-03 ---- - -* **MongoDBSearch**: MongoDB deployments using X509 internal cluster authentication are now supported. Previously MongoDB Search required SCRAM authentication among members of a MongoDB replica set. Note: SCRAM client authentication is still required, this change merely relaxes the requirements on internal cluster authentication. - diff --git a/changelog/20251106_feature_mongodbsearch_updated_the_default_mongodbmongodb.md b/changelog/20251106_feature_mongodbsearch_updated_the_default_mongodbmongodb.md index 540075ffd..583437307 100644 --- a/changelog/20251106_feature_mongodbsearch_updated_the_default_mongodbmongodb.md +++ b/changelog/20251106_feature_mongodbsearch_updated_the_default_mongodbmongodb.md @@ -2,5 +2,3 @@ kind: feature date: 2025-11-06 --- - -* **MongoDBSearch**: Updated the default `mongodb/mongodb-search` image version to 0.55.0. This is the version MCK uses if `.spec.version` is not specified. diff --git a/controllers/searchcontroller/mongodbsearch_reconcile_helper_test.go b/controllers/searchcontroller/mongodbsearch_reconcile_helper_test.go index d6885bafa..2017df5ad 100644 --- a/controllers/searchcontroller/mongodbsearch_reconcile_helper_test.go +++ b/controllers/searchcontroller/mongodbsearch_reconcile_helper_test.go @@ -3,15 +3,15 @@ package searchcontroller import ( "context" "fmt" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" "strings" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.uber.org/zap" "sigs.k8s.io/controller-runtime/pkg/client" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" searchv1 "github.com/mongodb/mongodb-kubernetes/api/v1/search" diff --git a/docker/mongodb-kubernetes-tests/tests/search/search_enterprise_tls.py b/docker/mongodb-kubernetes-tests/tests/search/search_enterprise_tls.py index 0bf5c7b93..14a46d041 100644 --- a/docker/mongodb-kubernetes-tests/tests/search/search_enterprise_tls.py +++ b/docker/mongodb-kubernetes-tests/tests/search/search_enterprise_tls.py @@ -1,5 +1,11 @@ import yaml -from kubetester import create_or_update_secret, run_periodically, try_load, get_service, kubetester +from kubetester import ( + create_or_update_secret, + get_service, + kubetester, + run_periodically, + try_load, +) from kubetester.certs import create_mongodb_tls_certs, create_tls_certs from kubetester.kubetester import KubernetesTester from kubetester.kubetester import fixture as yaml_fixture @@ -291,7 +297,9 @@ def assert_search_service_prometheus_port(mdbs: MongoDBSearch, should_exist: boo if should_exist: assert "prometheus" in ports, "Prometheus port should be in service when prometheus is enabled" - assert ports["prometheus"] == expected_port, f"Prometheus port should be {expected_port} but was {ports['prometheus']}" + assert ( + ports["prometheus"] == expected_port + ), f"Prometheus port should be {expected_port} but was {ports['prometheus']}" else: assert "prometheus" not in ports, "Prometheus port should not be in service when prometheus is disabled" @@ -302,20 +310,16 @@ def assert_search_pod_prometheus_endpoint(mdbs: MongoDBSearch, should_be_accessi if should_be_accessible: result = KubernetesTester.run_command_in_pod_container( - pod_name, - mdbs.namespace, - ["curl", "-f", url], - container="mongot" + pod_name, mdbs.namespace, ["curl", "-f", url], container="mongot" ) - assert "# HELP" in result or "# TYPE" in result, f"Prometheus metrics endpoint should return valid metrics, got: {result[:200]}" + assert ( + "# HELP" in result or "# TYPE" in result + ), f"Prometheus metrics endpoint should return valid metrics, got: {result[:200]}" logger.info(f"Prometheus endpoint is accessible and returning metrics") else: try: result = KubernetesTester.run_command_in_pod_container( - pod_name, - mdbs.namespace, - ["curl", "-f", url], - container_name="mongot" + pod_name, mdbs.namespace, ["curl", "-f", url], container_name="mongot" ) assert False, f"Prometheus endpoint should not be accessible but got: {result}" except Exception as e: diff --git a/main.go b/main.go index dd538fdea..0f4223b46 100644 --- a/main.go +++ b/main.go @@ -4,7 +4,6 @@ import ( "context" "flag" "fmt" - golog "log" "os" "runtime/debug" "slices" @@ -31,6 +30,7 @@ import ( corev1 "k8s.io/api/core/v1" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" + golog "log" localruntime "runtime" ctrl "sigs.k8s.io/controller-runtime" runtime_cluster "sigs.k8s.io/controller-runtime/pkg/cluster" From ff1dfa2bd39a1097a2381362b23aa806f3eb50c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Sierant?= Date: Fri, 7 Nov 2025 18:53:51 +0100 Subject: [PATCH 3/9] Running prom endpoint test in tools pod --- .../tests/search/search_enterprise_tls.py | 68 +++++++++++++++---- 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/docker/mongodb-kubernetes-tests/tests/search/search_enterprise_tls.py b/docker/mongodb-kubernetes-tests/tests/search/search_enterprise_tls.py index 14a46d041..99fcc763a 100644 --- a/docker/mongodb-kubernetes-tests/tests/search/search_enterprise_tls.py +++ b/docker/mongodb-kubernetes-tests/tests/search/search_enterprise_tls.py @@ -218,6 +218,11 @@ def check_mongod_parameters(): run_periodically(check_mongod_parameters, timeout=600) +@mark.e2e_search_enterprise_tls +def test_search_deploy_tools_pod(namespace: str): + deploy_mongodb_tools_pod(namespace) + + @mark.e2e_search_enterprise_tls def test_search_verify_prometheus_disabled_initially(mdbs: MongoDBSearch): assert_search_service_prometheus_port(mdbs, should_exist=False) @@ -291,36 +296,69 @@ def assert_search_service_prometheus_port(mdbs: MongoDBSearch, should_exist: boo service_name = f"{mdbs.name}-search-svc" service = get_service(mdbs.namespace, service_name) - assert service is not None, f"Service {service_name} should exist" + assert service is not None ports = {p.name: p.port for p in service.spec.ports} if should_exist: - assert "prometheus" in ports, "Prometheus port should be in service when prometheus is enabled" - assert ( - ports["prometheus"] == expected_port - ), f"Prometheus port should be {expected_port} but was {ports['prometheus']}" + assert "prometheus" in ports + assert ports["prometheus"] == expected_port else: - assert "prometheus" not in ports, "Prometheus port should not be in service when prometheus is disabled" + assert "prometheus" not in ports + + +def deploy_mongodb_tools_pod(namespace: str): + from kubetester import get_pod_when_ready + + pod_body = { + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "name": "mongodb-tools-pod", + "labels": {"app": "mongodb-tools"}, + }, + "spec": { + "containers": [ + { + "name": "mongodb-tools", + "image": "mongodb/mongodb-community-server:8.0-ubi8", + "command": ["/bin/bash", "-c"], + "args": ["sleep infinity"], + } + ], + "restartPolicy": "Never", + }, + } + + try: + KubernetesTester.create_pod(namespace, pod_body) + logger.info(f"Created mongodb-tools-pod in namespace {namespace}") + except Exception as e: + logger.info(f"Pod may already exist: {e}") + + get_pod_when_ready(namespace, "app=mongodb-tools", default_retry=60) + logger.info("mongodb-tools-pod is ready") def assert_search_pod_prometheus_endpoint(mdbs: MongoDBSearch, should_be_accessible: bool, port: int = 9946): - pod_name = f"{mdbs.name}-search-0" - url = f"http://localhost:{port}/metrics" + service_fqdn = f"{mdbs.name}-search-svc.{mdbs.namespace}.svc.cluster.local" + url = f"http://{service_fqdn}:{port}/metrics" if should_be_accessible: result = KubernetesTester.run_command_in_pod_container( - pod_name, mdbs.namespace, ["curl", "-f", url], container="mongot" + "mongodb-tools-pod", mdbs.namespace, ["curl", "-f", "-s", url], container="mongodb-tools" ) - assert ( - "# HELP" in result or "# TYPE" in result - ), f"Prometheus metrics endpoint should return valid metrics, got: {result[:200]}" - logger.info(f"Prometheus endpoint is accessible and returning metrics") + assert "# HELP" in result or "# TYPE" in result + + logger.info(f"Prometheus endpoint is accessible at {url} and returning metrics") else: try: result = KubernetesTester.run_command_in_pod_container( - pod_name, mdbs.namespace, ["curl", "-f", url], container_name="mongot" + "mongodb-tools-pod", + mdbs.namespace, + ["curl", "-f", "-s", "--max-time", "5", url], + container="mongodb-tools", ) assert False, f"Prometheus endpoint should not be accessible but got: {result}" except Exception as e: - logger.info(f"Expected failure: Prometheus endpoint is not accessible: {e}") + logger.info(f"Expected failure: Prometheus endpoint is not accessible at {url}: {e}") From d41c71ed9dabff537a46d9670b64fe8ad38a9241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Sierant?= Date: Fri, 7 Nov 2025 18:55:47 +0100 Subject: [PATCH 4/9] Running prom endpoint test in tools pod --- api/v1/search/mongodbsearch_types.go | 1 + controllers/searchcontroller/mongodbsearch_reconcile_helper.go | 1 + 2 files changed, 2 insertions(+) diff --git a/api/v1/search/mongodbsearch_types.go b/api/v1/search/mongodbsearch_types.go index b5f1fe9cb..d6a79b2db 100644 --- a/api/v1/search/mongodbsearch_types.go +++ b/api/v1/search/mongodbsearch_types.go @@ -234,6 +234,7 @@ func (s *MongoDBSearch) GetMongotMetricsPort() int32 { return MongotDefaultPrometheusPort } + //nolint:gosec return int32(s.Spec.Prometheus.Port) } diff --git a/controllers/searchcontroller/mongodbsearch_reconcile_helper.go b/controllers/searchcontroller/mongodbsearch_reconcile_helper.go index e20822942..5e1a04a02 100644 --- a/controllers/searchcontroller/mongodbsearch_reconcile_helper.go +++ b/controllers/searchcontroller/mongodbsearch_reconcile_helper.go @@ -391,6 +391,7 @@ func createMongotConfig(search *searchv1.MongoDBSearch, db SearchSourceDBResourc if prometheus := search.GetPrometheus(); prometheus != nil { port := search.GetMongotMetricsPort() if prometheus.Port != 0 { + //nolint:gosec port = int32(prometheus.Port) } config.Metrics = mongot.ConfigMetrics{ From 6ab6bd669cad6066fa9ecd21a83c49fe7e6dbe0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Sierant?= Date: Fri, 7 Nov 2025 20:57:26 +0100 Subject: [PATCH 5/9] Apply suggestion from @fealebenpae Co-authored-by: Yavor Georgiev --- api/v1/search/mongodbsearch_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/v1/search/mongodbsearch_types.go b/api/v1/search/mongodbsearch_types.go index d6a79b2db..0b561aac0 100644 --- a/api/v1/search/mongodbsearch_types.go +++ b/api/v1/search/mongodbsearch_types.go @@ -31,7 +31,7 @@ func init() { } type Prometheus struct { - // Port where metrics endpoint will be exposed on. Defaults to 9216. + // Port where metrics endpoint will be exposed on. Defaults to 9946. // +optional // +kubebuilder:validation:Minimum=0 // +kubebuilder:validation:Maximum=65535 From 6fc106718a75c80ab51d4db9f64860a11a02c0dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Sierant?= Date: Fri, 7 Nov 2025 21:49:54 +0100 Subject: [PATCH 6/9] Review fixes --- api/v1/search/mongodbsearch_types.go | 18 +++---- api/v1/search/zz_generated.deepcopy.go | 20 ++++++++ .../crd/bases/mongodb.com_mongodbsearch.yaml | 2 +- .../appdbreplicaset_controller_test.go | 5 +- .../operator/mongodbsearch_controller_test.go | 49 +++++++++++++------ .../mongodbsearch_reconcile_helper.go | 19 +++---- .../tests/search/search_enterprise_tls.py | 7 ++- .../crds/mongodb.com_mongodbsearch.yaml | 2 +- public/crds.yaml | 2 +- 9 files changed, 81 insertions(+), 43 deletions(-) diff --git a/api/v1/search/mongodbsearch_types.go b/api/v1/search/mongodbsearch_types.go index 0b561aac0..7179d3dca 100644 --- a/api/v1/search/mongodbsearch_types.go +++ b/api/v1/search/mongodbsearch_types.go @@ -38,6 +38,15 @@ type Prometheus struct { Port int `json:"port,omitempty"` } +func (p *Prometheus) GetPort() int32 { + if p.Port == 0 { + return MongotDefaultPrometheusPort + } + + //nolint:gosec + return int32(p.Port) +} + type MongoDBSearchSpec struct { // Optional version of MongoDB Search component (mongot). If not set, then the operator will set the most appropriate version of MongoDB Search. // +optional @@ -229,15 +238,6 @@ func (s *MongoDBSearch) GetMongotGrpcPort() int32 { return MongotDefaultGrpcPort } -func (s *MongoDBSearch) GetMongotMetricsPort() int32 { - if s.Spec.Prometheus == nil || s.Spec.Prometheus.Port == 0 { - return MongotDefaultPrometheusPort - } - - //nolint:gosec - return int32(s.Spec.Prometheus.Port) -} - // TLSSecretNamespacedName will get the namespaced name of the Secret containing the server certificate and key func (s *MongoDBSearch) TLSSecretNamespacedName() types.NamespacedName { return types.NamespacedName{Name: s.Spec.Security.TLS.CertificateKeySecret.Name, Namespace: s.Namespace} diff --git a/api/v1/search/zz_generated.deepcopy.go b/api/v1/search/zz_generated.deepcopy.go index d18d025f8..2064e07c4 100644 --- a/api/v1/search/zz_generated.deepcopy.go +++ b/api/v1/search/zz_generated.deepcopy.go @@ -160,6 +160,11 @@ func (in *MongoDBSearchSpec) DeepCopyInto(out *MongoDBSearchSpec) { (*in).DeepCopyInto(*out) } in.Security.DeepCopyInto(&out.Security) + if in.Prometheus != nil { + in, out := &in.Prometheus, &out.Prometheus + *out = new(Prometheus) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MongoDBSearchSpec. @@ -228,6 +233,21 @@ func (in *MongoDBSource) DeepCopy() *MongoDBSource { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Prometheus) DeepCopyInto(out *Prometheus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Prometheus. +func (in *Prometheus) DeepCopy() *Prometheus { + if in == nil { + return nil + } + out := new(Prometheus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Security) DeepCopyInto(out *Security) { *out = *in diff --git a/config/crd/bases/mongodb.com_mongodbsearch.yaml b/config/crd/bases/mongodb.com_mongodbsearch.yaml index ce46291f7..eacc0e71d 100644 --- a/config/crd/bases/mongodb.com_mongodbsearch.yaml +++ b/config/crd/bases/mongodb.com_mongodbsearch.yaml @@ -112,7 +112,7 @@ spec: properties: port: description: Port where metrics endpoint will be exposed on. Defaults - to 9216. + to 9946. maximum: 65535 minimum: 0 type: integer diff --git a/controllers/operator/appdbreplicaset_controller_test.go b/controllers/operator/appdbreplicaset_controller_test.go index 947e40218..951603ea1 100644 --- a/controllers/operator/appdbreplicaset_controller_test.go +++ b/controllers/operator/appdbreplicaset_controller_test.go @@ -3,10 +3,10 @@ package operator import ( "context" "encoding/json" + "errors" "fmt" "os" "path/filepath" - "strings" "testing" "time" @@ -54,7 +54,6 @@ func init() { // getReleaseJsonPath searches for a specified target directory by traversing the directory tree backwards from the current working directory func getReleaseJsonPath() (string, error) { - repositoryRootDirName := "mongodb-kubernetes" releaseFileName := "release.json" currentDir, err := os.Getwd() @@ -62,7 +61,7 @@ func getReleaseJsonPath() (string, error) { return "", err } for currentDir != "/" { - if strings.HasSuffix(currentDir, repositoryRootDirName) { + if _, err := os.Stat(filepath.Join(currentDir, releaseFileName)); !errors.Is(err, os.ErrNotExist) { return filepath.Join(currentDir, releaseFileName), nil } currentDir = filepath.Dir(currentDir) diff --git a/controllers/operator/mongodbsearch_controller_test.go b/controllers/operator/mongodbsearch_controller_test.go index 904424df5..ad1fc7a14 100644 --- a/controllers/operator/mongodbsearch_controller_test.go +++ b/controllers/operator/mongodbsearch_controller_test.go @@ -8,6 +8,7 @@ import ( "github.com/ghodss/yaml" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/types" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" @@ -22,12 +23,12 @@ import ( "github.com/mongodb/mongodb-kubernetes/api/v1/status" userv1 "github.com/mongodb/mongodb-kubernetes/api/v1/user" "github.com/mongodb/mongodb-kubernetes/controllers/operator/mock" - "github.com/mongodb/mongodb-kubernetes/controllers/operator/workflow" "github.com/mongodb/mongodb-kubernetes/controllers/searchcontroller" mdbcv1 "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/api/v1" "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/api/v1/common" "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/mongot" "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/util/constants" + "github.com/mongodb/mongodb-kubernetes/pkg/util" ) func newMongoDBCommunity(name, namespace string) *mdbcv1.MongoDBCommunity { @@ -135,10 +136,6 @@ func buildExpectedMongotConfig(search *searchv1.MongoDBSearch, mdbc *mdbcv1.Mong }, Wireproto: wireprotoServer, }, - Metrics: mongot.ConfigMetrics{ - Enabled: true, - Address: fmt.Sprintf("0.0.0.0:%d", search.GetMongotMetricsPort()), - }, HealthCheck: mongot.ConfigHealthCheck{ Address: fmt.Sprintf("0.0.0.0:%d", search.GetMongotHealthCheckPort()), }, @@ -205,22 +202,16 @@ func TestMongoDBSearchReconcile_Success(t *testing.T) { mdbc := newMongoDBCommunity("mdb", mock.TestNamespace) reconciler, c := newSearchReconciler(mdbc, search) - res, err := reconciler.Reconcile( - ctx, - reconcile.Request{NamespacedName: types.NamespacedName{Name: search.Name, Namespace: search.Namespace}}, - ) - expected, _ := workflow.OK().ReconcileResult() - assert.NoError(t, err) - assert.Equal(t, expected, res) + checkSearchReconcileSuccessful(ctx, t, reconciler, c, search) svc := &corev1.Service{} - err = c.Get(ctx, search.SearchServiceNamespacedName(), svc) + err := c.Get(ctx, search.SearchServiceNamespacedName(), svc) assert.NoError(t, err) servicePortNames := []string{} for _, port := range svc.Spec.Ports { servicePortNames = append(servicePortNames, port.Name) } - expectedPortNames := []string{"mongot-grpc", "metrics", "healthcheck"} + expectedPortNames := []string{"mongot-grpc", "healthcheck"} if tc.withWireproto { expectedPortNames = append(expectedPortNames, "mongot-wireproto") } @@ -254,7 +245,7 @@ func checkSearchReconcileFailed( reconcile.Request{NamespacedName: types.NamespacedName{Name: search.Name, Namespace: search.Namespace}}, ) assert.NoError(t, err) - assert.True(t, res.RequeueAfter > 0) + assert.Less(t, res.RequeueAfter, util.TWENTY_FOUR_HOURS) updated := &searchv1.MongoDBSearch{} assert.NoError(t, c.Get(ctx, types.NamespacedName{Name: search.Name, Namespace: search.Namespace}, updated)) @@ -262,6 +253,34 @@ func checkSearchReconcileFailed( assert.Contains(t, updated.Status.Message, expectedMsg) } +// checkSearchReconcileSuccessful performs reconcile to check if it gets to a Running state. +// In case it's a first reconcile and still Pending it's retried with mocked sts simulated as ready. +func checkSearchReconcileSuccessful( + ctx context.Context, + t *testing.T, + reconciler *MongoDBSearchReconciler, + c client.Client, + search *searchv1.MongoDBSearch, +) { + namespacedName := types.NamespacedName{Name: search.Name, Namespace: search.Namespace} + res, err := reconciler.Reconcile(ctx, reconcile.Request{NamespacedName: namespacedName}) + require.NoError(t, err) + mdbs := &searchv1.MongoDBSearch{} + require.NoError(t, c.Get(ctx, namespacedName, mdbs)) + if mdbs.Status.Phase == status.PhasePending { + // mark mocked search statefulset as ready to not return Pending this time + require.NoError(t, mock.MarkAllStatefulSetsAsReady(ctx, search.Namespace, c)) + + res, err = reconciler.Reconcile(ctx, reconcile.Request{NamespacedName: namespacedName}) + require.NoError(t, err) + mdbs = &searchv1.MongoDBSearch{} + require.NoError(t, c.Get(ctx, namespacedName, mdbs)) + } + + require.Equal(t, util.TWENTY_FOUR_HOURS, res.RequeueAfter) + require.Equal(t, status.PhaseRunning, mdbs.Status.Phase) +} + func TestMongoDBSearchReconcile_InvalidVersion(t *testing.T) { ctx := context.Background() search := newMongoDBSearch("search", mock.TestNamespace, "mdb") diff --git a/controllers/searchcontroller/mongodbsearch_reconcile_helper.go b/controllers/searchcontroller/mongodbsearch_reconcile_helper.go index 5e1a04a02..1548850e8 100644 --- a/controllers/searchcontroller/mongodbsearch_reconcile_helper.go +++ b/controllers/searchcontroller/mongodbsearch_reconcile_helper.go @@ -331,12 +331,12 @@ func buildSearchHeadlessService(search *searchv1.MongoDBSearch) corev1.Service { TargetPort: intstr.FromInt32(search.GetMongotGrpcPort()), }) - if search.GetPrometheus() != nil { + if prometheus := search.GetPrometheus(); prometheus != nil { serviceBuilder.AddPort(&corev1.ServicePort{ Name: "prometheus", Protocol: corev1.ProtocolTCP, - Port: search.GetMongotMetricsPort(), - TargetPort: intstr.FromInt32(search.GetMongotMetricsPort()), + Port: prometheus.GetPort(), + TargetPort: intstr.FromInt32(prometheus.GetPort()), }) } @@ -389,14 +389,9 @@ func createMongotConfig(search *searchv1.MongoDBSearch, db SearchSourceDBResourc } if prometheus := search.GetPrometheus(); prometheus != nil { - port := search.GetMongotMetricsPort() - if prometheus.Port != 0 { - //nolint:gosec - port = int32(prometheus.Port) - } config.Metrics = mongot.ConfigMetrics{ Enabled: true, - Address: fmt.Sprintf("0.0.0.0:%d", port), + Address: fmt.Sprintf("0.0.0.0:%d", prometheus.GetPort()), } } @@ -484,9 +479,9 @@ func (r *MongoDBSearchReconcileHelper) getMongotImage() string { return "" } - for _, container := range r.mdbSearch.Spec.StatefulSetConfiguration.SpecWrapper.Spec.Template.Spec.Containers { - if container.Name == MongotContainerName { - return container.Image + for _, c := range r.mdbSearch.Spec.StatefulSetConfiguration.SpecWrapper.Spec.Template.Spec.Containers { + if c.Name == MongotContainerName { + return c.Image } } diff --git a/docker/mongodb-kubernetes-tests/tests/search/search_enterprise_tls.py b/docker/mongodb-kubernetes-tests/tests/search/search_enterprise_tls.py index 99fcc763a..117c1e69d 100644 --- a/docker/mongodb-kubernetes-tests/tests/search/search_enterprise_tls.py +++ b/docker/mongodb-kubernetes-tests/tests/search/search_enterprise_tls.py @@ -295,7 +295,6 @@ def get_user_sample_movies_helper(mdb): def assert_search_service_prometheus_port(mdbs: MongoDBSearch, should_exist: bool, expected_port: int = 9946): service_name = f"{mdbs.name}-search-svc" service = get_service(mdbs.namespace, service_name) - assert service is not None ports = {p.name: p.port for p in service.spec.ports} @@ -308,6 +307,10 @@ def assert_search_service_prometheus_port(mdbs: MongoDBSearch, should_exist: boo def deploy_mongodb_tools_pod(namespace: str): + """ + Deploys a bastion pod to perform connectivty checks using pod exec. It's similar to how we + run connectivity checks in snippets. + """ from kubetester import get_pod_when_ready pod_body = { @@ -345,6 +348,8 @@ def assert_search_pod_prometheus_endpoint(mdbs: MongoDBSearch, should_be_accessi url = f"http://{service_fqdn}:{port}/metrics" if should_be_accessible: + # We don't necessarily need the connectivity test to run via a bastion pod as we could connect to it directly when running test in pod. + # But it's not requiring forwarding when running locally. result = KubernetesTester.run_command_in_pod_container( "mongodb-tools-pod", mdbs.namespace, ["curl", "-f", "-s", url], container="mongodb-tools" ) diff --git a/helm_chart/crds/mongodb.com_mongodbsearch.yaml b/helm_chart/crds/mongodb.com_mongodbsearch.yaml index ce46291f7..eacc0e71d 100644 --- a/helm_chart/crds/mongodb.com_mongodbsearch.yaml +++ b/helm_chart/crds/mongodb.com_mongodbsearch.yaml @@ -112,7 +112,7 @@ spec: properties: port: description: Port where metrics endpoint will be exposed on. Defaults - to 9216. + to 9946. maximum: 65535 minimum: 0 type: integer diff --git a/public/crds.yaml b/public/crds.yaml index 086c2dfe4..b9b9a94ad 100644 --- a/public/crds.yaml +++ b/public/crds.yaml @@ -4134,7 +4134,7 @@ spec: properties: port: description: Port where metrics endpoint will be exposed on. Defaults - to 9216. + to 9946. maximum: 65535 minimum: 0 type: integer From 7a7aabc284637b11f0751ca48774dea728c2fb94 Mon Sep 17 00:00:00 2001 From: Anand Singh Date: Mon, 10 Nov 2025 17:56:02 +0100 Subject: [PATCH 7/9] update default --- api/v1/search/mongodbsearch_types.go | 1 + 1 file changed, 1 insertion(+) diff --git a/api/v1/search/mongodbsearch_types.go b/api/v1/search/mongodbsearch_types.go index 7179d3dca..74ff481a1 100644 --- a/api/v1/search/mongodbsearch_types.go +++ b/api/v1/search/mongodbsearch_types.go @@ -35,6 +35,7 @@ type Prometheus struct { // +optional // +kubebuilder:validation:Minimum=0 // +kubebuilder:validation:Maximum=65535 + // +kubebuilder:default=9946 Port int `json:"port,omitempty"` } From 58caaa4ba46bba5a04cb4c5eae6806d5f517e636 Mon Sep 17 00:00:00 2001 From: Anand Singh Date: Mon, 10 Nov 2025 19:30:00 +0100 Subject: [PATCH 8/9] add manifest update --- config/crd/bases/mongodb.com_mongodbsearch.yaml | 1 + helm_chart/crds/mongodb.com_mongodbsearch.yaml | 1 + public/crds.yaml | 1 + 3 files changed, 3 insertions(+) diff --git a/config/crd/bases/mongodb.com_mongodbsearch.yaml b/config/crd/bases/mongodb.com_mongodbsearch.yaml index eacc0e71d..bf3839cb8 100644 --- a/config/crd/bases/mongodb.com_mongodbsearch.yaml +++ b/config/crd/bases/mongodb.com_mongodbsearch.yaml @@ -111,6 +111,7 @@ spec: set, the metrics endpoint will be disabled. properties: port: + default: 9946 description: Port where metrics endpoint will be exposed on. Defaults to 9946. maximum: 65535 diff --git a/helm_chart/crds/mongodb.com_mongodbsearch.yaml b/helm_chart/crds/mongodb.com_mongodbsearch.yaml index eacc0e71d..bf3839cb8 100644 --- a/helm_chart/crds/mongodb.com_mongodbsearch.yaml +++ b/helm_chart/crds/mongodb.com_mongodbsearch.yaml @@ -111,6 +111,7 @@ spec: set, the metrics endpoint will be disabled. properties: port: + default: 9946 description: Port where metrics endpoint will be exposed on. Defaults to 9946. maximum: 65535 diff --git a/public/crds.yaml b/public/crds.yaml index b9b9a94ad..9d2019ebd 100644 --- a/public/crds.yaml +++ b/public/crds.yaml @@ -4133,6 +4133,7 @@ spec: set, the metrics endpoint will be disabled. properties: port: + default: 9946 description: Port where metrics endpoint will be exposed on. Defaults to 9946. maximum: 65535 From 398dda21082b26a700ef8aac5d17a685266c7ee6 Mon Sep 17 00:00:00 2001 From: Anand Singh Date: Mon, 10 Nov 2025 19:32:24 +0100 Subject: [PATCH 9/9] remove extraneous default port --- api/v1/search/mongodbsearch_types.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/api/v1/search/mongodbsearch_types.go b/api/v1/search/mongodbsearch_types.go index 376888563..3240a25ff 100644 --- a/api/v1/search/mongodbsearch_types.go +++ b/api/v1/search/mongodbsearch_types.go @@ -40,10 +40,6 @@ type Prometheus struct { } func (p *Prometheus) GetPort() int32 { - if p.Port == 0 { - return MongotDefaultPrometheusPort - } - //nolint:gosec return int32(p.Port) }