Skip to content

Commit 167e26b

Browse files
authored
Handle new mongot config schema (#230)
# Summary Implements minimum necessary changes to run against newest mongot container built from master: * Workaround for mongot's requirement of password file having owner-only permissions * New config schema structure * Switched default mongot image to ECR * Simulated searchCoordinated role
1 parent 18e23d3 commit 167e26b

22 files changed

+394
-98
lines changed

api/v1/search/mongodbsearch_types.go

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package search
22

33
import (
4+
"fmt"
5+
46
"k8s.io/apimachinery/pkg/runtime/schema"
57
"k8s.io/apimachinery/pkg/types"
68

@@ -14,8 +16,9 @@ import (
1416
)
1517

1618
const (
17-
MongotDefaultPort = 27027
18-
MongotDefaultMetricsPort = 9946
19+
MongotDefaultPort = 27027
20+
MongotDefaultMetricsPort = 9946
21+
MongotDefaultSyncSourceUsername = "mongot-user"
1922
)
2023

2124
func init() {
@@ -38,6 +41,10 @@ type MongoDBSearchSpec struct {
3841
type MongoDBSource struct {
3942
// +optional
4043
MongoDBResourceRef *userv1.MongoDBResourceRef `json:"mongodbResourceRef,omitempty"`
44+
// +optional
45+
PasswordSecretRef *userv1.SecretKeyRef `json:"passwordSecretRef,omitempty"`
46+
// +optional
47+
Username *string `json:"username,omitempty"`
4148
}
4249

4350
type MongoDBSearchStatus struct {
@@ -105,6 +112,25 @@ func (s *MongoDBSearch) MongotConfigConfigMapNamespacedName() types.NamespacedNa
105112
return types.NamespacedName{Name: s.Name + "-search-config", Namespace: s.Namespace}
106113
}
107114

115+
func (s *MongoDBSearch) SourceUserPasswordSecretRef() *userv1.SecretKeyRef {
116+
if s.Spec.Source != nil && s.Spec.Source.PasswordSecretRef != nil {
117+
return s.Spec.Source.PasswordSecretRef
118+
}
119+
120+
return &userv1.SecretKeyRef{
121+
Name: fmt.Sprintf("%s-%s-password", s.Name, MongotDefaultSyncSourceUsername),
122+
Key: "password",
123+
}
124+
}
125+
126+
func (s *MongoDBSearch) SourceUsername() string {
127+
if s.Spec.Source != nil && s.Spec.Source.Username != nil {
128+
return *s.Spec.Source.Username
129+
}
130+
131+
return MongotDefaultSyncSourceUsername
132+
}
133+
108134
func (s *MongoDBSearch) StatefulSetNamespacedName() types.NamespacedName {
109135
return types.NamespacedName{Name: s.Name + "-search", Namespace: s.Namespace}
110136
}

config/crd/bases/mongodb.com_mongodbsearch.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,21 @@ spec:
160160
required:
161161
- name
162162
type: object
163+
passwordSecretRef:
164+
description: |-
165+
SecretKeyRef is a reference to a value in a given secret in the same
166+
namespace. Based on:
167+
https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.15/#secretkeyselector-v1-core
168+
properties:
169+
key:
170+
type: string
171+
name:
172+
type: string
173+
required:
174+
- name
175+
type: object
176+
username:
177+
type: string
163178
type: object
164179
statefulSet:
165180
description: |-

config/manager/manager.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -392,9 +392,9 @@ spec:
392392
- name: RELATED_IMAGE_MONGODB_IMAGE_8_0_0_ubi9
393393
value: "quay.io/mongodb/mongodb-enterprise-server:8.0.0-ubi9"
394394
- name: RELATED_IMAGE_MDB_SEARCH_IMAGE_1_47_0
395-
value: "quay.io/mongodb/mongodb-search-community:1.47.0"
395+
value: "268558157000.dkr.ecr.us-east-1.amazonaws.com/dev/mongodb-search-community:1.47.0"
396396
- name: MDB_SEARCH_COMMUNITY_REPO_URL
397-
value: "quay.io/mongodb"
397+
value: "268558157000.dkr.ecr.us-east-1.amazonaws.com/dev"
398398
- name: MDB_SEARCH_COMMUNITY_NAME
399399
value: "mongodb-search-community"
400400
- name: MDB_SEARCH_COMMUNITY_VERSION

controllers/operator/mongodbsearch_controller_test.go

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/stretchr/testify/assert"
1010
"k8s.io/apimachinery/pkg/types"
1111
"k8s.io/client-go/util/workqueue"
12+
"k8s.io/utils/ptr"
1213
"sigs.k8s.io/controller-runtime/pkg/client"
1314
"sigs.k8s.io/controller-runtime/pkg/controller/controllertest"
1415
"sigs.k8s.io/controller-runtime/pkg/event"
@@ -73,21 +74,42 @@ func newSearchReconciler(
7374
}
7475

7576
func buildExpectedMongotConfig(search *searchv1.MongoDBSearch, mdbc *mdbcv1.MongoDBCommunity) mongot.Config {
76-
return mongot.Config{CommunityPrivatePreview: mongot.CommunityPrivatePreview{
77-
MongodHostAndPort: fmt.Sprintf(
78-
"%s.%s.svc.cluster.local:%d",
79-
mdbc.ServiceName(), mdbc.Namespace,
80-
mdbc.GetMongodConfiguration().GetDBPort(),
81-
),
82-
QueryServerAddress: fmt.Sprintf("localhost:%d", search.GetMongotPort()),
83-
KeyFilePath: "/mongot/keyfile/keyfile",
84-
DataPath: "/mongot/data/config.yml",
85-
Metrics: mongot.Metrics{
77+
return mongot.Config{
78+
SyncSource: mongot.ConfigSyncSource{
79+
ReplicaSet: mongot.ConfigReplicaSet{
80+
HostAndPort: fmt.Sprintf("%s.%s.svc.cluster.local:%d", mdbc.Name+"-svc", search.Namespace, 27017),
81+
Username: "mongot-user",
82+
PasswordFile: "/tmp/sourceUserPassword",
83+
TLS: ptr.To(false),
84+
ReadPreference: ptr.To("secondaryPreferred"),
85+
ReplicaSetName: "mdb",
86+
},
87+
},
88+
Storage: mongot.ConfigStorage{
89+
DataPath: "/mongot/data/config.yml",
90+
},
91+
Server: mongot.ConfigServer{
92+
Wireproto: &mongot.ConfigWireproto{
93+
Address: "0.0.0.0:27027",
94+
Authentication: &mongot.ConfigAuthentication{
95+
Mode: "keyfile",
96+
KeyFile: "/tmp/keyfile",
97+
},
98+
TLS: mongot.ConfigTLS{Mode: "disabled"},
99+
},
100+
},
101+
Metrics: mongot.ConfigMetrics{
86102
Enabled: true,
87-
Address: fmt.Sprintf("localhost:%d", search.GetMongotMetricsPort()),
103+
Address: fmt.Sprintf("localhost:%d", searchv1.MongotDefaultMetricsPort),
104+
},
105+
HealthCheck: mongot.ConfigHealthCheck{
106+
Address: "0.0.0.0:8080",
88107
},
89-
Logging: mongot.Logging{Verbosity: "DEBUG"},
90-
}}
108+
Logging: mongot.ConfigLogging{
109+
Verbosity: "TRACE",
110+
LogPath: nil,
111+
},
112+
}
91113
}
92114

93115
func TestMongoDBSearchReconcile_NotFound(t *testing.T) {

controllers/search_controller/mongodbsearch_reconcile_helper.go

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"golang.org/x/xerrors"
1414
"k8s.io/apimachinery/pkg/fields"
1515
"k8s.io/apimachinery/pkg/util/intstr"
16+
"k8s.io/utils/ptr"
1617
"sigs.k8s.io/controller-runtime/pkg/client"
1718
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1819

@@ -207,26 +208,50 @@ func buildSearchHeadlessService(search *searchv1.MongoDBSearch) corev1.Service {
207208
}
208209

209210
func createMongotConfig(search *searchv1.MongoDBSearch, db SearchSourceDBResource) mongot.Config {
210-
return mongot.Config{CommunityPrivatePreview: mongot.CommunityPrivatePreview{
211-
MongodHostAndPort: fmt.Sprintf("%s.%s.svc.cluster.local:%d", db.DatabaseServiceName(), db.GetNamespace(), db.DatabasePort()),
212-
QueryServerAddress: fmt.Sprintf("localhost:%d", search.GetMongotPort()),
213-
KeyFilePath: "/mongot/keyfile/keyfile",
214-
DataPath: "/mongot/data/config.yml",
215-
Metrics: mongot.Metrics{
211+
return mongot.Config{
212+
SyncSource: mongot.ConfigSyncSource{
213+
ReplicaSet: mongot.ConfigReplicaSet{
214+
HostAndPort: fmt.Sprintf("%s.%s.svc.cluster.local:%d", db.DatabaseServiceName(), db.GetNamespace(), db.DatabasePort()),
215+
Username: search.SourceUsername(),
216+
PasswordFile: "/tmp/sourceUserPassword",
217+
TLS: ptr.To(false),
218+
ReadPreference: ptr.To("secondaryPreferred"),
219+
ReplicaSetName: db.Name(),
220+
},
221+
},
222+
Storage: mongot.ConfigStorage{
223+
DataPath: "/mongot/data/config.yml",
224+
},
225+
Server: mongot.ConfigServer{
226+
Wireproto: &mongot.ConfigWireproto{
227+
Address: "0.0.0.0:27027",
228+
Authentication: &mongot.ConfigAuthentication{
229+
Mode: "keyfile",
230+
KeyFile: "/tmp/keyfile",
231+
},
232+
TLS: mongot.ConfigTLS{Mode: "disabled"},
233+
},
234+
},
235+
Metrics: mongot.ConfigMetrics{
216236
Enabled: true,
217237
Address: fmt.Sprintf("localhost:%d", search.GetMongotMetricsPort()),
218238
},
219-
Logging: mongot.Logging{
220-
Verbosity: "DEBUG",
239+
HealthCheck: mongot.ConfigHealthCheck{
240+
Address: "0.0.0.0:8080",
221241
},
222-
}}
242+
Logging: mongot.ConfigLogging{
243+
Verbosity: "TRACE",
244+
LogPath: nil,
245+
},
246+
}
223247
}
224248

225249
func GetMongodConfigParameters(search *searchv1.MongoDBSearch) map[string]interface{} {
226250
return map[string]interface{}{
227251
"setParameter": map[string]interface{}{
228-
"mongotHost": mongotHostAndPort(search),
229-
"searchIndexManagementHostAndPort": mongotHostAndPort(search),
252+
"mongotHost": mongotHostAndPort(search),
253+
"searchIndexManagementHostAndPort": mongotHostAndPort(search),
254+
"skipAuthenticationToSearchIndexManagementServer": false,
230255
},
231256
}
232257
}

controllers/search_controller/search_construction.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,18 @@ func CreateSearchStatefulSetFunc(mdbSearch *searchv1.MongoDBSearch, sourceDBReso
9595

9696
dataVolumeName := "data"
9797
keyfileVolumeName := "keyfile"
98+
sourceUserPasswordVolumeName := "password"
9899
mongotConfigVolumeName := "config"
99100

100101
pvcVolumeMount := statefulset.CreateVolumeMount(dataVolumeName, "/mongot/data", statefulset.WithSubPath("data"))
101102

102-
keyfileVolume := statefulset.CreateVolumeFromSecret("keyfile", sourceDBResource.KeyfileSecretName())
103+
keyfileVolume := statefulset.CreateVolumeFromSecret(keyfileVolumeName, sourceDBResource.KeyfileSecretName())
103104
keyfileVolumeMount := statefulset.CreateVolumeMount(keyfileVolumeName, "/mongot/keyfile", statefulset.WithReadOnly(true))
104105

105-
mongotConfigVolume := statefulset.CreateVolumeFromConfigMap("config", mdbSearch.MongotConfigConfigMapNamespacedName().Name)
106+
sourceUserPasswordVolume := statefulset.CreateVolumeFromSecret(sourceUserPasswordVolumeName, mdbSearch.SourceUserPasswordSecretRef().Name)
107+
sourceUserPasswordVolumeMount := statefulset.CreateVolumeMount(sourceUserPasswordVolumeName, "/mongot/sourceUserPassword", statefulset.WithReadOnly(true))
108+
109+
mongotConfigVolume := statefulset.CreateVolumeFromConfigMap(mongotConfigVolumeName, mdbSearch.MongotConfigConfigMapNamespacedName().Name)
106110
mongotConfigVolumeMount := statefulset.CreateVolumeMount(mongotConfigVolumeName, "/mongot/config", statefulset.WithReadOnly(true))
107111

108112
var persistenceConfig *common.PersistenceConfig
@@ -120,12 +124,14 @@ func CreateSearchStatefulSetFunc(mdbSearch *searchv1.MongoDBSearch, sourceDBReso
120124
keyfileVolumeMount,
121125
tmpVolumeMount,
122126
mongotConfigVolumeMount,
127+
sourceUserPasswordVolumeMount,
123128
}
124129

125130
volumes := []corev1.Volume{
126131
tmpVolume,
127132
keyfileVolume,
128133
mongotConfigVolume,
134+
sourceUserPasswordVolume,
129135
}
130136

131137
stsModifications := []statefulset.Modification{
@@ -180,7 +186,17 @@ func mongodbSearchContainer(mdbSearch *searchv1.MongoDBSearch, volumeMounts []co
180186
container.WithCommand([]string{"sh"}),
181187
container.WithArgs([]string{
182188
"-c",
183-
"/mongot-community/mongot --config /mongot/config/config.yml",
189+
`
190+
cp /mongot/keyfile/keyfile /tmp/keyfile
191+
chown 2000:2000 /tmp/keyfile
192+
chmod 0600 /tmp/keyfile
193+
194+
cp /mongot/sourceUserPassword/password /tmp/sourceUserPassword
195+
chown 2000:2000 /tmp/sourceUserPassword
196+
chmod 0600 /tmp/sourceUserPassword
197+
198+
/mongot-community/mongot --config /mongot/config/config.yml
199+
`,
184200
}),
185201
containerSecurityContext,
186202
)

docker/mongodb-kubernetes-tests/kubetester/helm.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ def helm_repo_add(repo_name: str, url: str):
120120

121121

122122
def process_run_and_check(args, **kwargs):
123+
if "check" not in kwargs:
124+
kwargs["check"] = True
125+
123126
try:
124127
logger.debug(f"subprocess.run: {args}")
125128
completed_process = subprocess.run(args, **kwargs)

docker/mongodb-kubernetes-tests/tests/common/search/movies_search_helper.py

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
import logging
2+
3+
import pymongo.errors
4+
5+
from kubetester import kubetester
16
from tests import test_logger
27
from tests.common.search.search_tester import SearchTester
38

@@ -26,9 +31,33 @@ def create_search_index(self):
2631
def wait_for_search_indexes(self):
2732
self.search_tester.wait_for_search_indexes_ready(self.db_name, self.col_name)
2833

29-
def assert_search_query(self):
30-
# sample taken from: https://www.mongodb.com/docs/atlas/atlas-search/tutorial/run-query/#run-a-complex-query-on-the-movies-collection-7
31-
result = self.search_tester.client[self.db_name][self.col_name].aggregate(
34+
def assert_search_query(self, retry_timeout: int = 1):
35+
def wait_for_search_results():
36+
# sample taken from: https://www.mongodb.com/docs/atlas/atlas-search/tutorial/run-query/#run-a-complex-query-on-the-movies-collection-7
37+
count = 0
38+
status_msg = ""
39+
try:
40+
result = self.execute_example_search_query()
41+
status_msg = f"{self.db_name}/{self.col_name}: search query results:\n"
42+
for r in result:
43+
status_msg += f"{r}\n"
44+
count += 1
45+
status_msg += f"Count: {count}"
46+
logger.debug(status_msg)
47+
except pymongo.errors.PyMongoError as e:
48+
logger.debug(f"error: {e}")
49+
50+
return count == 4, status_msg
51+
52+
kubetester.run_periodically(
53+
fn=wait_for_search_results,
54+
timeout=retry_timeout,
55+
sleep_time=1,
56+
msg="Search query to return correct data",
57+
)
58+
59+
def execute_example_search_query(self):
60+
return self.search_tester.client[self.db_name][self.col_name].aggregate(
3261
[
3362
{
3463
"$search": {
@@ -47,11 +76,3 @@ def assert_search_query(self):
4776
{"$project": {"title": 1, "plot": 1, "genres": 1, "_id": 0}},
4877
]
4978
)
50-
51-
logger.debug(f"{self.db_name}/{self.col_name}: search query results:")
52-
count = 0
53-
for r in result:
54-
logger.debug(r)
55-
count += 1
56-
57-
assert count == 4

docker/mongodb-kubernetes-tests/tests/common/search/search_tester.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ def wait_for_search_indexes_ready(self, database_name: str, collection_name: str
4949

5050
def search_indexes_ready(self, database_name: str, collection_name: str):
5151
search_indexes = self.get_search_indexes(database_name, collection_name)
52+
if len(search_indexes) == 0:
53+
logger.error(f"There are no search indexes available in {database_name}.{collection_name}")
54+
return False
55+
5256
for idx in search_indexes:
5357
if idx.get("status") != "READY":
5458
logger.debug(f"{database_name}/{collection_name}: search index {idx} is not ready")

0 commit comments

Comments
 (0)