Repository navigation
Add LeaseLock for leader election (upstream #1877) #1
Description
Goal
Add LeaseLock to kubernetes/base/leaderelection/resourcelock/, so leader election can use the
Kubernetes coordination.k8s.io/v1 Lease resource instead of only ConfigMap.
Upstream issue: kubernetes-client#1877
Why
client-go moved leader election to Lease and removed ConfigMap support entirely
(kubernetes/client-go commit 276ea3ed). The Python client still only ships ConfigMapLock.
Three separate people have asked for this, including one porting it to kubernetes_asyncio.
Prior art — this is a takeover, not a fresh start
PR kubernetes-client#2314 by @rvlane (Dec 2024, +139/-7) implemented this.
roycaihwreviewed it: "LGTM overall. Could you add a test?"- The test was never added.
k8s-triage-robotauto-closed it 2025-11-21 afterlifecycle/stale->lifecycle/rotten.
The approach was approved. Only the test was missing. rvlane's work is Apache-2.0 (CLA signed,
confirmed in the thread) and must be credited with a Co-authored-by trailer.
Reference implementation: kubernetes/client-go tools/leaderelection/resourcelock/leaselock.go.
Contract a lock must satisfy
Read from leaderelection.py:
get(name, namespace) -> (bool, LeaderElectionRecord | ApiException)create(name, namespace, election_record) -> boolupdate(name, namespace, updated_record) -> bool.name,.namespace,.identityattributes
LeaderElectionRecord fields as written by try_acquire_or_renew:
| field | written as |
|---|---|
holder_identity |
str |
lease_duration |
str(config.lease_duration) |
acquire_time |
str(datetime) -> "YYYY-MM-DD HH:MM:SS.ffffff" |
renew_time |
same |
Critical: leaderelection.py compares old_election_record.__dict__ != self.observed_record.__dict__.
So a value written and then read back must compare equal. The record round-trip through
V1LeaseSpec (which stores real datetimes, not strings) is the main correctness risk and is what
the test must cover.
Changes needed on top of rvlane's version
- Named logger. rvlane's file calls
logging.basicConfig(level=logging.INFO)at import and
useslogging.info(...). Since that PR, this package moved tologger = logging.getLogger("leaderelection")(seeconfigmaplock.py:21,leaderelection.py). Calling
basicConfigfrom a library reconfigures the root logger of the importing application — it
must not ship. Use the named logger. - Set
lease_referenceafter a successfulcreate, so a followingupdatein the same cycle
has a reference to work from. - Do not silently swallow a time parse failure —
time_str_to_isocurrently logs and returns
None, which would write a nullacquireTime. - Leave
configmaplock.pyalone. rvlane's diff also removed unused imports there; that is
unrelated to the feature and widens the diff. Keep this PR focused.
Test plan (the missing piece roycaihw asked for)
New tests in leaderelection_test.py, mocking CoordinationV1Api:
createbuilds aV1Leasewith the right name and a spec carrying holder identity, duration
and both times.geton a missing lease returns(False, ApiException).geton an existing lease returns aLeaderElectionRecordwith the right holder identity.- Round-trip: a record written by
create/updateand read back bygetcompares equal
field-for-field to whatleaderelection.pywould have written. This is the test that protects
the__dict__comparison above. updatereturnsFalse(not raising) when the API raisesApiException.
Out of scope
- Deprecating or removing
ConfigMapLock. - Any change to
leaderelection.pyitself. - The asyncio client.
Activity
chala2001 commentedon Aug 28, 2026
Shipped as kubernetes-client#2690 — 3 files, +370/-1, 12 new tests. All four rebase fixes in the spec were implemented and each one is covered by a test that fails when the fix is reverted. Left out of scope as planned:
leaseTransitions(offered as a follow-up in the PR body).