Skip to content

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

Open
Timofei Larkin (lllamnyp) wants to merge 1 commit into
mainfrom
feat/traffic-distribution-annotation
Open

Timofei Larkin (lllamnyp) wants to merge 1 commit 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. 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.

Summary by CodeRabbit

  • New Features
    • Added configurable traffic distribution for the client Service using an EtcdCluster annotation, including zone-local routing options.
    • Invalid or unsupported settings generate warning events, while reconciliation continues.
  • Documentation
    • Documented supported values, Kubernetes version requirements, routing behavior, and how to clear the setting.

…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>
@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: 0138ce9c-7dab-422c-a7bc-33c6573cc112
📥 Commits

Reviewing files that changed from the base of the PR and between e6c1314 and 8735074.

📒 Files selected for processing: 8
  • README.md
  • controllers/client_service.go
  • controllers/client_service_envtest_test.go
  • controllers/client_service_test.go
  • controllers/etcdcluster_controller.go
  • controllers/helpers.go
  • docs/concepts.md
  • main.go

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

📝 Walkthrough

The operator now reads a traffic-distribution annotation on an EtcdCluster and reconciles the value on its client Service. It clears values it owns when the annotation is removed or invalid, reports warning Events for invalid or unpersisted values, and returns other apply errors.

Changes: Client Service traffic distribution

Layer / File(s) Summary
Annotation and reconcile wiring
controllers/helpers.go, controllers/client_service.go, controllers/etcdcluster_controller.go, main.go
Adds the annotation key and accepted-value parsing. Connects client Service reconciliation to the controller and configures event recording and Event permissions.
Traffic-distribution field reconciliation
controllers/client_service.go
Applies valid values to the client Service with server-side apply. Releases the field when appropriate, checks field ownership, and handles API responses and warnings.
Validation and documented behavior
controllers/client_service_test.go, controllers/client_service_envtest_test.go, README.md, docs/concepts.md
Adds unit and envtest coverage for annotation values, field ownership, warnings, and API-server outcomes. Documents configuration and behavior.

Priority: ⬇️ Low

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 annotation during reconcile
  EtcdClusterReconciler->>KubernetesAPIServer: apply client Service trafficDistribution
  KubernetesAPIServer-->>EtcdClusterReconciler: return persisted value or apply error
  EtcdClusterReconciler->>EventRecorder: emit warning for invalid, rejected, or unpersisted value
Loading
Merge Risk: ⚪ Minimal · up to 87350

The change adds an opt-in annotation to configure traffic distribution on the client Service. Clusters without the annotation should behave as before, and no concrete merge-blocking risk was identified.

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. (2 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
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 EtcdCluster annotation.
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.
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. (2 skipped: 2 unsupported.)

Resolution

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

  • Fix all pre-merge checks with AI
✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 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 branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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