From 2932ecc0502715cacdaeda350501d347d482318e Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Thu, 9 Jul 2026 17:32:58 +0400 Subject: [PATCH] fix(etcd): keep legacy etcd-headless Service alive during v1alpha2 adoption etcd-migrate adopts legacy clusters IN PLACE, so the Pods keep their original spec.subdomain: etcd-headless and are dialed at etcd-.etcd-headless..svc until they eventually roll onto the operator's native .etcd..svc domain. The v1alpha2 operator only creates the native `etcd` Service, and the legacy `etcd-headless` Service is pruned during the transition, so those names stop resolving (no such host), the operator's MemberList fails, and status.readyMembers never populates -- the EtcdCluster never goes Ready even though the etcd processes are healthy and in quorum. Ship a chart-managed transitional headless `etcd-headless` Service (selector mirrors the operator's native `etcd` Service via etcd-operator.cozystack.io/cluster, publishNotReadyAddresses: true). This is the DNS counterpart of the legacy *.etcd-headless..svc wildcard already kept in the server/peer cert SANs for the same transition window -- the TLS half of the compat was done, the DNS half was missing. Safe to remove, together with that SAN, once the members have rolled onto the native `etcd` subdomain. Verified live: recreating this Service on an adopted 3-node cluster restored per-pod DNS and the cluster went readyMembers=3 / Available=True. Refs: #3243 Signed-off-by: Andrey Kolkov --- .../extra/etcd/templates/etcd-cluster.yaml | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/packages/extra/etcd/templates/etcd-cluster.yaml b/packages/extra/etcd/templates/etcd-cluster.yaml index f933ebc0a..d6cd1bfeb 100644 --- a/packages/extra/etcd/templates/etcd-cluster.yaml +++ b/packages/extra/etcd/templates/etcd-cluster.yaml @@ -275,3 +275,50 @@ spec: app.kubernetes.io/managed-by: etcd-operator app.kubernetes.io/name: etcd version: {{ $.Chart.Version }} +--- +# Transitional headless Service backing the LEGACY per-pod DNS names. +# +# `etcd-migrate` adopts legacy clusters IN PLACE — the existing Pods are never +# recreated, so they keep their original `spec.subdomain: etcd-headless` (and +# hostname etcd-). Their stable DNS name therefore stays +# `etcd-.etcd-headless..svc`, which the v1alpha2 operator dials for +# MemberList/health until the members are eventually rolled onto its native +# `.etcd..svc` domain (served by the operator-owned `etcd` Service). +# +# The v1alpha2 operator only creates the native `etcd` Service, and the legacy +# `etcd-headless` Service (previously owned by the old operator) is pruned during +# the transition. Without a Service named `etcd-headless`, the adopted Pods' +# `etcd-.etcd-headless..svc` records stop resolving (`no such host`), +# MemberList fails, and status.readyMembers never populates — the cluster never +# goes Ready even though the etcd processes are healthy and in quorum. This is +# the DNS counterpart of the legacy `*.etcd-headless..svc` wildcard kept in +# the server/peer cert SANs above for the same transition window. +# +# Selector mirrors the operator's native `etcd` Service +# (etcd-operator.cozystack.io/cluster), so this resolves the same member Pods; +# publishNotReadyAddresses keeps peer discovery working while a member is not +# yet Ready. Safe to remove — together with the legacy cert SAN — once the +# members have been rolled onto the native `etcd` subdomain. +apiVersion: v1 +kind: Service +metadata: + name: etcd-headless + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: etcd + app.kubernetes.io/instance: {{ .Release.Name }} + cozystack.io/service: etcd +spec: + clusterIP: None + publishNotReadyAddresses: true + selector: + etcd-operator.cozystack.io/cluster: etcd + ports: + - name: client + port: 2379 + targetPort: 2379 + protocol: TCP + - name: peer + port: 2380 + targetPort: 2380 + protocol: TCP