Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .evergreen-tasks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1359,3 +1359,8 @@ tasks:
tags: [ "patch-run" ]
commands:
- func: "e2e_test"

- name: e2e_search_sharded_external_mongod_single_mongot
tags: [ "patch-run" ]
commands:
- func: "e2e_test"
1 change: 1 addition & 0 deletions .evergreen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,7 @@ task_groups:
- e2e_search_enterprise_basic
- e2e_search_enterprise_tls
- e2e_search_enterprise_x509_cluster_auth
- e2e_search_sharded_external_mongod_single_mongot
<<: *teardown_group

# this task group contains just a one task, which is smoke testing whether the operator
Expand Down
8 changes: 8 additions & 0 deletions api/v1/mdb/mongodb_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1250,6 +1250,14 @@ func (m *MongoDB) ShardRsName(i int) string {
return fmt.Sprintf("%s-%d", m.Name, i)
}

func (m *MongoDB) ShardRsNames() []string {
names := make([]string, m.Spec.ShardCount)
for i := range m.Spec.ShardCount {
names[i] = m.ShardRsName(i)
}
return names
}

func (m *MongoDB) MultiShardRsName(clusterIdx int, shardIdx int) string {
return fmt.Sprintf("%s-%d-%d", m.Name, shardIdx, clusterIdx)
}
Expand Down
123 changes: 120 additions & 3 deletions api/v1/search/mongodbsearch_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ const (
MongotDefaultSyncSourceUsername = "search-sync-source"

ForceWireprotoAnnotation = "mongodb.com/v1.force-search-wireproto"

MongoDBSearchIndexFieldName = "mdbsearch-for-mongodbresourceref-index"
)

func init() {
Expand Down Expand Up @@ -97,14 +99,54 @@ type MongoDBSource struct {
}

type ExternalMongoDBSource struct {
// HostAndPorts is the list of mongod host:port seeds for replica set sources.
// Mutually exclusive with Sharded.
// +optional
HostAndPorts []string `json:"hostAndPorts,omitempty"`
// ShardedCluster contains configuration for external sharded MongoDB clusters.
// Mutually exclusive with HostAndPorts.
// +optional
ShardedCluster *ExternalShardedClusterConfig `json:"shardedCluster,omitempty"`
// mongod keyfile used to connect to the external MongoDB deployment
// +optional
KeyFileSecretKeyRef *userv1.SecretKeyRef `json:"keyfileSecretRef,omitempty"`
// TLS configuration for the external MongoDB deployment
// +optional
TLS *ExternalMongodTLS `json:"tls,omitempty"`
}

// ExternalShardedClusterConfig contains configuration for external sharded MongoDB clusters
type ExternalShardedClusterConfig struct {
// Router contains the mongos router configuration
// +kubebuilder:validation:Required
Router ExternalRouterConfig `json:"router"`
// Shards is the list of shard configurations
// +kubebuilder:validation:MinItems=1
Shards []ExternalShardConfig `json:"shards"`
}

// ExternalRouterConfig contains configuration for mongos routers in an external sharded cluster
type ExternalRouterConfig struct {
// Hosts is the list of mongos router endpoints (host:port)
// +kubebuilder:validation:Required
// +kubebuilder:validation:MinItems=1
Hosts []string `json:"hosts"`
// TLS configuration specific to the router. If not specified, falls back to the top-level external.tls config.
// +optional
TLS *ExternalMongodTLS `json:"tls,omitempty"`
}

// ExternalShardConfig contains configuration for a single shard in an external sharded cluster
type ExternalShardConfig struct {
// ShardName is the logical shard name (e.g., "shard-0").
// +kubebuilder:validation:Required
// +kubebuilder:validation:MinLength=1
ShardName string `json:"shardName"`
// Hosts is the list of mongod host:port seeds for this shard's replica set
// +kubebuilder:validation:MinItems=1
Hosts []string `json:"hosts"`
}

type ExternalMongodTLS struct {
// CA is a reference to a Secret containing the CA certificate that issued mongod's TLS certificate.
// The CA certificate is expected to be PEM encoded and available at the "ca.crt" key.
Expand All @@ -120,7 +162,14 @@ type TLS struct {
// CertificateKeySecret is a reference to a Secret containing a private key and certificate to use for TLS.
// The key and cert are expected to be PEM encoded and available at "tls.key" and "tls.crt".
// This is the same format used for the standard "kubernetes.io/tls" Secret type, but no specific type is required.
CertificateKeySecret corev1.LocalObjectReference `json:"certificateKeySecretRef"`
// If both CertificateKeySecret.Name and CertsSecretPrefix are specified, CertificateKeySecret.Name takes precedence.
// +optional
CertificateKeySecret corev1.LocalObjectReference `json:"certificateKeySecretRef,omitempty"`
// CertsSecretPrefix is a prefix used to derive the TLS secret name.
// When set, the operator will look for a secret named "{prefix}-{resourceName}-search-cert".
// If CertificateKeySecret.Name is also specified, that takes precedence over this field.
// +optional
CertsSecretPrefix string `json:"certsSecretPrefix,omitempty"`
}

type MongoDBSearchStatus struct {
Expand Down Expand Up @@ -251,9 +300,26 @@ func (s *MongoDBSearch) GetMongotGrpcPort() int32 {
return MongotDefaultGrpcPort
}

// TLSSecretNamespacedName will get the namespaced name of the Secret containing the server certificate and key
// TLSSecretNamespacedName will get the namespaced name of the Secret containing the server certificate and key.
// Precedence:
// 1. CertificateKeySecret.Name - explicit secret name
// 2. CertsSecretPrefix - uses pattern {prefix}-{resourceName}-search-cert
// 3. Default - uses pattern {resourceName}-search-cert
func (s *MongoDBSearch) TLSSecretNamespacedName() types.NamespacedName {
return types.NamespacedName{Name: s.Spec.Security.TLS.CertificateKeySecret.Name, Namespace: s.Namespace}
// Explicit name takes precedence
if s.Spec.Security.TLS.CertificateKeySecret.Name != "" {
return types.NamespacedName{Name: s.Spec.Security.TLS.CertificateKeySecret.Name, Namespace: s.Namespace}
}

// Prefix-based naming: {prefix}-{resourceName}-search-cert
if s.Spec.Security.TLS.CertsSecretPrefix != "" {
secretName := fmt.Sprintf("%s-%s-search-cert", s.Spec.Security.TLS.CertsSecretPrefix, s.Name)
return types.NamespacedName{Name: secretName, Namespace: s.Namespace}
}

// Default naming: {resourceName}-search-cert
secretName := fmt.Sprintf("%s-search-cert", s.Name)
return types.NamespacedName{Name: secretName, Namespace: s.Namespace}
}

// TLSOperatorSecretNamespacedName will get the namespaced name of the Secret created by the operator
Expand All @@ -262,6 +328,39 @@ func (s *MongoDBSearch) TLSOperatorSecretNamespacedName() types.NamespacedName {
return types.NamespacedName{Name: s.Name + "-search-certificate-key", Namespace: s.Namespace}
}

func (s *MongoDBSearch) CertificateKeySecretName() bool {
if s.Spec.Security.TLS == nil {
return false
}
return s.Spec.Security.TLS.CertificateKeySecret.Name != ""
}

// TLSSecretForShard returns the namespaced name of the TLS source secret for a specific shard.
// This is used in per-shard TLS mode for sharded clusters.
// Naming pattern:
// - With prefix: {prefix}-{shardName}-search-cert
// - Without prefix: {shardName}-search-cert
func (s *MongoDBSearch) TLSSecretForShard(shardName string) types.NamespacedName {
var secretName string
if s.Spec.Security.TLS != nil && s.Spec.Security.TLS.CertsSecretPrefix != "" {
secretName = fmt.Sprintf("%s-%s-search-cert", s.Spec.Security.TLS.CertsSecretPrefix, shardName)
} else {
secretName = fmt.Sprintf("%s-search-cert", shardName)
}
return types.NamespacedName{Name: secretName, Namespace: s.Namespace}
}

// TLSOperatorSecretForShard returns the namespaced name of the operator-managed TLS secret
// for a specific shard. This is the secret created by the operator containing the combined certificate and key.
func (s *MongoDBSearch) TLSOperatorSecretForShard(shardName string) types.NamespacedName {
return types.NamespacedName{Name: fmt.Sprintf("%s-search-certificate-key", shardName), Namespace: s.Namespace}
}

// IsTLSConfigured returns true if TLS is enabled (TLS struct is present)
func (s *MongoDBSearch) IsTLSConfigured() bool {
return s.Spec.Security.TLS != nil
}

func (s *MongoDBSearch) GetMongotHealthCheckPort() int32 {
return MongotDefautHealthCheckPort
}
Expand All @@ -270,6 +369,12 @@ func (s *MongoDBSearch) IsExternalMongoDBSource() bool {
return s.Spec.Source != nil && s.Spec.Source.ExternalMongoDBSource != nil
}

// IsExternalSourceSharded returns true if the source is an external sharded MongoDB cluster
func (s *MongoDBSearch) IsExternalSourceSharded() bool {
return s.IsExternalMongoDBSource() &&
s.Spec.Source.ExternalMongoDBSource.ShardedCluster != nil
}

func (s *MongoDBSearch) GetLogLevel() mdb.LogLevel {
if s.Spec.LogLevel == "" {
return "INFO"
Expand All @@ -296,3 +401,15 @@ func (s *MongoDBSearch) GetEffectiveMongotPort() int32 {
func (s *MongoDBSearch) GetPrometheus() *Prometheus {
return s.Spec.Prometheus
}

func (s *MongoDBSearch) MongotStatefulSetForShard(shardName string) types.NamespacedName {
return types.NamespacedName{Name: fmt.Sprintf("%s-mongot-%s", s.Name, shardName), Namespace: s.Namespace}
}

func (s *MongoDBSearch) MongotServiceForShard(shardName string) types.NamespacedName {
return types.NamespacedName{Name: fmt.Sprintf("%s-mongot-%s-svc", s.Name, shardName), Namespace: s.Namespace}
}

func (s *MongoDBSearch) MongotConfigMapForShard(shardName string) types.NamespacedName {
return types.NamespacedName{Name: fmt.Sprintf("%s-mongot-%s-config", s.Name, shardName), Namespace: s.Namespace}
}
73 changes: 73 additions & 0 deletions api/v1/search/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading