Skip to content

Add finalizer-guarded deletion for VCFM to prevent interrupted migrat… - #84

Open
jcpowermac wants to merge 1 commit into
openshift:mainfrom
jcpowermac:SPLAT-finding5-deletion-guard
Open

Add finalizer-guarded deletion for VCFM to prevent interrupted migrat…#84
jcpowermac wants to merge 1 commit into
openshift:mainfrom
jcpowermac:SPLAT-finding5-deletion-guard

Conversation

@jcpowermac

@jcpowermac jcpowermac commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

…ions

Deleting a VCFM CR mid-migration left the cluster split across vCenters with CPMS partially updated and no way to resume or track the migration. This adds a protection finalizer that blocks deletion while a migration is in progress (StartTime set, Ready not True), with an annotation-based force-delete override for deliberate abandonment.

Summary by CodeRabbit

  • New Features
    • Added protection against deleting a migration while it is actively running.
    • Added support for deliberately forcing deletion when necessary.
    • Migration status now indicates when deletion is blocked and provides a warning event.
    • Deletion proceeds automatically after migration completion or when no migration has started.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 6f00fbf2-79bb-4726-9ac6-b4b988afe826

📥 Commits

Reviewing files that changed from the base of the PR and between 3bdbc72 and 8fc17e9.

📒 Files selected for processing (3)
  • api/v1alpha1/vmwarecloudfoundationmigration_types.go
  • internal/controller/vmwarecloudfoundationmigration_controller.go
  • internal/controller/vmwarecloudfoundationmigration_controller_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • api/v1alpha1/vmwarecloudfoundationmigration_types.go
  • internal/controller/vmwarecloudfoundationmigration_controller.go

Walkthrough

The migration API adds finalizer and force-delete constants. The controller manages the finalizer, blocks deletion during active migrations, reports blocked status, emits a warning event, and permits forced or completed deletion. Tests cover each deletion state.

Changes

Migration deletion protection

Layer / File(s) Summary
Finalizer contract and constants
api/v1alpha1/vmwarecloudfoundationmigration_types.go
Exports Finalizer and ForceDeleteAnnotation constants. The finalizer protects the resource during active migration. The force-delete annotation permits removal when deletion is blocked.
Controller finalizer handling
internal/controller/vmwarecloudfoundationmigration_controller.go
Adds strconv and controllerutil imports, introduces reasonDeletionBlocked condition reason, and invokes handleFinalizer during reconciliation. The handleFinalizer method adds the finalizer to active resources, blocks deletion when migration has started but Ready is not true, records a Ready=False condition, updates status, emits a warning event, requeues periodically, and removes the finalizer when deletion is allowed or forced.
Deletion behavior tests
internal/controller/vmwarecloudfoundationmigration_controller_test.go
Adds test imports and extends singleton cleanup to reconcile and verify deletion. Tests verify finalizer installation, immediate deletion before migration starts, blocked deletion during migration, duplicate warning suppression, force-delete annotation bypass, and deletion after migration reaches Ready.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Reconciler
  participant KubernetesAPI
  participant EventRecorder
  Reconciler->>KubernetesAPI: Check and add finalizer
  Reconciler->>KubernetesAPI: Check migration Ready status
  alt Migration not Ready
    Reconciler->>KubernetesAPI: Update Ready=False condition
    Reconciler->>EventRecorder: Emit deletion warning
    Reconciler->>Reconciler: Requeue for periodic check
  else Migration Ready or force-deleted
    Reconciler->>KubernetesAPI: Remove finalizer
  end
Loading

Suggested reviewers: mtulio, vr4manta

🚥 Pre-merge checks | ✅ 14 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Test Structure And Quality ⚠️ Warning Assertions lack meaningful failure messages. All Expect() calls lack diagnostic context (e.g., "failed to reconcile", "finalizer not added"). This violates requirement #4. Add failure messages to all Expect() assertions: Expect(err).NotTo(HaveOccurred(), "descriptive error context"). Apply to ~30+ assertions in the new deletion test context.
✅ Passed checks (14 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the finalizer-based deletion protection that prevents interrupted VCFM migrations.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Stable And Deterministic Test Names ✅ Passed All Ginkgo test names are stable and deterministic. Twelve test names were found (including It, Context, Describe definitions). None contain dynamic information such as generated suffixes, timestam...
Microshift Test Compatibility ✅ Passed The added tests are controller unit tests using envtest, not e2e tests. The custom check targets e2e tests specifically; controller unit tests are outside the check scope.
Single Node Openshift (Sno) Test Compatibility ✅ Passed Tests added are controller unit tests using envtest, not e2e tests. Check specifies "When new Ginkgo e2e tests are added"; unit tests in isolated test environments are outside scope.
Topology-Aware Scheduling Compatibility ✅ Passed PR introduces finalizer-based deletion protection for a custom resource, not pod scheduling constraints. Changes are limited to API type definitions, controller logic for finalizer management, and...
Ote Binary Stdout Contract ✅ Passed The PR changes contain no process-level stdout writes. Type definitions use only SchemeBuilder registration in init(). Controller methods use klog.FromContext(). Tests follow Ginkgo v2 conventions...
Ipv6 And Disconnected Network Test Compatibility ✅ Passed The new Ginkgo tests in vmwarecloudfoundationmigration_controller_test.go contain no IPv4 assumptions or external connectivity requirements. Tests use only in-memory Kubernetes clients and fake rec...
No-Weak-Crypto ✅ Passed The PR adds Kubernetes finalizer and annotation logic only. Changed imports and added code contain no MD5, SHA-1, DES, RC4, Blowfish, ECB, custom crypto, or secret comparisons.
Container-Privileges ✅ Passed PR changes only Go source files for finalizer logic; no modifications to Dockerfile, manifests, or container security contexts. Existing configs follow security best practices with non-root user an...
No-Sensitive-Data-In-Logs ✅ Passed The PR adds protection finalizer logic without exposing sensitive data. The handleFinalizer method logs informational messages about deletion status via r.Recorder.Eventf, but does not log password...
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@openshift-ci
openshift-ci Bot requested review from mtulio and vr4manta July 29, 2026 13:22
@openshift-ci

openshift-ci Bot commented Jul 29, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: jcpowermac

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

The pull request process is described 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

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jul 29, 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: 1

🧹 Nitpick comments (5)
internal/controller/vmwarecloudfoundationmigration_controller_test.go (3)

330-359: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Give this cleanup Eventually an explicit timeout.

It relies on Gomega's 1s default while performing several sequential envtest API round-trips per attempt (Get, Update, Delete, Get, handleFinalizer → Update). That is a realistic flake source for suite cleanup.

As per coding guidelines: "timeouts on cluster operations (Eventually/Consistently calls)".

♻️ Suggested change
-			}).Should(BeTrue())
+			}, "30s", "200ms").Should(BeTrue())
🤖 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 `@internal/controller/vmwarecloudfoundationmigration_controller_test.go` around
lines 330 - 359, Update the cleanup Eventually block around handleFinalizer to
pass an explicit timeout appropriate for the sequential envtest API operations,
rather than relying on Gomega’s default timeout; preserve the existing polling
behavior and success condition.

Source: Coding guidelines


372-385: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Setup contradicts the test name and makes the assertion incidental.

newResource() sets State: Running, so "never started" only holds because handleFinalizer returns early after adding the finalizer and never reaches the StartTime assignment. Create the resource with MigrationStatePending (or explicitly assert Status.StartTime is nil before deleting) so the test pins the intended precondition rather than an implementation detail.

🤖 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 `@internal/controller/vmwarecloudfoundationmigration_controller_test.go` around
lines 372 - 385, Update the “deletes immediately when the migration never
started” test setup to create the resource with MigrationStatePending instead of
relying on newResource()’s Running state and finalizer behavior. Keep the
deletion and not-found assertions unchanged so the test explicitly covers a
migration that has not started.

387-447: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Consider splitting this It into per-behavior specs.

It currently asserts blocking, the warning event, event idempotence on a second pass, and force-delete unblocking. A failure in the first half hides the force-delete coverage entirely. Three It blocks over a shared BeforeEach (create → start → delete) would isolate the failures.

As per coding guidelines: "Review Ginkgo test code for single responsibility (each It block tests one specific behavior)".

🤖 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 `@internal/controller/vmwarecloudfoundationmigration_controller_test.go` around
lines 387 - 447, Split the single test around handleFinalizer into separate It
specs for deletion blocking, warning-event idempotence on a second pass, and
force-delete unblocking. Move the shared create/start/delete setup into
BeforeEach, while retaining each behavior’s existing assertions and using the
appropriate reconciler and recorder setup within each isolated spec.

Source: Coding guidelines

internal/controller/vmwarecloudfoundationmigration_controller.go (2)

126-129: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

err is silently dropped when done is false.

Today every error path in handleFinalizer returns done=true, so nothing is lost — but the call site makes that invariant implicit and easy to break later. Consider checking err independently.

♻️ Suggested hardening
-	if result, done, err := r.handleFinalizer(ctx, migration); done {
-		return result, err
-	}
+	result, done, err := r.handleFinalizer(ctx, migration)
+	if err != nil || done {
+		return result, err
+	}
🤖 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 `@internal/controller/vmwarecloudfoundationmigration_controller.go` around
lines 126 - 129, Update the handleFinalizer call in the reconciliation flow to
check err independently of done, returning the error whenever it is non-nil even
if done is false; preserve the existing result return for completed finalizer
handling.

213-213: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Strict "true" match on the force-delete annotation.

"True"/"1"/" true" are silently ignored, which is a confusing UX for an escape hatch used under pressure. strconv.ParseBool(strings.TrimSpace(...)) is more forgiving.

🤖 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 `@internal/controller/vmwarecloudfoundationmigration_controller.go` at line
213, Update the force-delete detection near the forced variable in the migration
reconciliation flow to trim whitespace and parse the annotation with
strconv.ParseBool, accepting standard boolean representations such as “true”,
“True”, and “1”. Preserve false behavior for missing, invalid, or explicitly
false annotation values.
🤖 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 `@internal/controller/vmwarecloudfoundationmigration_controller.go`:
- Around line 222-237: Ensure the blocked-deletion path requeues reconciliation
while returning done=false, rather than returning an empty Result that is
discarded. Update the call site that invokes this path to propagate its returned
Result whenever reconciliation short-circuits, including the
Spec.State-not-Running branch, so paused or unchanged migrations are
re-evaluated. Preserve the existing status and event behavior guarded by
alreadyRecorded.

---

Nitpick comments:
In `@internal/controller/vmwarecloudfoundationmigration_controller_test.go`:
- Around line 330-359: Update the cleanup Eventually block around
handleFinalizer to pass an explicit timeout appropriate for the sequential
envtest API operations, rather than relying on Gomega’s default timeout;
preserve the existing polling behavior and success condition.
- Around line 372-385: Update the “deletes immediately when the migration never
started” test setup to create the resource with MigrationStatePending instead of
relying on newResource()’s Running state and finalizer behavior. Keep the
deletion and not-found assertions unchanged so the test explicitly covers a
migration that has not started.
- Around line 387-447: Split the single test around handleFinalizer into
separate It specs for deletion blocking, warning-event idempotence on a second
pass, and force-delete unblocking. Move the shared create/start/delete setup
into BeforeEach, while retaining each behavior’s existing assertions and using
the appropriate reconciler and recorder setup within each isolated spec.

In `@internal/controller/vmwarecloudfoundationmigration_controller.go`:
- Around line 126-129: Update the handleFinalizer call in the reconciliation
flow to check err independently of done, returning the error whenever it is
non-nil even if done is false; preserve the existing result return for completed
finalizer handling.
- Line 213: Update the force-delete detection near the forced variable in the
migration reconciliation flow to trim whitespace and parse the annotation with
strconv.ParseBool, accepting standard boolean representations such as “true”,
“True”, and “1”. Preserve false behavior for missing, invalid, or explicitly
false annotation values.
🪄 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: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 9bd0accb-ff77-4f09-bd0d-bb0f87674f35

📥 Commits

Reviewing files that changed from the base of the PR and between 5944945 and 3bdbc72.

📒 Files selected for processing (3)
  • api/v1alpha1/vmwarecloudfoundationmigration_types.go
  • internal/controller/vmwarecloudfoundationmigration_controller.go
  • internal/controller/vmwarecloudfoundationmigration_controller_test.go

Comment thread internal/controller/vmwarecloudfoundationmigration_controller.go Outdated
…ions

Deleting a VCFM CR mid-migration left the cluster split across vCenters
with CPMS partially updated and no way to resume or track the migration.
This adds a protection finalizer that blocks deletion while a migration
is in progress (StartTime set, Ready not True), with an annotation-based
force-delete override for deliberate abandonment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jcpowermac
jcpowermac force-pushed the SPLAT-finding5-deletion-guard branch from 3bdbc72 to 8fc17e9 Compare August 4, 2026 13:00
@openshift-ci

openshift-ci Bot commented Aug 4, 2026

Copy link
Copy Markdown

@jcpowermac: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/e2e-vsphere-vcf-migration 8fc17e9 link false /test e2e-vsphere-vcf-migration

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

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

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant