@@ -4,12 +4,16 @@ import (
4
4
"context"
5
5
"fmt"
6
6
7
+ "github.com/blang/semver"
7
8
"go.uber.org/zap"
8
9
"golang.org/x/xerrors"
9
10
"k8s.io/apimachinery/pkg/api/errors"
11
+ "k8s.io/apimachinery/pkg/fields"
10
12
"k8s.io/apimachinery/pkg/runtime"
13
+ "k8s.io/apimachinery/pkg/types"
11
14
"sigs.k8s.io/controller-runtime/pkg/client"
12
15
"sigs.k8s.io/controller-runtime/pkg/controller"
16
+ "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
13
17
"sigs.k8s.io/controller-runtime/pkg/event"
14
18
"sigs.k8s.io/controller-runtime/pkg/handler"
15
19
"sigs.k8s.io/controller-runtime/pkg/manager"
@@ -22,6 +26,7 @@ import (
22
26
23
27
mdbv1 "github.com/mongodb/mongodb-kubernetes/api/v1/mdb"
24
28
rolev1 "github.com/mongodb/mongodb-kubernetes/api/v1/role"
29
+ searchv1 "github.com/mongodb/mongodb-kubernetes/api/v1/search"
25
30
mdbstatus "github.com/mongodb/mongodb-kubernetes/api/v1/status"
26
31
"github.com/mongodb/mongodb-kubernetes/controllers/om"
27
32
"github.com/mongodb/mongodb-kubernetes/controllers/om/backup"
@@ -39,6 +44,7 @@ import (
39
44
"github.com/mongodb/mongodb-kubernetes/controllers/operator/recovery"
40
45
"github.com/mongodb/mongodb-kubernetes/controllers/operator/watch"
41
46
"github.com/mongodb/mongodb-kubernetes/controllers/operator/workflow"
47
+ "github.com/mongodb/mongodb-kubernetes/controllers/search_controller"
42
48
mcoConstruct "github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/controllers/construct"
43
49
"github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/kube/annotations"
44
50
"github.com/mongodb/mongodb-kubernetes/mongodb-community-operator/pkg/kube/configmap"
@@ -52,6 +58,7 @@ import (
52
58
"github.com/mongodb/mongodb-kubernetes/pkg/util/architectures"
53
59
"github.com/mongodb/mongodb-kubernetes/pkg/util/env"
54
60
util_int "github.com/mongodb/mongodb-kubernetes/pkg/util/int"
61
+ "github.com/mongodb/mongodb-kubernetes/pkg/util/maputil"
55
62
"github.com/mongodb/mongodb-kubernetes/pkg/vault"
56
63
"github.com/mongodb/mongodb-kubernetes/pkg/vault/vaultwatcher"
57
64
)
@@ -219,6 +226,8 @@ func (r *ReconcileMongoDbReplicaSet) Reconcile(ctx context.Context, request reco
219
226
return r .updateStatus (ctx , rs , workflow .Failed (xerrors .Errorf ("Failed to reconcileHostnameOverrideConfigMap: %w" , err )), log )
220
227
}
221
228
229
+ shouldMirrorKeyfile := r .applySearchOverrides (ctx , rs , log )
230
+
222
231
sts := construct .DatabaseStatefulSet (* rs , rsConfig , log )
223
232
if status := r .ensureRoles (ctx , rs .Spec .DbCommonSpec , r .enableClusterMongoDBRoles , conn , kube .ObjectKeyFromApiObject (rs ), log ); ! status .IsOK () {
224
233
return r .updateStatus (ctx , rs , status , log )
@@ -238,7 +247,7 @@ func (r *ReconcileMongoDbReplicaSet) Reconcile(ctx context.Context, request reco
238
247
// See CLOUDP-189433 and CLOUDP-229222 for more details.
239
248
if recovery .ShouldTriggerRecovery (rs .Status .Phase != mdbstatus .PhaseRunning , rs .Status .LastTransition ) {
240
249
log .Warnf ("Triggering Automatic Recovery. The MongoDB resource %s/%s is in %s state since %s" , rs .Namespace , rs .Name , rs .Status .Phase , rs .Status .LastTransition )
241
- automationConfigStatus := r .updateOmDeploymentRs (ctx , conn , rs .Status .Members , rs , sts , log , caFilePath , agentCertSecretName , prometheusCertHash , true ).OnErrorPrepend ("Failed to create/update (Ops Manager reconciliation phase):" )
250
+ automationConfigStatus := r .updateOmDeploymentRs (ctx , conn , rs .Status .Members , rs , sts , log , caFilePath , agentCertSecretName , prometheusCertHash , true , shouldMirrorKeyfile ).OnErrorPrepend ("Failed to create/update (Ops Manager reconciliation phase):" )
242
251
deploymentError := create .DatabaseInKubernetes (ctx , r .client , * rs , sts , rsConfig , log )
243
252
if deploymentError != nil {
244
253
log .Errorf ("Recovery failed because of deployment errors, %w" , deploymentError )
@@ -254,7 +263,7 @@ func (r *ReconcileMongoDbReplicaSet) Reconcile(ctx context.Context, request reco
254
263
}
255
264
status = workflow .RunInGivenOrder (publishAutomationConfigFirst (ctx , r .client , * rs , lastSpec , rsConfig , log ),
256
265
func () workflow.Status {
257
- return r .updateOmDeploymentRs (ctx , conn , rs .Status .Members , rs , sts , log , caFilePath , agentCertSecretName , prometheusCertHash , false ).OnErrorPrepend ("Failed to create/update (Ops Manager reconciliation phase):" )
266
+ return r .updateOmDeploymentRs (ctx , conn , rs .Status .Members , rs , sts , log , caFilePath , agentCertSecretName , prometheusCertHash , false , shouldMirrorKeyfile ).OnErrorPrepend ("Failed to create/update (Ops Manager reconciliation phase):" )
258
267
},
259
268
func () workflow.Status {
260
269
workflowStatus := create .HandlePVCResize (ctx , r .client , & sts , log )
@@ -408,14 +417,24 @@ func AddReplicaSetController(ctx context.Context, mgr manager.Manager, imageUrls
408
417
zap .S ().Errorf ("Failed to watch for vault secret changes: %w" , err )
409
418
}
410
419
}
420
+
421
+ err = c .Watch (source .Kind (mgr .GetCache (), & searchv1.MongoDBSearch {},
422
+ handler .TypedEnqueueRequestsFromMapFunc (func (ctx context.Context , search * searchv1.MongoDBSearch ) []reconcile.Request {
423
+ source := search .GetMongoDBResourceRef ()
424
+ return []reconcile.Request {{NamespacedName : types.NamespacedName {Namespace : source .Namespace , Name : source .Name }}}
425
+ })))
426
+ if err != nil {
427
+ return err
428
+ }
429
+
411
430
zap .S ().Infof ("Registered controller %s" , util .MongoDbReplicaSetController )
412
431
413
432
return nil
414
433
}
415
434
416
435
// updateOmDeploymentRs performs OM registration operation for the replicaset. So the changes will be finally propagated
417
436
// to automation agents in containers
418
- func (r * ReconcileMongoDbReplicaSet ) updateOmDeploymentRs (ctx context.Context , conn om.Connection , membersNumberBefore int , rs * mdbv1.MongoDB , set appsv1.StatefulSet , log * zap.SugaredLogger , caFilePath string , agentCertSecretName string , prometheusCertHash string , isRecovering bool ) workflow.Status {
437
+ func (r * ReconcileMongoDbReplicaSet ) updateOmDeploymentRs (ctx context.Context , conn om.Connection , membersNumberBefore int , rs * mdbv1.MongoDB , set appsv1.StatefulSet , log * zap.SugaredLogger , caFilePath string , agentCertSecretName string , prometheusCertHash string , isRecovering bool , shouldMirrorKeyfileForMongot bool ) workflow.Status {
419
438
log .Debug ("Entering UpdateOMDeployments" )
420
439
// Only "concrete" RS members should be observed
421
440
// - if scaling down, let's observe only members that will remain after scale-down operation
@@ -469,6 +488,11 @@ func (r *ReconcileMongoDbReplicaSet) updateOmDeploymentRs(ctx context.Context, c
469
488
470
489
err = conn .ReadUpdateDeployment (
471
490
func (d om.Deployment ) error {
491
+ if shouldMirrorKeyfileForMongot {
492
+ if err := r .mirrorKeyfileIntoSecretForMongot (ctx , d , rs , log ); err != nil {
493
+ return err
494
+ }
495
+ }
472
496
return ReconcileReplicaSetAC (ctx , d , rs .Spec .DbCommonSpec , lastRsConfig .ToMap (), rs .Name , replicaSet , caFilePath , internalClusterPath , & p , log )
473
497
},
474
498
log ,
@@ -609,3 +633,70 @@ func getAllHostsRs(set appsv1.StatefulSet, clusterName string, membersCount int,
609
633
hostnames , _ := dns .GetDnsForStatefulSetReplicasSpecified (set , clusterName , membersCount , externalDomain )
610
634
return hostnames
611
635
}
636
+
637
+ func (r * ReconcileMongoDbReplicaSet ) applySearchOverrides (ctx context.Context , rs * mdbv1.MongoDB , log * zap.SugaredLogger ) bool {
638
+ search := r .lookupCorrespondingSearchResource (ctx , rs , log )
639
+ if search == nil {
640
+ log .Debugf ("No MongoDBSearch resource found, skipping search overrides" )
641
+ return false
642
+ }
643
+
644
+ log .Infof ("Applying search overrides from MongoDBSearch %s" , search .NamespacedName ())
645
+
646
+ if rs .Spec .AdditionalMongodConfig == nil {
647
+ rs .Spec .AdditionalMongodConfig = mdbv1 .NewEmptyAdditionalMongodConfig ()
648
+ }
649
+ searchMongodConfig := search_controller .GetMongodConfigParameters (search )
650
+ rs .Spec .AdditionalMongodConfig .AddOption ("setParameter" , searchMongodConfig ["setParameter" ])
651
+
652
+ mdbVersion , err := semver .ParseTolerant (rs .Spec .Version )
653
+ if err != nil {
654
+ log .Warnf ("Failed to parse MongoDB version %q: %w. Proceeding without the automatic creation of the searchCoordinator role that's necessary for MongoDB <8.2" , rs .Spec .Version , err )
655
+ } else if semver .MustParse ("8.2.0" ).GT (mdbVersion ) {
656
+ log .Infof ("Polyfilling the searchCoordinator role for MongoDB %s" , rs .Spec .Version )
657
+
658
+ if rs .Spec .Security == nil {
659
+ rs .Spec .Security = & mdbv1.Security {}
660
+ }
661
+ rs .Spec .Security .Roles = append (rs .Spec .Security .Roles , search_controller .SearchCoordinatorRole ())
662
+ }
663
+
664
+ return true
665
+ }
666
+
667
+ func (r * ReconcileMongoDbReplicaSet ) mirrorKeyfileIntoSecretForMongot (ctx context.Context , d om.Deployment , rs * mdbv1.MongoDB , log * zap.SugaredLogger ) error {
668
+ keyfileContents := maputil .ReadMapValueAsString (d , "auth" , "key" )
669
+ keyfileSecret := & corev1.Secret {ObjectMeta : metav1.ObjectMeta {Name : fmt .Sprintf ("%s-keyfile" , rs .Name ), Namespace : rs .Namespace }}
670
+
671
+ log .Infof ("Mirroring the replicaset %s's keyfile into the secret %s" , rs .ObjectKey (), kube .ObjectKeyFromApiObject (keyfileSecret ))
672
+
673
+ _ , err := controllerutil .CreateOrUpdate (ctx , r .client , keyfileSecret , func () error {
674
+ keyfileSecret .StringData = map [string ]string {"keyfile" : keyfileContents }
675
+ return controllerutil .SetOwnerReference (rs , keyfileSecret , r .client .Scheme ())
676
+ })
677
+ if err != nil {
678
+ return xerrors .Errorf ("Failed to mirror the replicaset's keyfile into a secret: %w" , err )
679
+ } else {
680
+ return nil
681
+ }
682
+ }
683
+
684
+ func (r * ReconcileMongoDbReplicaSet ) lookupCorrespondingSearchResource (ctx context.Context , rs * mdbv1.MongoDB , log * zap.SugaredLogger ) * searchv1.MongoDBSearch {
685
+ var search * searchv1.MongoDBSearch
686
+ searchList := & searchv1.MongoDBSearchList {}
687
+ if err := r .client .List (ctx , searchList , & client.ListOptions {
688
+ FieldSelector : fields .OneTermEqualSelector (search_controller .MongoDBSearchIndexFieldName , rs .GetNamespace ()+ "/" + rs .GetName ()),
689
+ }); err != nil {
690
+ log .Debugf ("Failed to list MongoDBSearch resources: %v" , err )
691
+ }
692
+ // this validates that there is exactly one MongoDBSearch pointing to this resource,
693
+ // and that this resource passes search validations. If either fails, proceed without a search target
694
+ // for the mongod automation config.
695
+ if len (searchList .Items ) == 1 {
696
+ searchSource := search_controller .NewEnterpriseResourceSearchSource (rs )
697
+ if searchSource .Validate () == nil {
698
+ search = & searchList .Items [0 ]
699
+ }
700
+ }
701
+ return search
702
+ }
0 commit comments