k8slease

package
v0.0.21 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 28, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Overview

Package k8slease is the Kubernetes-backed port.SessionLease: cross-process, cross-HOST single-writer enforcement for the multi-replica cloud-native posture (ADR 0027 Phase 4), backed by a coordination.k8s.io/v1 Lease object per session id. It is the multi-host story the single-host flock lease cannot give: an API-server-coordinated lease survives a replica moving between nodes.

It is BUILT but UNWIRED by default: composition constructs it ONLY when an operator selects it with --session-lease-k8s-namespace, so the default path is byte-identical with no lease. k8slease never returns ErrLeaseUnsupported — it is a fully-supporting adapter; an RBAC Forbidden surfaces as a hard infrastructure error (the operator fixed the wrong thing), not a sticky disable.

Object naming: a session id is arbitrary text (a team/subagent id, a UUID, a user string) and need not be a valid RFC-1123 object name, so the Lease object is named "mecatl-lease-" + hex(sha256(id))[:40] — always ≤253 chars, always RFC-1123-valid, and collision-free (one-way hash). The raw id is preserved in an annotation for operators eyeballing `kubectl get leases`.

Fencing: spec.leaseTransitions is the canonical fencing counter — it advances on every takeover and is the port's Token. spec.renewTime + spec.leaseDurationSeconds is the expiry; a Get-then-Update with the read resourceVersion gives optimistic-concurrency CAS, so a lost race surfaces as a 409 Conflict → ErrLeaseHeld.

RBAC: this adapter only ever calls Get/Create/Update/Delete (never List or Watch), so it needs get,create,update,delete on `leases` in the `coordination.k8s.io` API group, namespace-scoped (a Role + RoleBinding on the configured namespace). See docs/usage.md for the manifest.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Lease

type Lease struct {
	// contains filtered or unexported fields
}

Lease is a port.SessionLease over coordination.k8s.io Lease objects in one namespace.

func New

func New(clientset kubernetes.Interface, namespace string, ttl time.Duration, clock port.Clock) *Lease

New constructs a k8s-backed lease over clientset in namespace, with the given TTL and clock. A non-positive ttl defaults to 30s.

func (*Lease) Acquire

func (l *Lease) Acquire(ctx context.Context, id session.SessionID, owner string) (port.Lease, error)

Acquire grants the lease when the object is absent, expired, or already held by owner; otherwise ErrLeaseHeld. A takeover bumps leaseTransitions (the token) and uses the read resourceVersion as a CAS guard so a concurrent takeover loses with a 409 Conflict → ErrLeaseHeld.

func (*Lease) Release

func (l *Lease) Release(ctx context.Context, in port.Lease) error

Release relinquishes a lease the caller still holds (holder + token match) by writing a TOMBSTONE: the holder is cleared and the renewTime is wound back into the past so the object reads as already-expired, while leaseTransitions (the fencing token) is RETAINED. This keeps the per-id token monotone across release — the port.SessionLease contract a successful takeover after a release returns a strictly-greater token (pinned by the shared leaseconformance suite). Deleting the object instead would reset leaseTransitions to 1 on the next create and break that contract. Idempotent: a NotFound, a holder/token mismatch, or a concurrent change is a no-op success — Release only drops the caller's OWN hold.

func (*Lease) Renew

func (l *Lease) Renew(ctx context.Context, in port.Lease) (port.Lease, error)

Renew extends a lease the caller still holds (holder + token match, unexpired), keeping the token and refreshing renewTime under a CAS Update. A holder change, a token mismatch, an expiry, or a 409 Conflict → ErrLeaseHeld (the loss signal).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL