From c613276f3513fd0868f7fbf55e8c75376e5ad4d9 Mon Sep 17 00:00:00 2001 From: myJamong Date: Fri, 6 Mar 2026 17:41:03 +0900 Subject: [PATCH 1/8] add externaldns annotations --- deploy/cr.yaml | 12 +++++++++ pkg/apis/psmdb/v1/psmdb_types.go | 10 +++++++ .../perconaservermongodb/service.go | 12 +++++++++ pkg/psmdb/mongos.go | 20 +++++++++++++- pkg/psmdb/service.go | 27 ++++++++++++++++++- 5 files changed, 79 insertions(+), 2 deletions(-) diff --git a/deploy/cr.yaml b/deploy/cr.yaml index 669fc25750..fc7e016861 100644 --- a/deploy/cr.yaml +++ b/deploy/cr.yaml @@ -263,6 +263,10 @@ spec: # rack: rack-22 # internalTrafficPolicy: Local # externalTrafficPolicy: Local +# externalDNS: +# prefix: "prod" +# domain: "mongo.example.com" +# ttl: 300 resources: limits: cpu: "600m" @@ -573,6 +577,10 @@ spec: # rack: rack-22 # internalTrafficPolicy: Local # externalTrafficPolicy: Local +# externalDNS: +# prefix: "prod" +# domain: "mongo.example.com" +# ttl: 300 resources: limits: cpu: "600m" @@ -698,6 +706,10 @@ spec: # nodePort: 32017 # internalTrafficPolicy: Local # externalTrafficPolicy: Local +# externalDNS: +# prefix: "prod" +# domain: "mongo.example.com" +# ttl: 300 # hostAliases: # - ip: "10.10.0.2" # hostnames: diff --git a/pkg/apis/psmdb/v1/psmdb_types.go b/pkg/apis/psmdb/v1/psmdb_types.go index e1bee7ea6a..9aeed9a307 100644 --- a/pkg/apis/psmdb/v1/psmdb_types.go +++ b/pkg/apis/psmdb/v1/psmdb_types.go @@ -1458,6 +1458,16 @@ type Expose struct { InternalTrafficPolicy *corev1.ServiceInternalTrafficPolicy `json:"internalTrafficPolicy,omitempty"` ExternalTrafficPolicy corev1.ServiceExternalTrafficPolicy `json:"externalTrafficPolicy,omitempty"` + + ExternalDNS *ExternalDNSConfig `json:"externalDNS,omitempty"` +} + +type ExternalDNSConfig struct { + // +kubebuilder:validation:Required + Prefix string `json:"prefix"` + // +kubebuilder:validation:Required + Domain string `json:"domain"` + TTL int `json:"ttl,omitempty"` } func (e *Expose) SaveOldMeta() bool { diff --git a/pkg/controller/perconaservermongodb/service.go b/pkg/controller/perconaservermongodb/service.go index 26883f5688..b17958a821 100644 --- a/pkg/controller/perconaservermongodb/service.go +++ b/pkg/controller/perconaservermongodb/service.go @@ -9,6 +9,7 @@ import ( k8serrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/controller-runtime/pkg/client" + logf "sigs.k8s.io/controller-runtime/pkg/log" api "github.com/percona/percona-server-mongodb-operator/pkg/apis/psmdb/v1" "github.com/percona/percona-server-mongodb-operator/pkg/mcs" @@ -28,7 +29,18 @@ func (r *ReconcilePerconaServerMongoDB) reconcileServices(ctx context.Context, c } func (r *ReconcilePerconaServerMongoDB) reconcileReplsetServices(ctx context.Context, cr *api.PerconaServerMongoDB, repls []*api.ReplsetSpec) error { + log := logf.FromContext(ctx) + for _, rs := range repls { + if dns := rs.Expose.ExternalDNS; dns != nil { + if dns.Prefix == "" || dns.Domain == "" { + return errors.Errorf("externalDNS requires both prefix and domain for replset %s", rs.Name) + } + if !rs.Expose.Enabled { + log.Info("externalDNS is configured but expose is not enabled, skipping DNS annotations", "replset", rs.Name) + } + } + // Create headless service service := psmdb.Service(cr, rs) if err := setControllerReference(cr, service, r.scheme); err != nil { diff --git a/pkg/psmdb/mongos.go b/pkg/psmdb/mongos.go index ed6d0afa2b..ae87ed704f 100644 --- a/pkg/psmdb/mongos.go +++ b/pkg/psmdb/mongos.go @@ -434,7 +434,25 @@ func MongosService(cr *api.PerconaServerMongoDB, name string) corev1.Service { } if cr.Spec.Sharding.Mongos != nil { - svc.Annotations = cr.Spec.Sharding.Mongos.Expose.ServiceAnnotations + annotations := make(map[string]string) + for k, v := range cr.Spec.Sharding.Mongos.Expose.ServiceAnnotations { + annotations[k] = v + } + + if dns := cr.Spec.Sharding.Mongos.Expose.ExternalDNS; dns != nil { + var hostname string + if cr.Spec.Sharding.Mongos.Expose.ServicePerPod { + hostname = BuildDNSHostname(dns, "mongos", name) + } else { + hostname = BuildDNSHostnameWithoutIndex(dns, "mongos") + } + annotations["external-dns.alpha.kubernetes.io/hostname"] = hostname + if dns.TTL > 0 { + annotations["external-dns.alpha.kubernetes.io/ttl"] = strconv.Itoa(dns.TTL) + } + } + + svc.Annotations = annotations for k, v := range cr.Spec.Sharding.Mongos.Expose.ServiceLabels { if _, ok := svc.Labels[k]; !ok { svc.Labels[k] = v diff --git a/pkg/psmdb/service.go b/pkg/psmdb/service.go index b4a9b2dacf..303007afa4 100644 --- a/pkg/psmdb/service.go +++ b/pkg/psmdb/service.go @@ -63,6 +63,19 @@ func Service(cr *api.PerconaServerMongoDB, replset *api.ReplsetSpec) *corev1.Ser // ExternalService returns a Service object needs to serve external connections func ExternalService(cr *api.PerconaServerMongoDB, replset *api.ReplsetSpec, podName string) *corev1.Service { + annotations := make(map[string]string) + for k, v := range replset.Expose.ServiceAnnotations { + annotations[k] = v + } + + if dns := replset.Expose.ExternalDNS; dns != nil { + hostname := BuildDNSHostname(dns, replset.Name, podName) + annotations["external-dns.alpha.kubernetes.io/hostname"] = hostname + if dns.TTL > 0 { + annotations["external-dns.alpha.kubernetes.io/ttl"] = strconv.Itoa(dns.TTL) + } + } + svc := &corev1.Service{ TypeMeta: metav1.TypeMeta{ Kind: "Service", @@ -71,7 +84,7 @@ func ExternalService(cr *api.PerconaServerMongoDB, replset *api.ReplsetSpec, pod ObjectMeta: metav1.ObjectMeta{ Name: podName, Namespace: cr.Namespace, - Annotations: replset.Expose.ServiceAnnotations, + Annotations: annotations, }, } @@ -439,3 +452,15 @@ func IsServiceImported(ctx context.Context, k8sclient client.Client, cr *api.Per } return true, nil } + +// BuildDNSHostname builds a DNS hostname in the format: prefix-component-podIndex.domain +func BuildDNSHostname(dns *api.ExternalDNSConfig, component, podName string) string { + parts := strings.Split(podName, "-") + podIndex := parts[len(parts)-1] + return fmt.Sprintf("%s-%s-%s.%s", dns.Prefix, component, podIndex, dns.Domain) +} + +// BuildDNSHostnameWithoutIndex builds a DNS hostname in the format: prefix-component.domain +func BuildDNSHostnameWithoutIndex(dns *api.ExternalDNSConfig, component string) string { + return fmt.Sprintf("%s-%s.%s", dns.Prefix, component, dns.Domain) +} From f22a11505b9d7b73d52524dd27469001cf68f158 Mon Sep 17 00:00:00 2001 From: myJamong Date: Mon, 9 Mar 2026 23:12:42 +0900 Subject: [PATCH 2/8] add validation for ttl and mongos --- pkg/apis/psmdb/v1/psmdb_types.go | 3 ++- pkg/controller/perconaservermongodb/service.go | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/apis/psmdb/v1/psmdb_types.go b/pkg/apis/psmdb/v1/psmdb_types.go index 9aeed9a307..a0f17d41c7 100644 --- a/pkg/apis/psmdb/v1/psmdb_types.go +++ b/pkg/apis/psmdb/v1/psmdb_types.go @@ -1467,7 +1467,8 @@ type ExternalDNSConfig struct { Prefix string `json:"prefix"` // +kubebuilder:validation:Required Domain string `json:"domain"` - TTL int `json:"ttl,omitempty"` + // +kubebuilder:validation:Minimum=0 + TTL int `json:"ttl,omitempty"` } func (e *Expose) SaveOldMeta() bool { diff --git a/pkg/controller/perconaservermongodb/service.go b/pkg/controller/perconaservermongodb/service.go index b17958a821..a6acc39ff0 100644 --- a/pkg/controller/perconaservermongodb/service.go +++ b/pkg/controller/perconaservermongodb/service.go @@ -72,6 +72,12 @@ func (r *ReconcilePerconaServerMongoDB) reconcileMongosSvc(ctx context.Context, return nil } + if dns := cr.Spec.Sharding.Mongos.Expose.ExternalDNS; dns != nil { + if dns.Prefix == "" || dns.Domain == "" { + return errors.New("externalDNS requires both prefix and domain for mongos") + } + } + if cr.Spec.Sharding.Mongos.Expose.ServicePerPod { for i := 0; i < int(cr.Spec.Sharding.Mongos.Size); i++ { err := r.createOrUpdateMongosSvc(ctx, cr, cr.Name+"-mongos-"+strconv.Itoa(i)) From 90546a304b31cf6b6c973a660d67a6e7d998377a Mon Sep 17 00:00:00 2001 From: myJamong Date: Mon, 9 Mar 2026 23:22:31 +0900 Subject: [PATCH 3/8] add unit test --- pkg/psmdb/service_test.go | 162 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) diff --git a/pkg/psmdb/service_test.go b/pkg/psmdb/service_test.go index 19c2809ec2..0c6c303e05 100644 --- a/pkg/psmdb/service_test.go +++ b/pkg/psmdb/service_test.go @@ -291,6 +291,114 @@ func TestExternalService(t *testing.T) { }, }, }, + "LoadBalancer with externalDNS": { + replset: &api.ReplsetSpec{ + Name: "rs0", + Expose: api.ExposeTogglable{ + Enabled: true, + Expose: api.Expose{ + ServiceAnnotations: map[string]string{ + "percona.com/test": "annotation", + }, + ExposeType: corev1.ServiceTypeLoadBalancer, + ExternalDNS: &api.ExternalDNSConfig{ + Prefix: "prod", + Domain: "mongo.example.com", + TTL: 300, + }, + }, + }, + }, + podName: "test-cr-rs0-0", + expectedSvc: &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cr-rs0-0", + Namespace: "test-ns", + Labels: map[string]string{ + "app.kubernetes.io/component": "external-service", + "app.kubernetes.io/instance": "test-cr", + "app.kubernetes.io/managed-by": "percona-server-mongodb-operator", + "app.kubernetes.io/name": "percona-server-mongodb", + "app.kubernetes.io/part-of": "percona-server-mongodb", + "app.kubernetes.io/replset": "rs0", + }, + Annotations: map[string]string{ + "percona.com/test": "annotation", + "external-dns.alpha.kubernetes.io/hostname": "prod-rs0-0.mongo.example.com", + "external-dns.alpha.kubernetes.io/ttl": "300", + }, + }, + Spec: corev1.ServiceSpec{ + PublishNotReadyAddresses: true, + Ports: []corev1.ServicePort{ + { + Name: "mongodb", + Port: 27017, + TargetPort: intstr.FromInt(27017), + AppProtocol: ptr.To("mongo"), + }, + }, + Selector: map[string]string{"statefulset.kubernetes.io/pod-name": "test-cr-rs0-0"}, + Type: corev1.ServiceTypeLoadBalancer, + ExternalTrafficPolicy: corev1.ServiceExternalTrafficPolicyLocal, + }, + }, + }, + "LoadBalancer with externalDNS no TTL": { + replset: &api.ReplsetSpec{ + Name: "rs0", + Expose: api.ExposeTogglable{ + Enabled: true, + Expose: api.Expose{ + ExposeType: corev1.ServiceTypeLoadBalancer, + ExternalDNS: &api.ExternalDNSConfig{ + Prefix: "staging", + Domain: "db.example.com", + }, + }, + }, + }, + podName: "test-cr-rs0-2", + expectedSvc: &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cr-rs0-2", + Namespace: "test-ns", + Labels: map[string]string{ + "app.kubernetes.io/component": "external-service", + "app.kubernetes.io/instance": "test-cr", + "app.kubernetes.io/managed-by": "percona-server-mongodb-operator", + "app.kubernetes.io/name": "percona-server-mongodb", + "app.kubernetes.io/part-of": "percona-server-mongodb", + "app.kubernetes.io/replset": "rs0", + }, + Annotations: map[string]string{ + "external-dns.alpha.kubernetes.io/hostname": "staging-rs0-2.db.example.com", + }, + }, + Spec: corev1.ServiceSpec{ + PublishNotReadyAddresses: true, + Ports: []corev1.ServicePort{ + { + Name: "mongodb", + Port: 27017, + TargetPort: intstr.FromInt(27017), + AppProtocol: ptr.To("mongo"), + }, + }, + Selector: map[string]string{"statefulset.kubernetes.io/pod-name": "test-cr-rs0-2"}, + Type: corev1.ServiceTypeLoadBalancer, + ExternalTrafficPolicy: corev1.ServiceExternalTrafficPolicyLocal, + }, + }, + }, } for name, tt := range tests { t.Run(name, func(t *testing.T) { @@ -308,3 +416,57 @@ func TestExternalService(t *testing.T) { }) } } + +func TestBuildDNSHostname(t *testing.T) { + tests := map[string]struct { + dns *api.ExternalDNSConfig + component string + podName string + expected string + }{ + "replset pod 0": { + dns: &api.ExternalDNSConfig{Prefix: "prod", Domain: "mongo.example.com"}, + component: "rs0", + podName: "my-cluster-rs0-0", + expected: "prod-rs0-0.mongo.example.com", + }, + "replset pod 2": { + dns: &api.ExternalDNSConfig{Prefix: "staging", Domain: "db.example.com"}, + component: "rs0", + podName: "my-cluster-rs0-2", + expected: "staging-rs0-2.db.example.com", + }, + "mongos pod": { + dns: &api.ExternalDNSConfig{Prefix: "prod", Domain: "mongo.example.com"}, + component: "mongos", + podName: "my-cluster-mongos-1", + expected: "prod-mongos-1.mongo.example.com", + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + result := BuildDNSHostname(tt.dns, tt.component, tt.podName) + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestBuildDNSHostnameWithoutIndex(t *testing.T) { + tests := map[string]struct { + dns *api.ExternalDNSConfig + component string + expected string + }{ + "mongos": { + dns: &api.ExternalDNSConfig{Prefix: "prod", Domain: "mongo.example.com"}, + component: "mongos", + expected: "prod-mongos.mongo.example.com", + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + result := BuildDNSHostnameWithoutIndex(tt.dns, tt.component) + assert.Equal(t, tt.expected, result) + }) + } +} From efb7c81d0170f069e70f7ceaeb4f715da3a5bd8a Mon Sep 17 00:00:00 2001 From: myJamong Date: Mon, 9 Mar 2026 23:37:09 +0900 Subject: [PATCH 4/8] change example cr prod to service-name --- deploy/cr.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deploy/cr.yaml b/deploy/cr.yaml index fc7e016861..42a3dcfa4a 100644 --- a/deploy/cr.yaml +++ b/deploy/cr.yaml @@ -264,7 +264,7 @@ spec: # internalTrafficPolicy: Local # externalTrafficPolicy: Local # externalDNS: -# prefix: "prod" +# prefix: "service-name" # domain: "mongo.example.com" # ttl: 300 resources: @@ -578,7 +578,7 @@ spec: # internalTrafficPolicy: Local # externalTrafficPolicy: Local # externalDNS: -# prefix: "prod" +# prefix: "service-name" # domain: "mongo.example.com" # ttl: 300 resources: @@ -707,7 +707,7 @@ spec: # internalTrafficPolicy: Local # externalTrafficPolicy: Local # externalDNS: -# prefix: "prod" +# prefix: "service-name" # domain: "mongo.example.com" # ttl: 300 # hostAliases: From f9915020ae5b01fb6740f92e5e86b5793ee293df Mon Sep 17 00:00:00 2001 From: myJamong Date: Tue, 10 Mar 2026 09:37:29 +0900 Subject: [PATCH 5/8] add externalDNS field to CRD schema and deepcopy --- ...mdb.percona.com_perconaservermongodbs.yaml | 39 +++++++++++++++++++ deploy/bundle.yaml | 39 +++++++++++++++++++ deploy/crd.yaml | 39 +++++++++++++++++++ deploy/cw-bundle.yaml | 39 +++++++++++++++++++ pkg/apis/psmdb/v1/zz_generated.deepcopy.go | 5 +++ 5 files changed, 161 insertions(+) diff --git a/config/crd/bases/psmdb.percona.com_perconaservermongodbs.yaml b/config/crd/bases/psmdb.percona.com_perconaservermongodbs.yaml index 6f197adcff..f99185f2e5 100644 --- a/config/crd/bases/psmdb.percona.com_perconaservermongodbs.yaml +++ b/config/crd/bases/psmdb.percona.com_perconaservermongodbs.yaml @@ -3954,6 +3954,19 @@ spec: type: boolean exposeType: type: string + externalDNS: + properties: + domain: + type: string + prefix: + type: string + ttl: + minimum: 0 + type: integer + required: + - prefix + - domain + type: object externalTrafficPolicy: type: string internalTrafficPolicy: @@ -14783,6 +14796,19 @@ spec: type: boolean exposeType: type: string + externalDNS: + properties: + domain: + type: string + prefix: + type: string + ttl: + minimum: 0 + type: integer + required: + - prefix + - domain + type: object externalTrafficPolicy: type: string internalTrafficPolicy: @@ -23229,6 +23255,19 @@ spec: type: object exposeType: type: string + externalDNS: + properties: + domain: + type: string + prefix: + type: string + ttl: + minimum: 0 + type: integer + required: + - prefix + - domain + type: object externalTrafficPolicy: type: string internalTrafficPolicy: diff --git a/deploy/bundle.yaml b/deploy/bundle.yaml index 770dc94c54..3b8db04edb 100644 --- a/deploy/bundle.yaml +++ b/deploy/bundle.yaml @@ -4841,6 +4841,19 @@ spec: type: boolean exposeType: type: string + externalDNS: + properties: + domain: + type: string + prefix: + type: string + ttl: + minimum: 0 + type: integer + required: + - prefix + - domain + type: object externalTrafficPolicy: type: string internalTrafficPolicy: @@ -15670,6 +15683,19 @@ spec: type: boolean exposeType: type: string + externalDNS: + properties: + domain: + type: string + prefix: + type: string + ttl: + minimum: 0 + type: integer + required: + - prefix + - domain + type: object externalTrafficPolicy: type: string internalTrafficPolicy: @@ -24116,6 +24142,19 @@ spec: type: object exposeType: type: string + externalDNS: + properties: + domain: + type: string + prefix: + type: string + ttl: + minimum: 0 + type: integer + required: + - prefix + - domain + type: object externalTrafficPolicy: type: string internalTrafficPolicy: diff --git a/deploy/crd.yaml b/deploy/crd.yaml index 9c4bbaa560..8c44a831f0 100644 --- a/deploy/crd.yaml +++ b/deploy/crd.yaml @@ -4841,6 +4841,19 @@ spec: type: boolean exposeType: type: string + externalDNS: + properties: + domain: + type: string + prefix: + type: string + ttl: + minimum: 0 + type: integer + required: + - prefix + - domain + type: object externalTrafficPolicy: type: string internalTrafficPolicy: @@ -15670,6 +15683,19 @@ spec: type: boolean exposeType: type: string + externalDNS: + properties: + domain: + type: string + prefix: + type: string + ttl: + minimum: 0 + type: integer + required: + - prefix + - domain + type: object externalTrafficPolicy: type: string internalTrafficPolicy: @@ -24116,6 +24142,19 @@ spec: type: object exposeType: type: string + externalDNS: + properties: + domain: + type: string + prefix: + type: string + ttl: + minimum: 0 + type: integer + required: + - prefix + - domain + type: object externalTrafficPolicy: type: string internalTrafficPolicy: diff --git a/deploy/cw-bundle.yaml b/deploy/cw-bundle.yaml index 04923990b9..6ddbbae07e 100644 --- a/deploy/cw-bundle.yaml +++ b/deploy/cw-bundle.yaml @@ -4841,6 +4841,19 @@ spec: type: boolean exposeType: type: string + externalDNS: + properties: + domain: + type: string + prefix: + type: string + ttl: + minimum: 0 + type: integer + required: + - prefix + - domain + type: object externalTrafficPolicy: type: string internalTrafficPolicy: @@ -15670,6 +15683,19 @@ spec: type: boolean exposeType: type: string + externalDNS: + properties: + domain: + type: string + prefix: + type: string + ttl: + minimum: 0 + type: integer + required: + - prefix + - domain + type: object externalTrafficPolicy: type: string internalTrafficPolicy: @@ -24116,6 +24142,19 @@ spec: type: object exposeType: type: string + externalDNS: + properties: + domain: + type: string + prefix: + type: string + ttl: + minimum: 0 + type: integer + required: + - prefix + - domain + type: object externalTrafficPolicy: type: string internalTrafficPolicy: diff --git a/pkg/apis/psmdb/v1/zz_generated.deepcopy.go b/pkg/apis/psmdb/v1/zz_generated.deepcopy.go index c6e07feffe..f8ed49809f 100644 --- a/pkg/apis/psmdb/v1/zz_generated.deepcopy.go +++ b/pkg/apis/psmdb/v1/zz_generated.deepcopy.go @@ -454,6 +454,11 @@ func (in *Expose) DeepCopyInto(out *Expose) { *out = new(corev1.ServiceInternalTrafficPolicy) **out = **in } + if in.ExternalDNS != nil { + in, out := &in.ExternalDNS, &out.ExternalDNS + *out = new(ExternalDNSConfig) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Expose. From ee194e4c65ff883e1fb778e1c7f2fce76a1325bf Mon Sep 17 00:00:00 2001 From: myJamong Date: Tue, 10 Mar 2026 15:55:31 +0900 Subject: [PATCH 6/8] remove required for prefix and use cr name as default --- .../psmdb.percona.com_perconaservermongodbs.yaml | 3 --- deploy/bundle.yaml | 3 --- deploy/crd.yaml | 3 --- deploy/cw-bundle.yaml | 3 --- pkg/apis/psmdb/v1/psmdb_types.go | 3 +-- pkg/controller/perconaservermongodb/service.go | 14 ++++++++++---- 6 files changed, 11 insertions(+), 18 deletions(-) diff --git a/config/crd/bases/psmdb.percona.com_perconaservermongodbs.yaml b/config/crd/bases/psmdb.percona.com_perconaservermongodbs.yaml index f99185f2e5..ac8b6e3e27 100644 --- a/config/crd/bases/psmdb.percona.com_perconaservermongodbs.yaml +++ b/config/crd/bases/psmdb.percona.com_perconaservermongodbs.yaml @@ -3964,7 +3964,6 @@ spec: minimum: 0 type: integer required: - - prefix - domain type: object externalTrafficPolicy: @@ -14806,7 +14805,6 @@ spec: minimum: 0 type: integer required: - - prefix - domain type: object externalTrafficPolicy: @@ -23265,7 +23263,6 @@ spec: minimum: 0 type: integer required: - - prefix - domain type: object externalTrafficPolicy: diff --git a/deploy/bundle.yaml b/deploy/bundle.yaml index 3b8db04edb..90de5ecb3c 100644 --- a/deploy/bundle.yaml +++ b/deploy/bundle.yaml @@ -4851,7 +4851,6 @@ spec: minimum: 0 type: integer required: - - prefix - domain type: object externalTrafficPolicy: @@ -15693,7 +15692,6 @@ spec: minimum: 0 type: integer required: - - prefix - domain type: object externalTrafficPolicy: @@ -24152,7 +24150,6 @@ spec: minimum: 0 type: integer required: - - prefix - domain type: object externalTrafficPolicy: diff --git a/deploy/crd.yaml b/deploy/crd.yaml index 8c44a831f0..22a1c2833d 100644 --- a/deploy/crd.yaml +++ b/deploy/crd.yaml @@ -4851,7 +4851,6 @@ spec: minimum: 0 type: integer required: - - prefix - domain type: object externalTrafficPolicy: @@ -15693,7 +15692,6 @@ spec: minimum: 0 type: integer required: - - prefix - domain type: object externalTrafficPolicy: @@ -24152,7 +24150,6 @@ spec: minimum: 0 type: integer required: - - prefix - domain type: object externalTrafficPolicy: diff --git a/deploy/cw-bundle.yaml b/deploy/cw-bundle.yaml index 6ddbbae07e..f5eb7d5cda 100644 --- a/deploy/cw-bundle.yaml +++ b/deploy/cw-bundle.yaml @@ -4851,7 +4851,6 @@ spec: minimum: 0 type: integer required: - - prefix - domain type: object externalTrafficPolicy: @@ -15693,7 +15692,6 @@ spec: minimum: 0 type: integer required: - - prefix - domain type: object externalTrafficPolicy: @@ -24152,7 +24150,6 @@ spec: minimum: 0 type: integer required: - - prefix - domain type: object externalTrafficPolicy: diff --git a/pkg/apis/psmdb/v1/psmdb_types.go b/pkg/apis/psmdb/v1/psmdb_types.go index a0f17d41c7..b0a96b4fe8 100644 --- a/pkg/apis/psmdb/v1/psmdb_types.go +++ b/pkg/apis/psmdb/v1/psmdb_types.go @@ -1463,8 +1463,7 @@ type Expose struct { } type ExternalDNSConfig struct { - // +kubebuilder:validation:Required - Prefix string `json:"prefix"` + Prefix string `json:"prefix,omitempty"` // +kubebuilder:validation:Required Domain string `json:"domain"` // +kubebuilder:validation:Minimum=0 diff --git a/pkg/controller/perconaservermongodb/service.go b/pkg/controller/perconaservermongodb/service.go index a6acc39ff0..75ced3220e 100644 --- a/pkg/controller/perconaservermongodb/service.go +++ b/pkg/controller/perconaservermongodb/service.go @@ -33,8 +33,11 @@ func (r *ReconcilePerconaServerMongoDB) reconcileReplsetServices(ctx context.Con for _, rs := range repls { if dns := rs.Expose.ExternalDNS; dns != nil { - if dns.Prefix == "" || dns.Domain == "" { - return errors.Errorf("externalDNS requires both prefix and domain for replset %s", rs.Name) + if dns.Domain == "" { + return errors.Errorf("externalDNS requires domain for replset %s", rs.Name) + } + if dns.Prefix == "" { + dns.Prefix = cr.Name } if !rs.Expose.Enabled { log.Info("externalDNS is configured but expose is not enabled, skipping DNS annotations", "replset", rs.Name) @@ -73,8 +76,11 @@ func (r *ReconcilePerconaServerMongoDB) reconcileMongosSvc(ctx context.Context, } if dns := cr.Spec.Sharding.Mongos.Expose.ExternalDNS; dns != nil { - if dns.Prefix == "" || dns.Domain == "" { - return errors.New("externalDNS requires both prefix and domain for mongos") + if dns.Domain == "" { + return errors.New("externalDNS requires domain for mongos") + } + if dns.Prefix == "" { + dns.Prefix = cr.Name } } From 19a85b21006e8ff050f734553c852c7e0f2e77dc Mon Sep 17 00:00:00 2001 From: myJamong Date: Tue, 10 Mar 2026 16:01:28 +0900 Subject: [PATCH 7/8] Move externalDNS validation and default prefix logic from reconcile functions to CheckNSetDefaults. --- pkg/apis/psmdb/v1/psmdb_defaults.go | 18 ++++++++++++++++ .../perconaservermongodb/service.go | 21 ++----------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/pkg/apis/psmdb/v1/psmdb_defaults.go b/pkg/apis/psmdb/v1/psmdb_defaults.go index 8a1e3d627c..cfc3975c0b 100644 --- a/pkg/apis/psmdb/v1/psmdb_defaults.go +++ b/pkg/apis/psmdb/v1/psmdb_defaults.go @@ -350,6 +350,15 @@ func (cr *PerconaServerMongoDB) CheckNSetDefaults(ctx context.Context, platform if len(cr.Spec.Sharding.Mongos.ServiceAccountName) == 0 && cr.CompareVersion("1.16.0") >= 0 { cr.Spec.Sharding.Mongos.ServiceAccountName = WorkloadSA } + + if dns := cr.Spec.Sharding.Mongos.Expose.ExternalDNS; dns != nil { + if dns.Domain == "" { + return errors.New("externalDNS requires domain for mongos") + } + if dns.Prefix == "" { + dns.Prefix = cr.Name + } + } } if mongos := cr.Spec.Sharding.Mongos; mongos != nil && cr.CompareVersion("1.18.0") >= 0 && cr.Status.State == AppStateInit { @@ -720,6 +729,15 @@ func (rs *ReplsetSpec) SetDefaults(platform version.Platform, cr *PerconaServerM } } + if dns := rs.Expose.ExternalDNS; dns != nil { + if dns.Domain == "" { + return fmt.Errorf("externalDNS requires domain for replset %s", rs.Name) + } + if dns.Prefix == "" { + dns.Prefix = cr.Name + } + } + if cr.CompareVersion("1.18.0") >= 0 && cr.Status.State == AppStateInit { if rs.Expose.DeprecatedExposeType != "" { log.Info("Field `.expose.exposeType` was deprecated in 1.18.0. Consider using `.expose.type` instead", "cluster", cr.Name, "namespace", cr.Namespace, "replset", rs.Name) diff --git a/pkg/controller/perconaservermongodb/service.go b/pkg/controller/perconaservermongodb/service.go index 75ced3220e..dc66c095aa 100644 --- a/pkg/controller/perconaservermongodb/service.go +++ b/pkg/controller/perconaservermongodb/service.go @@ -32,16 +32,8 @@ func (r *ReconcilePerconaServerMongoDB) reconcileReplsetServices(ctx context.Con log := logf.FromContext(ctx) for _, rs := range repls { - if dns := rs.Expose.ExternalDNS; dns != nil { - if dns.Domain == "" { - return errors.Errorf("externalDNS requires domain for replset %s", rs.Name) - } - if dns.Prefix == "" { - dns.Prefix = cr.Name - } - if !rs.Expose.Enabled { - log.Info("externalDNS is configured but expose is not enabled, skipping DNS annotations", "replset", rs.Name) - } + if rs.Expose.ExternalDNS != nil && !rs.Expose.Enabled { + log.Info("externalDNS is configured but expose is not enabled, skipping DNS annotations", "replset", rs.Name) } // Create headless service @@ -75,15 +67,6 @@ func (r *ReconcilePerconaServerMongoDB) reconcileMongosSvc(ctx context.Context, return nil } - if dns := cr.Spec.Sharding.Mongos.Expose.ExternalDNS; dns != nil { - if dns.Domain == "" { - return errors.New("externalDNS requires domain for mongos") - } - if dns.Prefix == "" { - dns.Prefix = cr.Name - } - } - if cr.Spec.Sharding.Mongos.Expose.ServicePerPod { for i := 0; i < int(cr.Spec.Sharding.Mongos.Size); i++ { err := r.createOrUpdateMongosSvc(ctx, cr, cr.Name+"-mongos-"+strconv.Itoa(i)) From ff70a0bf8a0e66bba681512bf534dec13357202c Mon Sep 17 00:00:00 2001 From: myJamong Date: Tue, 10 Mar 2026 19:08:17 +0900 Subject: [PATCH 8/8] run make generate manifest --- ...mdb.percona.com_perconaservermongodbs.yaml | 6 ++-- deploy/bundle.yaml | 6 ++-- deploy/crd.yaml | 6 ++-- deploy/cw-bundle.yaml | 6 ++-- e2e-tests/version-service/conf/crd.yaml | 36 +++++++++++++++++++ pkg/apis/psmdb/v1/zz_generated.deepcopy.go | 15 ++++++++ 6 files changed, 63 insertions(+), 12 deletions(-) diff --git a/config/crd/bases/psmdb.percona.com_perconaservermongodbs.yaml b/config/crd/bases/psmdb.percona.com_perconaservermongodbs.yaml index ac8b6e3e27..52df6874ad 100644 --- a/config/crd/bases/psmdb.percona.com_perconaservermongodbs.yaml +++ b/config/crd/bases/psmdb.percona.com_perconaservermongodbs.yaml @@ -3964,7 +3964,7 @@ spec: minimum: 0 type: integer required: - - domain + - domain type: object externalTrafficPolicy: type: string @@ -14805,7 +14805,7 @@ spec: minimum: 0 type: integer required: - - domain + - domain type: object externalTrafficPolicy: type: string @@ -23263,7 +23263,7 @@ spec: minimum: 0 type: integer required: - - domain + - domain type: object externalTrafficPolicy: type: string diff --git a/deploy/bundle.yaml b/deploy/bundle.yaml index 90de5ecb3c..9796a6b636 100644 --- a/deploy/bundle.yaml +++ b/deploy/bundle.yaml @@ -4851,7 +4851,7 @@ spec: minimum: 0 type: integer required: - - domain + - domain type: object externalTrafficPolicy: type: string @@ -15692,7 +15692,7 @@ spec: minimum: 0 type: integer required: - - domain + - domain type: object externalTrafficPolicy: type: string @@ -24150,7 +24150,7 @@ spec: minimum: 0 type: integer required: - - domain + - domain type: object externalTrafficPolicy: type: string diff --git a/deploy/crd.yaml b/deploy/crd.yaml index 22a1c2833d..60ef82bafe 100644 --- a/deploy/crd.yaml +++ b/deploy/crd.yaml @@ -4851,7 +4851,7 @@ spec: minimum: 0 type: integer required: - - domain + - domain type: object externalTrafficPolicy: type: string @@ -15692,7 +15692,7 @@ spec: minimum: 0 type: integer required: - - domain + - domain type: object externalTrafficPolicy: type: string @@ -24150,7 +24150,7 @@ spec: minimum: 0 type: integer required: - - domain + - domain type: object externalTrafficPolicy: type: string diff --git a/deploy/cw-bundle.yaml b/deploy/cw-bundle.yaml index f5eb7d5cda..eb4450085d 100644 --- a/deploy/cw-bundle.yaml +++ b/deploy/cw-bundle.yaml @@ -4851,7 +4851,7 @@ spec: minimum: 0 type: integer required: - - domain + - domain type: object externalTrafficPolicy: type: string @@ -15692,7 +15692,7 @@ spec: minimum: 0 type: integer required: - - domain + - domain type: object externalTrafficPolicy: type: string @@ -24150,7 +24150,7 @@ spec: minimum: 0 type: integer required: - - domain + - domain type: object externalTrafficPolicy: type: string diff --git a/e2e-tests/version-service/conf/crd.yaml b/e2e-tests/version-service/conf/crd.yaml index 9c4bbaa560..60ef82bafe 100644 --- a/e2e-tests/version-service/conf/crd.yaml +++ b/e2e-tests/version-service/conf/crd.yaml @@ -4841,6 +4841,18 @@ spec: type: boolean exposeType: type: string + externalDNS: + properties: + domain: + type: string + prefix: + type: string + ttl: + minimum: 0 + type: integer + required: + - domain + type: object externalTrafficPolicy: type: string internalTrafficPolicy: @@ -15670,6 +15682,18 @@ spec: type: boolean exposeType: type: string + externalDNS: + properties: + domain: + type: string + prefix: + type: string + ttl: + minimum: 0 + type: integer + required: + - domain + type: object externalTrafficPolicy: type: string internalTrafficPolicy: @@ -24116,6 +24140,18 @@ spec: type: object exposeType: type: string + externalDNS: + properties: + domain: + type: string + prefix: + type: string + ttl: + minimum: 0 + type: integer + required: + - domain + type: object externalTrafficPolicy: type: string internalTrafficPolicy: diff --git a/pkg/apis/psmdb/v1/zz_generated.deepcopy.go b/pkg/apis/psmdb/v1/zz_generated.deepcopy.go index f8ed49809f..53ae8f7c19 100644 --- a/pkg/apis/psmdb/v1/zz_generated.deepcopy.go +++ b/pkg/apis/psmdb/v1/zz_generated.deepcopy.go @@ -487,6 +487,21 @@ func (in *ExposeTogglable) DeepCopy() *ExposeTogglable { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExternalDNSConfig) DeepCopyInto(out *ExternalDNSConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalDNSConfig. +func (in *ExternalDNSConfig) DeepCopy() *ExternalDNSConfig { + if in == nil { + return nil + } + out := new(ExternalDNSConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExternalNode) DeepCopyInto(out *ExternalNode) { *out = *in