Skip to content

Commit 4c25e25

Browse files
committed
MongoDB multi-cluster enabled
Fix dns Update updateOmDeploymentRs OM registration helpers Update CRDs Todo and throw error Lint Add new scaling test + mock OM connection Add helpers Add scale up to test Extract common getReplicaSetProcessIdsFromReplicaSets read_statefulsets for MongoDB (pytest) Simplify tests Unit tests for state Improve comments, usage of state, replicate agent keys New constants Switch test order Some small e2e Multi cluster tests Naive scaler initialize cluster list
1 parent 91f232e commit 4c25e25

19 files changed

+1616
-138
lines changed

.evergreen-tasks.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,11 @@ tasks:
942942
commands:
943943
- func: e2e_test
944944

945+
- name: e2e_multi_cluster_new_replica_set_scale_up
946+
tags: [ "patch-run" ]
947+
commands:
948+
- func: e2e_test
949+
945950
- name: e2e_multi_cluster_scale_up_cluster
946951
tags: [ "patch-run" ]
947952
commands:

.evergreen.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,7 @@ task_groups:
838838
- e2e_multi_cluster_mtls_test
839839
- e2e_multi_cluster_replica_set_deletion
840840
- e2e_multi_cluster_replica_set_scale_up
841+
- e2e_multi_cluster_new_replica_set_scale_up
841842
- e2e_multi_cluster_scale_up_cluster
842843
- e2e_multi_cluster_scale_up_cluster_new_cluster
843844
- e2e_multi_cluster_replica_set_scale_down

api/v1/mdb/mongodb_types.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,17 @@ func (m *MongoDbSpec) GetExternalDomain() *string {
454454
return nil
455455
}
456456

457+
// GetExternalDomainForMemberCluster returns the external domain for a specific member cluster. Falls back to the global
458+
// external domain if not found.
459+
func (m *MongoDbSpec) GetExternalDomainForMemberCluster(clusterName string) *string {
460+
if cfg := m.ClusterSpecList.GetExternalAccessConfigurationForMemberCluster(clusterName); cfg != nil {
461+
if externalDomain := cfg.ExternalDomain; externalDomain != nil {
462+
return externalDomain
463+
}
464+
}
465+
return m.GetExternalDomain()
466+
}
467+
457468
func (m *MongoDbSpec) GetHorizonConfig() []MongoDBHorizonConfig {
458469
return m.Connectivity.ReplicaSetHorizons
459470
}

api/v1/mdb/mongodbbuilder.go

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -295,13 +295,8 @@ func (b *MongoDBBuilder) SetDefaultClusterSpecList() *MongoDBBuilder {
295295
return b
296296
}
297297

298-
func (b *MongoDBBuilder) SetClusterSpectList(clusters []string) *MongoDBBuilder {
299-
for _, e := range clusters {
300-
b.mdb.Spec.ClusterSpecList = append(b.mdb.Spec.ClusterSpecList, ClusterSpecItem{
301-
ClusterName: e,
302-
Members: 1, // number of cluster members b/w 1 to 5
303-
})
304-
}
298+
func (b *MongoDBBuilder) SetClusterSpecList(clusterSpecList ClusterSpecList) *MongoDBBuilder {
299+
b.mdb.Spec.ClusterSpecList = clusterSpecList
305300
return b
306301
}
307302

api/v1/mdb/zz_generated.deepcopy.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/mongodb.com_mongodb.yaml

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,146 @@ spec:
396396
clusterDomain:
397397
format: hostname
398398
type: string
399+
clusterSpecList:
400+
items:
401+
description: |-
402+
ClusterSpecItem is the mongodb multi-cluster spec that is specific to a
403+
particular Kubernetes cluster, this maps to the statefulset created in each cluster
404+
properties:
405+
clusterName:
406+
description: |-
407+
ClusterName is name of the cluster where the MongoDB Statefulset will be scheduled, the
408+
name should have a one on one mapping with the service-account created in the central cluster
409+
to talk to the workload clusters.
410+
type: string
411+
externalAccess:
412+
description: ExternalAccessConfiguration provides external access
413+
configuration for Multi-Cluster.
414+
properties:
415+
externalDomain:
416+
description: An external domain that is used for exposing
417+
MongoDB to the outside world.
418+
type: string
419+
externalService:
420+
description: Provides a way to override the default (NodePort)
421+
Service
422+
properties:
423+
annotations:
424+
additionalProperties:
425+
type: string
426+
description: A map of annotations that shall be added
427+
to the externally available Service.
428+
type: object
429+
spec:
430+
description: A wrapper for the Service spec object.
431+
type: object
432+
x-kubernetes-preserve-unknown-fields: true
433+
type: object
434+
type: object
435+
memberConfig:
436+
description: MemberConfig allows to specify votes, priorities
437+
and tags for each of the mongodb process.
438+
items:
439+
properties:
440+
priority:
441+
type: string
442+
tags:
443+
additionalProperties:
444+
type: string
445+
type: object
446+
votes:
447+
type: integer
448+
type: object
449+
type: array
450+
members:
451+
description: Amount of members for this MongoDB Replica Set
452+
type: integer
453+
podSpec:
454+
properties:
455+
persistence:
456+
description: Note, that this field is used by MongoDB resources
457+
only, let's keep it here for simplicity
458+
properties:
459+
multiple:
460+
properties:
461+
data:
462+
properties:
463+
labelSelector:
464+
type: object
465+
x-kubernetes-preserve-unknown-fields: true
466+
storage:
467+
type: string
468+
storageClass:
469+
type: string
470+
type: object
471+
journal:
472+
properties:
473+
labelSelector:
474+
type: object
475+
x-kubernetes-preserve-unknown-fields: true
476+
storage:
477+
type: string
478+
storageClass:
479+
type: string
480+
type: object
481+
logs:
482+
properties:
483+
labelSelector:
484+
type: object
485+
x-kubernetes-preserve-unknown-fields: true
486+
storage:
487+
type: string
488+
storageClass:
489+
type: string
490+
type: object
491+
type: object
492+
single:
493+
properties:
494+
labelSelector:
495+
type: object
496+
x-kubernetes-preserve-unknown-fields: true
497+
storage:
498+
type: string
499+
storageClass:
500+
type: string
501+
type: object
502+
type: object
503+
podTemplate:
504+
type: object
505+
x-kubernetes-preserve-unknown-fields: true
506+
type: object
507+
service:
508+
description: this is an optional service, it will get the name
509+
"<rsName>-service" in case not provided
510+
type: string
511+
statefulSet:
512+
description: |-
513+
StatefulSetConfiguration holds the optional custom StatefulSet
514+
that should be merged into the operator created one.
515+
properties:
516+
metadata:
517+
description: StatefulSetMetadataWrapper is a wrapper around
518+
Labels and Annotations
519+
properties:
520+
annotations:
521+
additionalProperties:
522+
type: string
523+
type: object
524+
labels:
525+
additionalProperties:
526+
type: string
527+
type: object
528+
type: object
529+
spec:
530+
type: object
531+
x-kubernetes-preserve-unknown-fields: true
532+
required:
533+
- spec
534+
type: object
535+
required:
536+
- members
537+
type: object
538+
type: array
399539
configServerCount:
400540
type: integer
401541
configSrv:

controllers/operator/common_controller.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,25 @@ func checkIfHasExcessProcesses(conn om.Connection, resourceName string, log *zap
299299
return workflow.Pending("cannot have more than 1 MongoDB Cluster per project (see https://docs.mongodb.com/kubernetes-operator/stable/tutorial/migrate-to-single-resource/)")
300300
}
301301

302+
// getReplicaSetProcessIdsFromDeployment extracts replica set member IDs from the OM deployment.
303+
// This is used to preserve stable member IDs across reconciliations in multi-cluster deployments,
304+
// preventing ID conflicts when clusters scale independently.
305+
// Returns a map of process name to replica set member ID (e.g., {"my-rs-0-0": 0, "my-rs-1-0": 1}).
306+
func getReplicaSetProcessIdsFromDeployment(replicaSetName string, deployment om.Deployment) map[string]int {
307+
processIds := map[string]int{}
308+
309+
replicaSet := deployment.GetReplicaSetByName(replicaSetName)
310+
if replicaSet == nil {
311+
return map[string]int{}
312+
}
313+
314+
for _, m := range replicaSet.Members() {
315+
processIds[m.Name()] = m.Id()
316+
}
317+
318+
return processIds
319+
}
320+
302321
// validateInternalClusterCertsAndCheckTLSType verifies that all the x509 internal cluster certs exist and return whether they are built following the kubernetes.io/tls secret type (tls.crt/tls.key entries).
303322
// TODO: this is almost the same as certs.EnsureSSLCertsForStatefulSet, we should centralize the functionality
304323
func (r *ReconcileCommonController) validateInternalClusterCertsAndCheckTLSType(ctx context.Context, configurator certs.X509CertConfigurator, opts certs.Options, log *zap.SugaredLogger) error {

controllers/operator/mongodbmultireplicaset_controller.go

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,7 @@ func (r *ReconcileMongoDbMultiReplicaSet) updateOmDeploymentRs(ctx context.Conte
728728
return err
729729
}
730730

731-
processIds := getReplicaSetProcessIdsFromReplicaSets(mrs.Name, existingDeployment)
731+
processIds := getReplicaSetProcessIdsFromDeployment(mrs.Name, existingDeployment)
732732

733733
// If there is no replicaset configuration saved in OM, it might be a new project, so we check the ids saved in annotation
734734
// A project migration can happen if .spec.opsManager.configMapRef is changed, or the original configMap has been modified.
@@ -795,21 +795,6 @@ func (r *ReconcileMongoDbMultiReplicaSet) updateOmDeploymentRs(ctx context.Conte
795795
return nil
796796
}
797797

798-
func getReplicaSetProcessIdsFromReplicaSets(replicaSetName string, deployment om.Deployment) map[string]int {
799-
processIds := map[string]int{}
800-
801-
replicaSet := deployment.GetReplicaSetByName(replicaSetName)
802-
if replicaSet == nil {
803-
return map[string]int{}
804-
}
805-
806-
for _, m := range replicaSet.Members() {
807-
processIds[m.Name()] = m.Id()
808-
}
809-
810-
return processIds
811-
}
812-
813798
func getReplicaSetProcessIdsFromAnnotation(mrs mdbmultiv1.MongoDBMultiCluster) (map[string]int, error) {
814799
if processIdsStr, ok := mrs.Annotations[util.LastAchievedRsMemberIds]; ok {
815800
processIds := make(map[string]map[string]int)

0 commit comments

Comments
 (0)