Skip to content

Token controller stops refreshing (RequeueAfter ≤ 0 kills self-requeue); JWTs silently expire #240

Description

@afuller-crusoe

Version

1.1.0

Which installation method(s) does this occur on?

Helm / Kubernetes manifests

Describe the bug.

Describe the bug

The Token controller's periodic refresh can silently stop. On the reconcile scheduled at the refresh instant,
Sync() pushes RequeueAfter = refreshTime.Sub(now) where refreshTime = exp − Lifetime/5 — so it is ≤ 0 at
that moment. durationStore uses Less (keeps the minimum, no floor), so Pop returns ≤ 0, and controller-runtime
treats RequeueAfter ≤ 0 as "do not requeue." The reconcile still mints a token and writes the Secret, so the loop
continues only via the Owns(&Secret{}) watch follow-up. When that watch event is coalesced/dropped, nothing
re-enqueues the Token: it is never refreshed again, the JWT expires, and consumers get Protocol authentication error.
It self-recovers only on the ~10h cache resync or an operator restart, so tokens flap.

Introduced by f9d0a98 ("feat(token): ensure Token is refreshed timely", first shipped in v1.1.0), which added
durationStore.Push(key, refreshTime.Sub(now)) and switched the store from Greater to Less. v1.0.1 is unaffected.

Steps/Code to reproduce bug

  1. Create a Token with refresh: true (default 15m lifetime).
  2. In the token-controller logs, each cycle emits two Finished syncing Token lines — one with no result
    (the mint, RequeueAfter ≤ 0) and one with result.RequeueAfter ≈ +12m (the Owns(Secret) follow-up).
  3. Eventually a cycle emits only the no-result reconcile (the follow-up watch event is coalesced). After that there are
    no further Token reconciles until the ~10h resync; the Secret's JWT expires and stops being refreshed.

Expected behavior

A Token with refresh: true never expires. The controller should arm its own next refresh on every reconcile —
compute the requeue from the newly-minted token's exp, and never return RequeueAfter ≤ 0 (floor to a positive
minimum, or set Requeue: true) — removing the dependency on the Owns(Secret) watch.

Environment details (please complete the following information):

  • Environment location: Cloud (Kubernetes)
  • Install / deployment method: Helm; slurm-operator v1.1.0 (also present on current main and v1.1.1)

Additional context

Relevant code: internal/controller/token/token_sync.go (the refreshTime.Sub(now) push),
internal/controller/token/token_controller.go (res = reconcile.Result{RequeueAfter: durationStore.Pop(req.String())},
no floor), and internal/utils/durationstore (Less, stores ≤0 verbatim, no clamp). The fix is small and local to
the token controller.

Minimum reproducible example

Relevant log output

Additional environment details

Other/Misc.

No response

Code of Conduct

  • I agree to follow Slurm Operator's Code of Conduct
  • I have searched the open bugs and have found no duplicates for this bug report

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions