Skip to content

feat(controllers): skip reconciliation for terminals on hibernated shoots#511

Closed
petersutter wants to merge 1 commit into
masterfrom
feat/skip-reconciliation-for-hibernated-shoots
Closed

feat(controllers): skip reconciliation for terminals on hibernated shoots#511
petersutter wants to merge 1 commit into
masterfrom
feat/skip-reconciliation-for-hibernated-shoots

Conversation

@petersutter

@petersutter petersutter commented Jun 24, 2026

Copy link
Copy Markdown
Member

/area robustness
/kind enhancement

What this PR does / why we need it:

When a Shoot referenced by a Terminal's host or target credentials is hibernated, the terminal controller now skips reconciliation and emits a Hibernated event. A Shoot watch with a wake-up predicate (IsHibernated: true → false) automatically re-enqueues affected Terminals when the Shoot comes back.

Which issue(s) this PR fixes:
Fixes #

Special notes for your reviewer:

Release note:

Terminal reconciliation is now skipped while a referenced Shoot is hibernated, reducing unnecessary errors and retries. Terminals are automatically re-reconciled when the Shoot wakes up.

Summary by CodeRabbit

  • New Features

    • Terminal sessions now recognize a new Hibernated state for related shoot resources.
    • Terminals automatically pause progress while a referenced shoot is hibernated and resume once it wakes up.
    • Terminal events are now emitted when hibernation prevents processing, improving status visibility.
  • Bug Fixes

    • Terminal reconciliations are now retriggered when a shoot transitions from hibernated to active, reducing missed updates.

@gardener-prow gardener-prow Bot added area/robustness Robustness, reliability, resilience related kind/enhancement Enhancement, improvement, extension labels Jun 24, 2026
@gardener-prow

gardener-prow Bot commented Jun 24, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign petersutter for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds Shoot hibernation awareness to the Terminal controller. A new EventHibernated constant, a TerminalShootRef field index, and IndexTerminalByShootRef are introduced. Reconciliation skips Terminals whose referenced Shoot is hibernated, recording an event, and a Shoot watch re-enqueues those Terminals on wake-up. A cache transform strips Shoot objects to only hibernation status fields. Integration and unit tests cover all new paths.

Changes

Terminal Shoot Hibernation Support

Layer / File(s) Summary
EventHibernated constant and TerminalShootRef index contract
api/v1alpha1/terminal_types.go, controllers/terminal_controller.go
Adds the EventHibernated = "Hibernated" event constant and the TerminalShootRef index key plus exported IndexTerminalByShootRef function that derives deduplicated namespace/name keys from a Terminal's host and target ShootRef credentials.
Hibernation detection and reconcile early-exit
controllers/terminal_controller.go
Adds isAnyReferencedShootHibernated, which fetches each referenced Shoot and returns the first hibernated one's key (missing Shoots are non-blocking). In handleTerminal, inserts an early-exit that records an EventHibernated event and returns without requeuing when any referenced Shoot is hibernated and the Terminal is not being deleted.
Shoot watch, wake-up predicate, and Terminal enqueueing
controllers/terminal_controller.go
Extends SetupWithManager to watch gardencorev1beta1.Shoot objects. shootWakeUpPredicate accepts only update events where IsHibernated transitions from true to false. mapShootToTerminals lists Terminals by the TerminalShootRef index and returns reconcile requests for each.
Cache transform and main.go startup wiring
main.go
Adds transformShoot(), a TransformFunc that strips cached Shoot objects to TypeMeta/ObjectMeta and Status.IsHibernated. Wires this into ctrl.NewManager via cache.Options.ByObject, and registers the TerminalShootRef field index at startup.
Hibernation tests and test environment setup
controllers/controller_suite_test.go, controllers/terminal_hibernation_test.go, test/common.go
Registers the TerminalShootRef field index in BeforeSuite, disables conflicting admission plugins in the envtest API server. Adds unit tests for IndexTerminalByShootRef, shootWakeUpPredicate, mapShootToTerminals, and isAnyReferencedShootHibernated, an integration test for the full hibernated-terminal→wake-up→reconcile cycle, and handleTerminal unit tests for the skip and deletion-bypass paths.

Sequence Diagram

sequenceDiagram
    participant Shoot as Shoot (Gardener API)
    participant shootWakeUpPredicate
    participant mapShootToTerminals
    participant TerminalReconciler
    participant isAnyReferencedShootHibernated

    Note over Shoot: IsHibernated transitions true→false
    Shoot->>shootWakeUpPredicate: Update event
    shootWakeUpPredicate-->>mapShootToTerminals: event accepted
    mapShootToTerminals->>TerminalReconciler: list Terminals via TerminalShootRef index
    mapShootToTerminals-->>TerminalReconciler: enqueue reconcile requests

    TerminalReconciler->>isAnyReferencedShootHibernated: check host/target ShootRefs
    isAnyReferencedShootHibernated-->>TerminalReconciler: no hibernated Shoot found
    TerminalReconciler->>TerminalReconciler: proceed with normal reconciliation
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐇 A Shoot fell asleep, so the Terminal waits,
No reconciles run while the cluster hibernates.
When the Shoot stirs awake, a predicate sees,
And re-enqueues the work with the greatest of ease.
The index links Shoots back to Terminals fast —
Hop hop, wake up! The hibernation has passed! 🌱

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: skipping terminal reconciliation on hibernated Shoots.
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.
Description check ✅ Passed The PR description follows the template with area/kind, purpose, and release note filled, though the issue reference and reviewer notes are incomplete.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/skip-reconciliation-for-hibernated-shoots

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.

@gardener-prow gardener-prow Bot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. cla: yes Indicates the PR's author has signed the cla-assistant.io CLA. labels Jun 24, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
controllers/terminal_hibernation_test.go (1)

727-744: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Let the Shoot wake-up watch trigger reconciliation in this integration test.

The manual annotation update requeues the Terminal directly, so this test does not prove that the new Shoot watch and mapShootToTerminals path works after IsHibernated changes to false.

Proposed direction
 			patch := client.MergeFrom(shoot.DeepCopy())
 			shoot.Status.IsHibernated = false
 			Expect(e.K8sClient.Status().Patch(ctx, shoot, patch)).To(Succeed())
 
-			By("Triggering a reconcile to confirm hibernation check no longer blocks")
-			Eventually(func(g Gomega) {
-				t := &dashboardv1alpha1.Terminal{}
-				g.Expect(e.K8sClient.Get(ctx, terminalKey, t)).To(Succeed())
-
-				if t.Annotations == nil {
-					t.Annotations = map[string]string{}
-				}
-
-				t.Annotations["test-wake-trigger"] = "true"
-				g.Expect(e.K8sClient.Update(ctx, t)).To(Succeed())
-			}, timeout, interval).Should(Succeed())
-
-			By("Waiting for the terminal to progress past hibernation check (LastOperation transitions away from Succeeded)")
+			By("Waiting for the Shoot wake-up watch to re-enqueue the Terminal")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controllers/terminal_hibernation_test.go` around lines 727 - 744, The test is
still forcing reconciliation by updating the Terminal annotation, so it does not
validate the Shoot wake-up watch path. In terminal_hibernation_test.go, after
clearing Shoot.Status.IsHibernated in the wake-up scenario, remove the manual
Terminal update/requeue and instead wait for the Shoot-to-Terminal watch flow to
trigger reconciliation via the mapShootToTerminals path, using the existing
e.K8sClient, shoot, and terminalKey references to assert the Terminal changes
are observed after the Shoot becomes unhibernated.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@controllers/terminal_controller.go`:
- Around line 303-308: The hibernation check in isAnyReferencedShootHibernated
should not swallow Shoot read errors other than NotFound; right now the caller
in the Terminal reconciliation path can continue with an unknown state. Update
the error handling so that only a NotFound result is treated as non-blocking,
while transient/RBAC/cache read failures are returned from
isAnyReferencedShootHibernated and propagated by the reconcile flow in
terminal_controller.go to trigger a requeue. Keep the existing normal event/log
behavior only for the confirmed hibernated case and use the Terminal
reconciliation logic to stop processing when the referenced Shoot cannot be read
reliably.

In `@controllers/terminal_hibernation_test.go`:
- Around line 718-724: The hibernation stability assertion in the Consistently
check should not treat a K8sClient.Get failure as success, because that allows
the test to pass if the Terminal is deleted. Update the check around
e.K8sClient.Get and terminalKey so that a missing Terminal makes the condition
fail, while still returning true only when t.Status.AttachServiceAccountName and
t.Status.PodName are both nil. Keep the logic localized to the consistency
predicate in terminal_hibernation_test.go.

---

Nitpick comments:
In `@controllers/terminal_hibernation_test.go`:
- Around line 727-744: The test is still forcing reconciliation by updating the
Terminal annotation, so it does not validate the Shoot wake-up watch path. In
terminal_hibernation_test.go, after clearing Shoot.Status.IsHibernated in the
wake-up scenario, remove the manual Terminal update/requeue and instead wait for
the Shoot-to-Terminal watch flow to trigger reconciliation via the
mapShootToTerminals path, using the existing e.K8sClient, shoot, and terminalKey
references to assert the Terminal changes are observed after the Shoot becomes
unhibernated.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b5ff6638-6e6f-4bec-83f4-0e8306e3c42b

📥 Commits

Reviewing files that changed from the base of the PR and between 3c76f00 and 7f519e2.

📒 Files selected for processing (6)
  • api/v1alpha1/terminal_types.go
  • controllers/controller_suite_test.go
  • controllers/terminal_controller.go
  • controllers/terminal_hibernation_test.go
  • main.go
  • test/common.go

Comment thread controllers/terminal_controller.go
Comment thread controllers/terminal_hibernation_test.go Outdated
…oots

Watch Shoot resources and re-enqueue referencing Terminals on
wake-up (IsHibernated: true → false). While hibernated, emit a
Hibernated event and return early. Strips cached Shoots to
metadata + hibernation status to reduce memory.
@petersutter petersutter force-pushed the feat/skip-reconciliation-for-hibernated-shoots branch from 7f519e2 to 67f2054 Compare June 24, 2026 11:07
@petersutter petersutter added the ok-to-test Indicates a non-member PR verified by an org member that is safe to test. label Jun 26, 2026
@petersutter

Copy link
Copy Markdown
Member Author

Superseded by #512. Old branch name feat/skip-reconciliation-for-hibernated-shoots produced a ref of 57 chars, exceeding the cc-utils 50-char limit (GAR tag-length workaround) — this caused the OCI image push to be skipped (can-push=false). Renamed to feat/skip-hibernated-shoots so the snapshot image gets pushed.

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

Labels

area/robustness Robustness, reliability, resilience related cla: yes Indicates the PR's author has signed the cla-assistant.io CLA. kind/enhancement Enhancement, improvement, extension ok-to-test Indicates a non-member PR verified by an org member that is safe to test. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant