Skip to content

fix(careinstruction): react to a Cluster losing its owner label - #76

Open
mikolajkucinski wants to merge 1 commit into
mainfrom
fix/move-shoot-between-careinstructions
Open

fix(careinstruction): react to a Cluster losing its owner label#76
mikolajkucinski wants to merge 1 commit into
mainfrom
fix/move-shoot-between-careinstructions

Conversation

@mikolajkucinski

@mikolajkucinski mikolajkucinski commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Description

Removing the ownership label from a Cluster released it on paper only. The watch predicate looks at the new object, so the update was filtered out, and the handler finds the CareInstruction by that same label anyway. Nothing reconciled until the pod was restarted.

The predicate now also admits updates where the label was just lost, the handler enqueues every CareInstruction in the namespace when the owner is unknown, and each CareInstruction tracks who owns the cluster of every shoot it matches. A change to that view restarts its shoot controller.

Based on #73, not on main, because it uses the restart mechanism from there.

Closes #63

@mikolajkucinski
mikolajkucinski requested a review from a team as a code owner August 7, 2026 11:41
@mikolajkucinski
mikolajkucinski force-pushed the fix/move-shoot-between-careinstructions branch 3 times, most recently from c0d657c to 63ec0dd Compare August 7, 2026 13:42
Base automatically changed from feat/trigger-reconciliation-from-greenhouse to main August 11, 2026 08:55
Copilot AI lite review requested due to automatic review settings August 11, 2026 09:10
@mikolajkucinski
mikolajkucinski force-pushed the fix/move-shoot-between-careinstructions branch from 63ec0dd to 528ff16 Compare August 11, 2026 09:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a reconciliation gap when a Greenhouse Cluster loses its shoot-grafter.cloudoperators.dev/careinstruction owner label, ensuring CareInstructions get re-enqueued and their shoot controllers restarted so ownership handoffs take effect without requiring a pod restart.

Changes:

  • Extend controller-runtime predicates and Cluster watch handling to admit “label just lost” updates and enqueue appropriate CareInstructions (including all candidates when the owner is unknown).
  • Track “blocked shoots” (foreign-owned clusters) per CareInstruction and restart the shoot controller when that ownership view changes.
  • Add/extend tests and document the operational handoff procedure for moving a Shoot between CareInstructions.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
README.md Documents how to move/release ownership using the CareInstruction label on the Secret/Cluster.
internal/clientutil/suite_test.go Adds a Ginkgo suite for internal/clientutil tests.
internal/clientutil/predicates.go Introduces PredicateHasLabelOrLost to pass updates where a label was removed.
internal/clientutil/predicates_test.go Adds unit tests for the new predicate behavior.
controller/careinstruction/enqueue_test.go Adds unit tests for Cluster → CareInstruction enqueue behavior (including released clusters).
controller/careinstruction/careinstruction_controller.go Updates Cluster watch wiring, adds a custom Cluster event handler, and restarts shoot controllers on ownership-view changes.
controller/careinstruction/careinstruction_controller_test.go Adds integration-style tests to ensure onboarding doesn’t restart controllers and ownership release does.
Suppressed comments (1)

controller/careinstruction/careinstruction_controller.go:782

  • If the Cluster has the CareInstruction label key but an empty value (possible via kubectl label ... key=), this will enqueue a request with an empty CareInstruction name. That can lead to pointless reconciles and also prevents the intended “released cluster” behavior. Treat empty values like “no owner label”.
	// Check if the cluster has the CareInstruction label
	if careInstructionName, exists := cluster.Labels[v1alpha1.CareInstructionLabel]; exists {
		return []ctrl.Request{
			{
				NamespacedName: client.ObjectKey{
					Name:      careInstructionName,
					Namespace: cluster.Namespace,
				},
			},
		}
	}

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread internal/clientutil/predicates.go Outdated
Comment thread controller/careinstruction/careinstruction_controller.go
@mikolajkucinski
mikolajkucinski force-pushed the fix/move-shoot-between-careinstructions branch 2 times, most recently from 0884fee to 82b2722 Compare August 13, 2026 15:07
Removing the ownership label from a Cluster released it on paper only. The
watch predicate looks at the new object and the handler finds the
CareInstruction by that same label, so the update was filtered out and
nothing reconciled until the shoot-grafter pod was restarted.

On-behalf-of: @SAP <mikolaj.kucinski@sap.com>
Signed-off-by: Mikolaj Kucinski <osidu12@gmail.com>
@mikolajkucinski
mikolajkucinski force-pushed the fix/move-shoot-between-careinstructions branch from 82b2722 to fc6d420 Compare August 13, 2026 15:10
@github-actions

Copy link
Copy Markdown

Merging this branch will increase overall coverage

Impacted Packages Coverage Δ 🤖
shoot-grafter/api/v1alpha1 73.08% (+13.85%) 🎉
shoot-grafter/controller/careinstruction 83.85% (+1.44%) 👍
shoot-grafter/controller/shoot 78.37% (+0.08%) 👍
shoot-grafter/internal/clientutil 75.00% (+1.67%) 👍

Coverage by file

Changed files (no unit tests)

Changed File Coverage Δ Total Covered Missed 🤖
shoot-grafter/api/v1alpha1/careinstruction_types.go 85.71% (+0.71%) 21 (+1) 18 (+1) 3 👍
shoot-grafter/controller/careinstruction/careinstruction_controller.go 83.67% (+1.94%) 349 (+37) 292 (+37) 57 👍
shoot-grafter/controller/shoot/shoot_controller.go 82.89% (+0.11%) 152 (+1) 126 (+1) 26 👍
shoot-grafter/internal/clientutil/predicates.go 75.00% (+1.67%) 24 (+9) 18 (+7) 6 (+2) 👍

Please note that the "Total", "Covered", and "Missed" counts above refer to code statements instead of lines of code. The value in brackets refers to the test coverage of that file in the old version of the code.

Changed unit test files

  • shoot-grafter/controller/careinstruction/careinstruction_controller_test.go
  • shoot-grafter/controller/careinstruction/enqueue_test.go
  • shoot-grafter/controller/careinstruction/ownership_test.go
  • shoot-grafter/controller/shoot/shoot_controller_fake_client_test.go
  • shoot-grafter/internal/clientutil/predicates_test.go
  • shoot-grafter/internal/clientutil/suite_test.go

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐛 [BUG] - Moving a Shoot to another CareInstruction is not possible

2 participants