From 9153648818429ee7e0f98c0216c665a303188b86 Mon Sep 17 00:00:00 2001 From: Nont <9658731+nwnt@users.noreply.github.com> Date: Mon, 3 Aug 2026 22:15:53 -0500 Subject: [PATCH] Implement updateConfig for custom controller Signed-off-by: Nont <9658731+nwnt@users.noreply.github.com> --- internal/controller/etcdcluster_controller.go | 51 +++++++++++++++++++ internal/etcdutils/etcdutils.go | 14 ++++- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/internal/controller/etcdcluster_controller.go b/internal/controller/etcdcluster_controller.go index f3ecaf97..73de2236 100644 --- a/internal/controller/etcdcluster_controller.go +++ b/internal/controller/etcdcluster_controller.go @@ -20,6 +20,8 @@ import ( "context" "crypto/tls" "fmt" + "slices" + "sort" "strings" "time" @@ -36,6 +38,7 @@ import ( ecv1alpha1 "go.etcd.io/etcd-operator/api/v1alpha1" "go.etcd.io/etcd-operator/internal/etcdutils" + "go.etcd.io/etcd/api/v3/etcdserverpb" etcdversions "go.etcd.io/etcd/api/v3/version" clientv3 "go.etcd.io/etcd/client/v3" ) @@ -411,6 +414,54 @@ func (r *EtcdClusterReconciler) promoteLearner(ctx context.Context, s *reconcile // the leader, move leadership to another member (the one with the lowest // ordinal) first. func (r *EtcdClusterReconciler) updateConfig(ctx context.Context, s *reconcileState) (ctrl.Result, error) { + logger := log.FromContext(ctx) + hash := EtcdClusterHash(s.cluster) + + sort.Slice(s.pods, func(i, j int) bool { + return strings.Compare(s.pods[i].Name, s.pods[j].Name) < 0 + }) + sort.Slice(s.memberListResp.Members, func(i, j int) bool { + return strings.Compare(s.memberListResp.Members[i].Name, s.memberListResp.Members[j].Name) < 0 + }) + + var outdated *corev1.Pod + for _, p := range slices.Backward(s.pods) { + if p.Annotations != nil && p.Annotations[HashMetadataKey] != hash { + outdated = p + break + } + } + if outdated == nil { + logger.Info("no pods with outdated config found") + return ctrl.Result{}, nil + } + logger.Info("a pod with outdated config found", "pod name", outdated.Name) + leaderId, leaderStatus := etcdutils.FindLeaderStatus(s.memberHealth, logger) + if leaderStatus == nil { + return ctrl.Result{}, fmt.Errorf("couldn't find leader for cluster %s", s.cluster.Name) + } + var outdatedMember *etcdserverpb.Member + for _, m := range s.memberListResp.Members { + if m.Name == outdated.Name { + outdatedMember = m + break + } + } + if outdatedMember.ID == leaderId { + // move the leader + var moveTo uint64 + for _, m := range s.memberListResp.Members { + if m.ID != leaderId { + moveTo = m.ID + } + } + eps := clientEndpointsFromPods(s.cluster.Name, s.cluster.Namespace, s.pods, clusterTLSEnabled(s.cluster)) + etcdutils.MoveLeader(etcdutils.ClientConfig{Endpoints: eps, TLS: s.tlsConfig}, moveTo) + } + if err := r.Delete(ctx, outdated); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil } diff --git a/internal/etcdutils/etcdutils.go b/internal/etcdutils/etcdutils.go index 1eab2876..6fdacaed 100644 --- a/internal/etcdutils/etcdutils.go +++ b/internal/etcdutils/etcdutils.go @@ -115,7 +115,6 @@ func FindLeaderStatus(healthInfos []EpHealth, logger logr.Logger) (uint64, *clie logger.Info("Leader found", "leaderID", leader) } return leader, leaderStatus - } func FindLearnerStatus(healthInfos []EpHealth, logger logr.Logger) (uint64, *clientv3.StatusResponse) { @@ -249,3 +248,16 @@ func RemoveMember(cfg ClientConfig, memberID uint64) error { _, err = c.MemberRemove(ctx, memberID) return err } + +func MoveLeader(cfg ClientConfig, memberId uint64) error { + c, err := clientv3.New(cfg.buildConfig()) + if err != nil { + return err + } + + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer func() { closeAndCancel(c, cancel) }() + + _, err = c.MoveLeader(ctx, memberId) + return err +}