Skip to content

Replace StatefulSets with a customized controller#404

Closed
nwnt wants to merge 1 commit into
etcd-io:mainfrom
nwnt:customized-controller
Closed

Replace StatefulSets with a customized controller#404
nwnt wants to merge 1 commit into
etcd-io:mainfrom
nwnt:customized-controller

Conversation

@nwnt

@nwnt nwnt commented Jul 7, 2026

Copy link
Copy Markdown
Member

Based on the original PR

I have tested all the changes in my local and they passed. Will be covering the additional required changes on top of the original PR in the call today.

@ahrtr @ivanvc

@nwnt

nwnt commented Jul 7, 2026

Copy link
Copy Markdown
Member Author

The diff between this PR and the original one can be found here .

@nwnt nwnt force-pushed the customized-controller branch 3 times, most recently from 22717d5 to 4db9881 Compare July 7, 2026 14:37
}
requeue = true
// Service must exist before pods start so that headless DNS resolves.
if err := createHeadlessServiceIfNotExist(ctx, logger, r.Client, s.cluster, r.Scheme); err != nil {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need headless service after removing statefulSet? Or is it just an improper function name?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still need headless service, regardless of statefulset being used or not.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, was about to reply that we need it for the FQDN hostname.subdomain.namespace.svc.cluster.local for each pod.

Comment thread internal/controller/utils.go Outdated
func reconcileStatefulSet(ctx context.Context, logger logr.Logger, ec *ecv1alpha1.EtcdCluster, c client.Client, replicas int32, scheme *runtime.Scheme) (*appsv1.StatefulSet, error) {
// memberPodName returns the deterministic name for an etcd member pod.
// The naming convention mirrors StatefulSet so that headless-service DNS is identical.
func memberPodName(clusterName string, ordinal int) string {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The internal/controller/utils.go is a little overloaded. I think we need to extract all POD related stuff into a separate file, or even separate package. If we move it into separate package, we need to do some abstraction to ensure it's business agnostic.

in the first step, probably we can just move them into a separate file, and consider refactoring it in next step if possible.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, move everything related into pods.go. I have also moved the related tests to pods_test.go.

@ahrtr

ahrtr commented Jul 7, 2026

Copy link
Copy Markdown
Member

link to #362

@nwnt nwnt force-pushed the customized-controller branch from 4db9881 to 17d7b0d Compare July 8, 2026 03:26
@nwnt nwnt changed the base branch from main to customized-controller July 8, 2026 03:27
Based on the original [PR](etcd-io#363 (comment))

Signed-off-by: Nont <9658731+nwnt@users.noreply.github.com>
@nwnt nwnt force-pushed the customized-controller branch from 17d7b0d to 0b08a70 Compare July 8, 2026 12:22
@kubernetes-prow

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: nwnt
Once this PR has been reviewed and has the lgtm label, please assign hakman for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ahrtr

ahrtr commented Jul 8, 2026

Copy link
Copy Markdown
Member

It seems that the pipelines are not triggered if you raise PR against customized-controller. Let's still raise PR against main, once it's reviewed I will manually merge the PR to customized-controller.

@ahrtr

ahrtr commented Jul 8, 2026

Copy link
Copy Markdown
Member

@nwnt nwnt changed the base branch from customized-controller to main July 8, 2026 12:40
@nwnt

nwnt commented Jul 8, 2026

Copy link
Copy Markdown
Member Author

Both are done.

@ahrtr ahrtr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @GunaKKIBM PTAL

func nextPodOrdinal(currentOrdinals []int, expectedReplica int) int {
sort.Ints(currentOrdinals)
for i := range expectedReplica {
if i >= len(currentOrdinals) || i != currentOrdinals[i] {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the case for the i != currentOrdinals[i] ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's for filling the gap if one is found. For example, if the current ordinals are {0,1,3}, when i is 2 it will see that currentOrdinals[i] is 3, so 2 is the gap and hence the next ordinal to be used.

@nwnt nwnt Jul 8, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the current ordinal slice is always sorted before entering this loop.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the current ordinals are {0,1,3}

In which case we may run into this situation? If no, we'd better remove it for better readability

@nwnt nwnt Jul 8, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we do. I added it because the logic from the original PR would fail one of the e2e tests as described here.

Here is the code from the original PR.

			logger.Info("Creating pod for already-registered etcd member")
			nextOrdinal := int(currentPodCount)

Supposed that we have etcd-0, etcd-1, and etcd-2, if etcd-1 is deleted, the reconciliation loop logic would propose that etcd-2 should be created because the count is 2. The added function instead identifies the gap, and proposes that the first ordinal in the gap should be used. In that same scenario, 1 would be the next ordinal compared to 2 in the original PR.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might need to revisit this later. There are three variables that we need to consider to decide the ordinal,

  • EtcdCluster.Spec.Size
  • etcd member count
  • real POD count

We need to differentiate two different cases

  • one POD is somehow deleted or crashed
  • normal scale in/out case, or upgrade case

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree. I came up with this just to pass the existing e2e tests and didn't mean it to be an ultimate solution for the problem. Your cases are something we want to provide as a guarantee that etcd-operator will behave in a certain way.

@ahrtr

ahrtr commented Jul 8, 2026

Copy link
Copy Markdown
Member

Let's try to get this PR merged later today. Overall looks good.

We can consider some refactoring later when this is completely done.

@ahrtr

ahrtr commented Jul 8, 2026

Copy link
Copy Markdown
Member

FYI. Before I manually merge each PR into customized-controller, I will rebase it based on main first, to ensure there is no any conflict between customized-controller and main.

@ahrtr

ahrtr commented Jul 8, 2026

Copy link
Copy Markdown
Member

@nwnt I just manually merge this PR into branch customized-controller. Also raised a comment kubernetes/test-infra#37411 (comment) PTAL

@ahrtr ahrtr closed this Jul 8, 2026
@nwnt nwnt deleted the customized-controller branch July 8, 2026 20:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants