Skip to content

feat(controllers): set client Service trafficDistribution from an annotation - #377

Merged
Timofei Larkin (lllamnyp) merged 3 commits into
mainfrom
feat/traffic-distribution-annotation
Oct 17, 2026
Merged

Timofei Larkin (lllamnyp) merged 3 commits into
mainfrom
feat/traffic-distribution-annotation

Conversation

@lllamnyp

@lllamnyp Timofei Larkin (lllamnyp) commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

What

The etcd-operator.cozystack.io/client-service-traffic-distribution annotation on an EtcdCluster sets spec.trafficDistribution on the <cluster>-client Service. In a cluster stretched across zones, clients then prefer a ready member in their own zone, or on their own node with PreferSameNode. There is no CRD, API type or schema change.

metadata:
  annotations:
    etcd-operator.cozystack.io/client-service-traffic-distribution: PreferClose

Prior work

This builds on #376 by Andrei Kvapil (@kvaps), which adds the same capability as a typed spec.clientService.trafficDistribution field. #376 has since been merged. The first commit here reverts it, removing the field from the API again, and the second commit adds the annotation. Thank you for working out the use case, the version matrix and the test shape. Reused from #376:

  • the scope: the client Service only, with the headless Service never touched;
  • the "not latched through status.observed" decision;
  • the Kubernetes version notes;
  • the controller-test helpers and cases (create, drift, headless untouched);
  • most of the docs prose.

What's different here:

Stopgap, and how it migrates

The annotation is a deliberate stopgap: the capability is needed now, and an API change is deferred for now. A typed field can replace it later. Migration would work like this: when both are set, the field wins. The annotation is deprecated, keeps working for a release or more, then is removed. Because both write through the same server-side-apply field manager, switching from one to the other doesn't flap the Service.

Behaviour

  • Values: PreferClose (Kubernetes 1.31+), PreferSameZone and PreferSameNode (1.34+). The operator builds against k8s.io/api 0.33, whose constants define all three.

  • Ownership: the field is written with server-side apply under field manager etcd-operator-traffic-distribution, and drift is reconciled back while the annotation is set. The operator's other Service writes are merge patches that never include this field. Removing the annotation releases the field, so it is cleared unless another manager also set it. A value set by hand on a cluster without the annotation is left alone. Clusters that don't use the annotation get no extra writes: ownership is checked from managedFields before any apply.

  • Invalid values: the operator emits a Warning Event on the EtcdCluster, leaves the field unset and continues the reconcile:

    • a value outside the three above → InvalidTrafficDistribution;
    • a value the apiserver rejects with 422, e.g. PreferSameZone on 1.33 (confirmed against the envtest 1.33 apiserver) → TrafficDistributionRejected;
    • a value an apiserver without the field silently drops (before 1.31) → TrafficDistributionNotPersisted. In that case each reconcile repeats a no-op apply, which costs one request and no storage write.

    Transient errors still fail the reconcile so it is retried, as with every other Service write.

  • Updates apply on the next reconcile. The controller watches EtcdCluster without a generation filter, so editing the annotation triggers a reconcile.

  • Docs: docs/concepts.md#client-service-traffic-distribution covers the zone-label prerequisites on nodes, spreading members across zones, and proxy support.

Testing

  • controllers/client_service_test.go (fake client) covers:
    • parsing of the annotation value;
    • no apply when the annotation is unused;
    • an invalid value;
    • a rejected value;
    • a dropped value;
    • a transient error being retried.
  • controllers/client_service_envtest_test.go runs against a real 1.33 apiserver, because the fake client in controller-runtime 0.21 can't apply. It covers:
    • set then remove;
    • drift reconciled back, including after a hand edit takes ownership;
    • a hand-set value with no annotation is left alone;
    • an invalid value clears the field;
    • updating to PreferSameZone takes whichever outcome the apiserver version gives.
  • go test ./controllers/ . with envtest 1.33 passes. go vet and gofmt are clean, and make manifests generate shows no drift: the events RBAC rule already existed.

CODEOWNERS

The third commit makes Timofei Larkin (@lllamnyp) the code owner of the API types (/api/) and the generated CRDs (/charts/etcd-operator/crd-bases/).

Summary by CodeRabbit

  • New Features
    • Configure client Service traffic distribution with an EtcdCluster annotation, including zone-local routing options. Removing the annotation clears the operator-managed setting; independently set Service values are preserved.
    • Invalid or unsupported values generate warning events without stopping reconciliation.
  • Changes
    • The spec.clientService.trafficDistribution configuration has been removed; use the annotation instead.
  • Documentation
    • Updated guidance on supported values, Kubernetes version requirements, routing behavior, and prerequisites for zone-local routing.

@github-actions github-actions Bot added controllers documentation Improvements or additions to documentation feature New feature or request labels Oct 16, 2026
@coderabbitai

coderabbitai Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c2f68f38-9ce2-40ce-9aa5-a691233578d4
📥 Commits

Reviewing files that changed from the base of the PR and between 88c3174 and 6d847e7.

📒 Files selected for processing: 1
  • .github/CODEOWNERS

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 0 remain after this review.

📝 Walkthrough

The operator now reads client Service traffic distribution from an EtcdCluster annotation instead of a typed spec field. It applies or releases the Service field through server-side apply, reports specified failures with Warning Events, and returns other apply errors.

Changes: Client Service traffic distribution

Layer / File(s) Summary
Annotation contract and API removal
controllers/helpers.go, api/v1alpha2/etcdcluster_types.go, api/v1alpha2/zz_generated.deepcopy.go, charts/etcd-operator/crd-bases/etcd-operator.cozystack.io_etcdclusters.yaml, api/v1alpha2/client_service_envtest_test.go, .github/CODEOWNERS
Adds the annotation key and removes the spec.clientService API type, generated deep-copy code, CRD schema, and related envtest checks. Adds ownership rules for the API and CRD directories.
Traffic-distribution field reconciliation
controllers/client_service.go, controllers/etcdcluster_controller.go, main.go
Parses the annotation and applies or releases the client Service field with server-side apply. Configures event recording and Event permissions, and handles invalid values, API rejection, and fields that the API server does not persist.
Reconciliation validation and documentation
controllers/client_service_test.go, controllers/client_service_envtest_test.go, README.md, docs/concepts.md
Tests annotation parsing, field ownership, warnings, and API-server outcomes. Documents configuration, accepted values, behavior, and routing prerequisites.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant EtcdCluster
  participant EtcdClusterReconciler
  participant KubernetesAPIServer
  participant EventRecorder
  EtcdCluster->>EtcdClusterReconciler: provide traffic-distribution annotation
  EtcdClusterReconciler->>KubernetesAPIServer: apply or release client Service field
  KubernetesAPIServer-->>EtcdClusterReconciler: return persisted value or apply error
  EtcdClusterReconciler->>EventRecorder: emit Warning Event when required
Loading
Merge Risk: ⚪ Minimal · up to 6d847

The ownership change has no established merge-blocking issue in the supplied review scope.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 18 functions across 6 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: configuring client Service trafficDistribution from an annotation.
Full details: Docstring Coverage

Explanation

Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 18 functions across 6 files. (1 skipped: 1 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

This reverts commit eccc4fb, removing
spec.clientService.trafficDistribution from the EtcdCluster API. The
capability returns in the next commit as an annotation on the
EtcdCluster, so the API does not change for now.

Assisted-By: LLM
Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
…otation

The etcd-operator.cozystack.io/client-service-traffic-distribution
annotation on an EtcdCluster sets spec.trafficDistribution on the
<cluster>-client Service (PreferClose, PreferSameZone or PreferSameNode),
so clients of a cluster stretched across zones prefer a nearby member.
It stands in for a typed spec field without changing the API for now.

The field is written with server-side apply under its own field manager,
so removing the annotation clears it unless another manager also set it,
and a value someone else set without the annotation is left alone. A
value outside that set, one the apiserver rejects (PreferSameZone and
PreferSameNode before 1.34) or one it drops (no support before 1.31) is
reported as a Warning Event and leaves the field unset; the rest of the
reconcile carries on. The headless Service is not touched.

Builds on the controller tests and docs of #376 by Andrei Kvapil.

Assisted-By: LLM
Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
Changes to the API types and the generated CRDs now route review to @lllamnyp alone.

Assisted-By: LLM
Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
@lllamnyp
Timofei Larkin (lllamnyp) merged commit 981f8a6 into main Oct 17, 2026
10 of 11 checks passed
@lllamnyp
Timofei Larkin (lllamnyp) deleted the feat/traffic-distribution-annotation branch October 17, 2026 20:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api-change controllers documentation Improvements or additions to documentation feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant