Skip to content

fix(lease): let a pool provision for a lease that matches nothing yet - #1048

Open
kirkbrauer wants to merge 2 commits into
mainfrom
controller-cold-pool-lease
Open

fix(lease): let a pool provision for a lease that matches nothing yet#1048
kirkbrauer wants to merge 2 commits into
mainfrom
controller-cold-pool-lease

Conversation

@kirkbrauer

@kirkbrauer kirkbrauer commented Aug 30, 2026

Copy link
Copy Markdown
Member

An ExporterSet with minReplicas: 0 has no members until something asks for one, so a lease against it matches zero exporters. The lease controller called that unsatisfiable, and reconcileStatusEnded ends an unsatisfiable lease in the same reconcile. The set's demand rule only counts leases that are pending and not ended, so it never saw the request — a pool that keeps nothing warm could never provision on demand, which is the case it exists for.

The controller now asks whether an exporter set could provision a match before deciding. If one could, the lease becomes Pending/Provisioning and is requeued, which is the state the set scales on. If none could — no pool matches the selector, or the only one that does is at maxReplicas — the lease is unsatisfiable as before, so a typo in a selector still fails fast rather than hanging.

This is the first thing the lease controller reads outside jumpstarter.dev, so it needs the virtualtarget scheme registered and a read on exportersets in the Role the operator builds for it. Where that read is refused — a controller newer than its operator, or a cluster without the exporter set CRDs — the check reports "no pool" and behaviour is exactly what it was before.

Three specs cover it. With the fix reverted, the one describing the bug fails and the other two (pool at ceiling, no matching pool) still pass.

An ExporterSet that keeps nothing warm has no members until something asks
for one. A lease against such a pool matches zero exporters, and the lease
controller called that unsatisfiable — which reconcileStatusEnded then ends
in the same reconcile. The set's demand rule counts pending, unended leases,
so it never saw the request: a pool with minReplicas 0 could never provision
on demand, which is the case it exists for.

The lease controller now asks whether an exporter set could provision a
match before deciding. If one could, the lease is Pending/Provisioning and
requeued, which is the state the set scales on. If none could — no pool
matches the selector, or the only one that does is at maxReplicas — nothing
changes and the lease is still unsatisfiable, so a typo in a selector still
fails fast instead of hanging.

This is the first thing the lease controller reads outside jumpstarter.dev,
so it needs the virtualtarget scheme and a read on exportersets, granted by
the operator alongside the controller's other permissions. Where that read
is refused — a controller newer than its operator, or a cluster with no
exporter set CRDs — the check quietly reports "no pool" and the behaviour is
exactly what it was before.

Assisted-by: Claude
Signed-off-by: Kirk Brauer <kirkebrauer@gmail.com>
@kirkbrauer kirkbrauer added bug Something isn't working go Pull requests that update go code exporter-lifecycle controller labels Aug 30, 2026
@coderabbitai

coderabbitai Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 55 minutes.

View limit details

Limit details: You’ve used all 2 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7811725b-4fbd-4b0e-b6c8-788400d2b4cd

📥 Commits

Reviewing files that changed from the base of the PR and between d787eec and d1ec302.

📒 Files selected for processing (5)
  • controller/cmd/main.go
  • controller/deploy/operator/internal/controller/jumpstarter/rbac.go
  • controller/internal/controller/lease_controller.go
  • controller/internal/controller/lease_controller_test.go
  • controller/internal/controller/suite_test.go

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.

…rterRef

reconcileStatusExporterRef was already at 29 of gocyclo's limit of 30, so
asking it one more question about exporter sets put it over at 32.

Move the block that resolves which exporters a lease could be given — the
one it names, or everything its selector matches — into candidateExporters,
which reports whether it has already settled the lease's status. The parent
drops to 21 and reads as the sequence of decisions it is.

No behaviour change; the same specs pass.

Assisted-by: Claude
Signed-off-by: Kirk Brauer <kirkebrauer@gmail.com>
ctx context.Context,
result *ctrl.Result,
lease *jumpstarterdevv1alpha1.Lease,
selector labels.Selector,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't we have the selector in lease already?

Comment on lines 334 to 340
selector, err := lease.GetExporterSelector()
if err != nil {
return fmt.Errorf("reconcileStatusExporterRef: failed to get exporter selector: %w", err)
} else if selector.Empty() && lease.Spec.ExporterRef == nil {
lease.SetStatusInvalid("InvalidSelector", "The selector for the lease is empty, a selector is required")
return nil
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: you can move this block inside the function as well, and then no need to pass selector. It's not used anywhere later :)

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

Labels

bug Something isn't working controller exporter-lifecycle go Pull requests that update go code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants