Skip to content

fix: Remove accidentally added controller files#3203

Merged
mnecas merged 3 commits intokubev2v:mainfrom
amitosw15:powerstore-fixes
Oct 17, 2025
Merged

fix: Remove accidentally added controller files#3203
mnecas merged 3 commits intokubev2v:mainfrom
amitosw15:powerstore-fixes

Conversation

@amitosw15
Copy link
Copy Markdown
Contributor

@amitosw15 amitosw15 commented Oct 16, 2025

This commit removes the controller files that were accidentally included in the initial PowerStore support merge (PR #2595). The core feature logic remains unchanged.

Summary by CodeRabbit

  • Refactor
    • Volume-populator capability is now evaluated once per plan (global check) instead of per-VM, simplifying allocation, copy, wait, and cleanup flows.
    • Platform adapters now report support at the plan level, which changes when and how data volumes are pre-populated or adopted during warm migrations.
    • Call sites updated to use the global capability check, unifying decision paths.

@amitosw15 amitosw15 self-assigned this Oct 16, 2025
@amitosw15 amitosw15 requested review from rgolangh and removed request for Hazanel, mrnold, solenoci and yaacov October 16, 2025 11:36
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 16, 2025

Walkthrough

The Builder interface method SupportsVolumePopulators was changed from taking a VM reference to being parameterless across adapter implementations and all migration/kubevirt call sites; adapter implementations adjusted internal heuristics (notably vsphere and ovirt) and migration flows now use a builder-wide capability check.

Changes

Cohort / File(s) Summary
Interface definition
pkg/controller/plan/adapter/base/doc.go
Builder interface signature changed from SupportsVolumePopulators(vmRef ref.Ref) boolSupportsVolumePopulators() bool.
Adapter builders
pkg/controller/plan/adapter/.../builder.go
pkg/controller/plan/adapter/ocp/builder.go, pkg/controller/plan/adapter/openstack/builder.go, pkg/controller/plan/adapter/ova/builder.go, pkg/controller/plan/adapter/ovirt/builder.go, pkg/controller/plan/adapter/vsphere/builder.go
Implementations updated to remove the vmRef parameter. ocp/ova return false, openstack returns true. ovirt now uses the Plan warm flag via r.Context.Plan.IsWarm(). vsphere replaces per-VM/per-disk datastore checks with a datastore-map-based heuristic over mapped source datastores to detect offload support.
Migration & kubevirt flows
pkg/controller/plan/migration.go, pkg/controller/plan/kubevirt.go
Replaced per-VM calls SupportsVolumePopulators(vm.Ref) with SupportsVolumePopulators() across deletePopulatorPVCs, execute, PhaseCreateDataVolumes, PhaseAllocateDisks, PhaseCopyDisks, PhaseWait, and kubevirt data-volume logic; branching now depends on a builder-wide capability.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Migration
  participant Builder
  participant VM as "VM (per-VM)"

  rect #dfefff
  Note over Migration,VM: Old flow — per-VM capability query
  Migration->>VM: iterate vmRef
  VM->>Migration: vmRef
  Migration->>Builder: SupportsVolumePopulators(vmRef)
  Builder-->>Migration: bool (per-VM)
  end

  rect #eaffdf
  Note over Migration,Builder: New flow — builder-wide capability query
  Migration->>Builder: SupportsVolumePopulators()
  Builder-->>Migration: bool (global)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

storage-offload

Suggested reviewers

  • mnecas
  • yaacov
  • mrnold
  • solenoci

Poem

🐰 I hopped through code with nimble paws,
One call less puzzled by VM laws.
Builders now answer with a single cheer,
Migrations leaner — hop, hop, hooray, my dear! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The pull request title indicates removal of accidentally added controller files, but the changeset actually updates the SupportsVolumePopulators method signature and related call sites across multiple adapters rather than deleting files, making the title misleading and unrelated to the actual refactor. Revise the title to reflect the primary change, for example “refactor: remove vmRef parameter from SupportsVolumePopulators across adapters and update call sites,” so it accurately summarizes the modifications.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0503314 and 6d66abf.

📒 Files selected for processing (1)
  • pkg/controller/plan/migration.go (5 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

⚙️ CodeRabbit configuration file

**/*.go: Review the code as if you are a friendly but pedantic very senior developer in our team. We generally expect the following:

  • Clarity and Simplicity: Write idiomatic Go. Code should be self-documenting. Complexity should be justified.
  • Concurrency: Ensure goroutine safety. Use channels for communication and synchronization. Avoid race conditions.
  • Error Handling: No silent failures. Errors must be checked, wrapped with context, and propagated. Use errors.Is and errors.As.
  • Resource Management: Prevent resource leaks. Defer close() or cleanup() calls immediately after allocation. Manage goroutine lifecycles.
  • API Design: APIs (REST, gRPC) must be well-defined, versioned, and backward-compatible. Input validation is mandatory.
  • Observability: Instrument code with structured logging, metrics (e.g., Prometheus), and tracing. Logs should provide context, not just messages.
  • Security: Be vigilant against vulnerabilities (e.g., injection, insecure direct object references). Sanitize inputs. Do not hardcode secrets.
  • Performance: Write efficient code, but avoid premature optimization. Be mindful of algorithmic complexity and memory allocations.
  • coverage: Make sure that the code has unit tests.

Files:

  • pkg/controller/plan/migration.go
🔇 Additional comments (2)
pkg/controller/plan/migration.go (2)

756-756: Logic appears correct for DataVolume creation.

The condition r.Plan.IsWarm() || !r.builder.SupportsVolumePopulators() correctly creates DataVolumes when:

  • The migration is warm (requires DataVolumes for snapshot/checkpoint management), OR
  • Volume populators are not supported (fallback to DataVolumes)

This aligns with the past review comment noting that warm migrations with storage offload need DataVolumes.


392-392: Verify that volume populator support is a builder-wide capability.

The method signature change from SupportsVolumePopulators(vm.Ref) to SupportsVolumePopulators() indicates that volume populator support is now determined at the builder level rather than per-VM. This is a significant semantic change.

Ensure that volume populator support does not vary by VM characteristics (e.g., disk types, VM configuration, storage classes) but is truly a provider/builder-wide capability. If support can vary by VM, this change would introduce incorrect behavior.

Run the following script to examine the Builder interface definition and all implementations:

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
pkg/controller/plan/migration.go (1)

790-833: Critical: Compilation error - incorrect method signature.

Line 790 calls r.builder.SupportsVolumePopulators(vm.Ref), but the method signature was updated to take no parameters. This inconsistency will cause a compilation failure.

Apply this diff to fix the method call:

-			if r.builder.SupportsVolumePopulators(vm.Ref) && r.Plan.IsWarm() {
+			if r.builder.SupportsVolumePopulators() && r.Plan.IsWarm() {

Note: The redundant r.Plan.IsWarm() check could be removed since the new SupportsVolumePopulators() implementation already returns false for warm migrations (see line 1276 in pkg/controller/plan/adapter/vsphere/builder.go), but keeping it doesn't break functionality.

pkg/controller/plan/adapter/vsphere/builder.go (1)

524-524: Critical: Compilation error - incorrect method call.

Line 524 calls r.SupportsVolumePopulators(vmRef), but the method signature was updated to take no parameters (line 1275). This will cause a compilation failure.

Apply this diff:

-	if r.Plan.IsWarm() && r.SupportsVolumePopulators(vmRef) {
+	if r.Plan.IsWarm() && r.SupportsVolumePopulators() {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 344ce87 and 4b22854.

📒 Files selected for processing (7)
  • pkg/controller/plan/adapter/base/doc.go (1 hunks)
  • pkg/controller/plan/adapter/ocp/builder.go (1 hunks)
  • pkg/controller/plan/adapter/openstack/builder.go (1 hunks)
  • pkg/controller/plan/adapter/ova/builder.go (1 hunks)
  • pkg/controller/plan/adapter/ovirt/builder.go (1 hunks)
  • pkg/controller/plan/adapter/vsphere/builder.go (1 hunks)
  • pkg/controller/plan/migration.go (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

⚙️ CodeRabbit configuration file

**/*.go: Review the code as if you are a friendly but pedantic very senior developer in our team. We generally expect the following:

  • Clarity and Simplicity: Write idiomatic Go. Code should be self-documenting. Complexity should be justified.
  • Concurrency: Ensure goroutine safety. Use channels for communication and synchronization. Avoid race conditions.
  • Error Handling: No silent failures. Errors must be checked, wrapped with context, and propagated. Use errors.Is and errors.As.
  • Resource Management: Prevent resource leaks. Defer close() or cleanup() calls immediately after allocation. Manage goroutine lifecycles.
  • API Design: APIs (REST, gRPC) must be well-defined, versioned, and backward-compatible. Input validation is mandatory.
  • Observability: Instrument code with structured logging, metrics (e.g., Prometheus), and tracing. Logs should provide context, not just messages.
  • Security: Be vigilant against vulnerabilities (e.g., injection, insecure direct object references). Sanitize inputs. Do not hardcode secrets.
  • Performance: Write efficient code, but avoid premature optimization. Be mindful of algorithmic complexity and memory allocations.
  • coverage: Make sure that the code has unit tests.

Files:

  • pkg/controller/plan/adapter/ovirt/builder.go
  • pkg/controller/plan/adapter/ocp/builder.go
  • pkg/controller/plan/adapter/ova/builder.go
  • pkg/controller/plan/adapter/openstack/builder.go
  • pkg/controller/plan/adapter/base/doc.go
  • pkg/controller/plan/migration.go
  • pkg/controller/plan/adapter/vsphere/builder.go
🧬 Code graph analysis (5)
pkg/controller/plan/adapter/ovirt/builder.go (3)
pkg/controller/plan/adapter/base/doc.go (1)
  • Builder (120-155)
pkg/apis/forklift/v1beta1/plan.go (1)
  • Plan (271-279)
pkg/controller/plan/context/migration.go (1)
  • Destination (158-165)
pkg/controller/plan/adapter/ocp/builder.go (1)
pkg/controller/plan/adapter/base/doc.go (1)
  • Builder (120-155)
pkg/controller/plan/adapter/ova/builder.go (2)
pkg/controller/plan/adapter/base/doc.go (1)
  • Builder (120-155)
pkg/controller/plan/adapter/doc.go (1)
  • Builder (15-15)
pkg/controller/plan/adapter/openstack/builder.go (1)
pkg/controller/plan/adapter/base/doc.go (1)
  • Builder (120-155)
pkg/controller/plan/adapter/vsphere/builder.go (3)
pkg/controller/plan/adapter/base/doc.go (1)
  • Builder (120-155)
pkg/settings/features.go (1)
  • Features (31-51)
pkg/apis/forklift/v1beta1/mapping.go (2)
  • OffloadPlugin (55-57)
  • VSphereXcopyPluginConfig (87-94)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Red Hat Konflux / forklift-api-dev-preview-on-pull-request
  • GitHub Check: Red Hat Konflux / forklift-controller-dev-preview-on-pull-request
  • GitHub Check: build
  • GitHub Check: lint
🔇 Additional comments (4)
pkg/controller/plan/migration.go (4)

391-396: LGTM - Consistent API update.

The removal of the vmRef parameter from SupportsVolumePopulators() is correctly applied here, making the check builder-wide rather than VM-specific.


727-749: LGTM - API updated consistently.

The parameterless SupportsVolumePopulators() call is correctly applied in the volume populator creation path.


756-787: LGTM - Fallback logic preserved.

The negated check correctly triggers the non-populator DataVolume path when volume populators are not supported.


886-916: LGTM - Progress tracking updated correctly.

The parameterless call correctly determines which progress update path to use (populator vs. DataVolume).

Comment on lines +1272 to 1296
// FIXME rgolan - the behaviour needs to be per disk hense this method is flawed. Needs a bigger change.
// For now this method returns true, if there's a mapping (backend by copy-offload-mapping ConfigMap, that
// maps StoragetClasses to Vsphere data stores
func (r *Builder) SupportsVolumePopulators() bool {
if !settings.Settings.Features.CopyOffload || r.Plan.Spec.Warm {
return false
}

// Get the VM to access its disks
vm := &model.VM{}
err := r.Source.Inventory.Find(vm, vmRef)
if err != nil {
klog.Errorf("failed to get VM to detect volume populators support: %s", err)
return false
}

if !r.Context.Plan.Spec.MigrateSharedDisks {
vm.RemoveSharedDisks()
}

// Build datastore map for lookups
dsMap := make(map[string]*api.StoragePair)
dsMapIn := r.Context.Map.Storage.Spec.Map
for i := range dsMapIn {
mapped := &dsMapIn[i]
ref := mapped.Source
for _, m := range dsMapIn {
ref := m.Source
ds := &model.Datastore{}
err := r.Source.Inventory.Find(ds, ref)
if err != nil {
klog.Errorf("failed to get datastore to detect volume populators support: %s", err)
return false
}
dsMap[ds.ID] = mapped
}

// Check each disk's datastore for offload capability
for _, disk := range vm.Disks {
mapped, found := dsMap[disk.Datastore.ID]
if !found {
// Skip unmapped datastores (will be caught by validation elsewhere)
continue
}
if m.OffloadPlugin != nil && m.OffloadPlugin.VSphereXcopyPluginConfig != nil {
klog.V(2).Infof("found offload plugin: config %+v on ds map %+v", m.OffloadPlugin.VSphereXcopyPluginConfig, dsMapIn)
return true

// If any disk's datastore doesn't have offload capability, we cannot use populators for this VM
if mapped.OffloadPlugin == nil || mapped.OffloadPlugin.VSphereXcopyPluginConfig == nil {
klog.V(2).Infof("VM %s has disk on datastore %s without offload capability, cannot use volume populators", vmRef.String(), disk.Datastore.ID)
return false
}
}

// All disks are on datastores with offload capability
klog.V(2).Infof("VM %s: all disks are on datastores with offload capability, can use volume populators", vmRef.String())
return true
return false
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Address the architectural limitation and improve error handling.

The FIXME comment on line 1272 correctly identifies that this implementation is flawed - volume populator support should be determined per-disk based on the specific datastore, not as a builder-wide capability. This could lead to incorrect behavior when a VM has disks on different datastores with varying offload plugin support.

Additionally, the error handling on line 1285 silently logs and returns false, which could mask real inventory lookup failures and mislead the caller into thinking the feature is simply not supported rather than that an error occurred.

Consider these improvements:

  1. Per-disk API: Refactor to accept disk/datastore information and return support status per-disk:
func (r *Builder) DiskSupportsVolumePopulator(datastoreID string) (bool, error)
  1. Explicit error handling: Return errors instead of logging and returning false:
 		err := r.Source.Inventory.Find(ds, ref)
 		if err != nil {
-			klog.Errorf("failed to get datastore to detect volume populators support: %s", err)
-			return false
+			return false, liberr.Wrap(err, "datastore", ref.String())
 		}
  1. Cache the result: Since this iterates all datastores and queries inventory, consider caching the result during Load() to avoid repeated expensive lookups.

Based on coding guidelines: Per Go best practices, errors should be checked, wrapped with context, and propagated rather than silently logged.

🤖 Prompt for AI Agents
In pkg/controller/plan/adapter/vsphere/builder.go around lines 1272-1296, the
current Builder.SupportsVolumePopulators implementation is incorrect because it
reports a builder-wide capability instead of per-disk/datastore support and it
swallows inventory lookup errors; change the design to a per-datastore check
such as DiskSupportsVolumePopulator(datastoreID string) (bool, error) (or
similar), make the method return an explicit error instead of logging+returning
false, wrap inventory lookup errors with context and propagate them to the
caller, and move/compute datastore support into a cache populated during Load()
to avoid repeated expensive Find calls; update callers to use the new per-disk
API and handle propagated errors accordingly.

Comment on lines 1276 to 1278
if !settings.Settings.Features.CopyOffload || r.Plan.Spec.Warm {
return false
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Note: Warm migration check makes call-site conditions redundant.

The check on line 1276 returns false for warm migrations, which makes the && r.Plan.IsWarm() condition at line 524 redundant (it will never be true when SupportsVolumePopulators() is true). While not incorrect, this could be clarified.

Consider one of these approaches:

  1. Document the early-exit: Add a comment explaining that this method returns false for warm plans, so callers don't need additional warm checks.

  2. Remove the warm check here: If the caller-side logic needs flexibility, remove the warm check from this method and let callers decide:

 func (r *Builder) SupportsVolumePopulators() bool {
-	if !settings.Settings.Features.CopyOffload || r.Plan.Spec.Warm {
+	if !settings.Settings.Features.CopyOffload {
 		return false
 	}

Then update call sites to explicitly check warm status when needed.

🤖 Prompt for AI Agents
In pkg/controller/plan/adapter/vsphere/builder.go around lines 1276-1278, the
method currently returns false when the plan is Warm, which makes caller-side
checks like "&& r.Plan.IsWarm()" redundant; either add a clear comment on this
early-exit explaining that the method intentionally returns false for warm plans
so callers do not need additional warm checks, or remove the warm check from
this method and then update all call sites to perform an explicit IsWarm() check
where required (pick one approach consistently and adjust callers accordingly).

@amitosw15 amitosw15 force-pushed the powerstore-fixes branch 4 times, most recently from 5c9f5a3 to eaade99 Compare October 16, 2025 12:02
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Oct 16, 2025

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 0% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 8.36%. Comparing base (f1fe5d0) to head (6d66abf).
⚠️ Report is 1059 commits behind head on main.

Files with missing lines Patch % Lines
pkg/controller/plan/adapter/vsphere/builder.go 0.00% 8 Missing ⚠️
pkg/controller/plan/migration.go 0.00% 5 Missing ⚠️
pkg/controller/plan/kubevirt.go 0.00% 2 Missing ⚠️
pkg/controller/plan/adapter/ocp/builder.go 0.00% 1 Missing ⚠️
pkg/controller/plan/adapter/openstack/builder.go 0.00% 1 Missing ⚠️
pkg/controller/plan/adapter/ova/builder.go 0.00% 1 Missing ⚠️
pkg/controller/plan/adapter/ovirt/builder.go 0.00% 1 Missing ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@            Coverage Diff            @@
##             main   #3203      +/-   ##
=========================================
- Coverage   15.45%   8.36%   -7.10%     
=========================================
  Files         112     391     +279     
  Lines       23377   53729   +30352     
=========================================
+ Hits         3613    4494     +881     
- Misses      19479   48884   +29405     
- Partials      285     351      +66     
Flag Coverage Δ
unittests 8.36% <0.00%> (-7.10%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread pkg/controller/plan/adapter/vsphere/builder.go Outdated
Comment thread pkg/controller/plan/adapter/ovirt/builder.go Outdated
Copy link
Copy Markdown
Collaborator

@rgolangh rgolangh left a comment

Choose a reason for hiding this comment

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

2 comments. ping me when needed

This commit removes the controller files that were accidentally included
in the initial PowerStore support merge (PR kubev2v#2595). The core feature
logic remains unchanged.

Resolves: MTV-3114
Related: kubev2v#2595
Signed-off-by: Amit Weinstock <aweinsto@redhat.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
pkg/controller/plan/adapter/vsphere/builder.go (3)

524-550: Warm-offload block is effectively dead with current builder semantics

r.Plan.IsWarm() && r.SupportsVolumePopulators() will never be true for providers where SupportsVolumePopulators() returns false during warm (e.g., vSphere), so pvcMap is never built. Drop the redundant IsWarm() check here or adjust the builder to not short-circuit on warm. Also, adoption should match by disk.File, not dvSource, to work when Source=Blank. Based on learnings

Apply this change inside DataVolumes (adoption works regardless of source type):

-        if pvcMap != nil && dvSource.VDDK != nil {
-            // In a warm migration with storage offload, the PVC has already been created with
-            // the name template. Copy the result to the DataVolume so it can adopt the PVC.
-            if pvc, present := pvcMap[dvSource.VDDK.BackingFile]; present {
-                dv.ObjectMeta.Name = pvc.Name
-            }
-        } else {
+        if pvcMap != nil {
+            // In a warm migration with storage offload, the PVC has already been created with
+            // the name template. Match using the backing file computed from the disk file.
+            if pvc, present := pvcMap[baseVolume(disk.File, r.Plan.IsWarm())]; present {
+                dv.ObjectMeta.Name = pvc.Name
+            }
+        } else {
             // Set PVC name/generateName using template if configured
             if err := r.setPVCNameFromTemplate(&dv.ObjectMeta, vm, diskIndex, disk); err != nil {
                 r.Log.Info("Failed to set PVC name from template", "error", err)
             }
         }

1891-1899: Nil dereference on URL parse failure in mergeSecrets

If liburl.Parse fails, h is nil, then h.Hostname() panics. Wrap with else.

-        h, err := liburl.Parse(string(value))
-        if err != nil {
-            // ignore and try to use as is
-            dst.Data["GOVMOMI_HOSTNAME"] = value
-        }
-        dst.Data["GOVMOMI_HOSTNAME"] = []byte(h.Hostname())
+        h, err := liburl.Parse(string(value))
+        if err != nil {
+            // use as-is when not a URL
+            dst.Data["GOVMOMI_HOSTNAME"] = value
+        } else {
+            dst.Data["GOVMOMI_HOSTNAME"] = []byte(h.Hostname())
+        }

1339-1355: Potential nil-pointer dereference on OffloadPlugin

mapped.OffloadPlugin or mapped.OffloadPlugin.VSphereXcopyPluginConfig can be nil; dereferencing them will panic.

-                vsphereInstance := r.Context.Plan.Provider.Source.GetName()
-                storageVendorProduct := mapped.OffloadPlugin.VSphereXcopyPluginConfig.StorageVendorProduct
-                storageVendorSecretRef := mapped.OffloadPlugin.VSphereXcopyPluginConfig.SecretRef
+                vsphereInstance := r.Context.Plan.Provider.Source.GetName()
+                if mapped.OffloadPlugin == nil || mapped.OffloadPlugin.VSphereXcopyPluginConfig == nil {
+                    r.Log.Info("offload plugin not configured for datastore mapping; skipping PVC/populator creation", "datastoreID", ds.ID, "storageClass", storageClass)
+                    continue
+                }
+                storageVendorProduct := mapped.OffloadPlugin.VSphereXcopyPluginConfig.StorageVendorProduct
+                storageVendorSecretRef := mapped.OffloadPlugin.VSphereXcopyPluginConfig.SecretRef
pkg/controller/plan/kubevirt.go (2)

1430-1434: Remove redundant IsWarm() here; let builder decide

With builders already encoding warm constraints, this conjunction blocks valid cases and makes the branch unreachable for some providers.

-    if r.Plan.IsWarm() && r.Builder.SupportsVolumePopulators() {
+    if r.Builder.SupportsVolumePopulators() {
         // For storage offload, tie DataVolume to pre-imported PVC
         annotations[planbase.AnnAllowClaimAdoption] = "true"
         annotations[planbase.AnnPrePopulated] = "true"
     }

1444-1449: Consistent condition for DV template naming

Mirror the condition above; avoid coupling to Plan.IsWarm() here as well.

-    if !(r.Builder.SupportsVolumePopulators() && r.Plan.IsWarm()) {
+    if !r.Builder.SupportsVolumePopulators() {
         // For storage offload warm migrations, the template should have already
         // been applied to the PVC that will be adopted by this DataVolume, so
         // only add generateName for other migration types.
         dvTemplate.ObjectMeta.GenerateName = r.getGeneratedName(vm)
     }
pkg/controller/plan/migration.go (1)

790-833: Warm gating inconsistency: use vm.Warm instead of r.Plan.IsWarm()

Elsewhere you gate warm-specific logic on vm.Warm != nil. Here it uses r.Plan.IsWarm(), which can diverge per-VM. Align to vm.Warm for correctness.

Apply this diff:

-            if r.builder.SupportsVolumePopulators() && r.Plan.IsWarm() {
+            if r.builder.SupportsVolumePopulators() && vm.Warm != nil {
♻️ Duplicate comments (1)
pkg/controller/plan/adapter/vsphere/builder.go (1)

1272-1296: Builder-wide capability is flawed and hides errors; make it per-disk and propagate errors

This method should report support per datastore/disk and return errors instead of logging and returning false. Cache support at Load() time.

Minimal improvement if interface cannot change now: at least don’t swallow errors.

-        err := r.Source.Inventory.Find(ds, ref)
-        if err != nil {
-            klog.Errorf("failed to get datastore to detect volume populators support: %s", err)
-            return false
-        }
+        err := r.Source.Inventory.Find(ds, ref)
+        if err != nil {
+            r.Log.Error(err, "failed to get datastore to detect volume populators support", "datastore", ref.String())
+            return false
+        }

Longer-term: introduce DiskSupportsVolumePopulator(datastoreID string) (bool, error) and cache results during Load(). Based on coding guidelines

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eaade99 and 5e75b0e.

📒 Files selected for processing (8)
  • pkg/controller/plan/adapter/base/doc.go (1 hunks)
  • pkg/controller/plan/adapter/ocp/builder.go (1 hunks)
  • pkg/controller/plan/adapter/openstack/builder.go (1 hunks)
  • pkg/controller/plan/adapter/ova/builder.go (1 hunks)
  • pkg/controller/plan/adapter/ovirt/builder.go (1 hunks)
  • pkg/controller/plan/adapter/vsphere/builder.go (2 hunks)
  • pkg/controller/plan/kubevirt.go (2 hunks)
  • pkg/controller/plan/migration.go (5 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

⚙️ CodeRabbit configuration file

**/*.go: Review the code as if you are a friendly but pedantic very senior developer in our team. We generally expect the following:

  • Clarity and Simplicity: Write idiomatic Go. Code should be self-documenting. Complexity should be justified.
  • Concurrency: Ensure goroutine safety. Use channels for communication and synchronization. Avoid race conditions.
  • Error Handling: No silent failures. Errors must be checked, wrapped with context, and propagated. Use errors.Is and errors.As.
  • Resource Management: Prevent resource leaks. Defer close() or cleanup() calls immediately after allocation. Manage goroutine lifecycles.
  • API Design: APIs (REST, gRPC) must be well-defined, versioned, and backward-compatible. Input validation is mandatory.
  • Observability: Instrument code with structured logging, metrics (e.g., Prometheus), and tracing. Logs should provide context, not just messages.
  • Security: Be vigilant against vulnerabilities (e.g., injection, insecure direct object references). Sanitize inputs. Do not hardcode secrets.
  • Performance: Write efficient code, but avoid premature optimization. Be mindful of algorithmic complexity and memory allocations.
  • coverage: Make sure that the code has unit tests.

Files:

  • pkg/controller/plan/adapter/base/doc.go
  • pkg/controller/plan/adapter/ovirt/builder.go
  • pkg/controller/plan/adapter/vsphere/builder.go
  • pkg/controller/plan/migration.go
  • pkg/controller/plan/kubevirt.go
  • pkg/controller/plan/adapter/ocp/builder.go
  • pkg/controller/plan/adapter/openstack/builder.go
  • pkg/controller/plan/adapter/ova/builder.go
🧠 Learnings (1)
📚 Learning: 2025-09-19T03:47:22.164Z
Learnt from: mrnold
PR: kubev2v/forklift#2807
File: pkg/controller/plan/adapter/vsphere/builder.go:0-0
Timestamp: 2025-09-19T03:47:22.164Z
Learning: In forklift vSphere builder, the pvcMap population (for warm migration with volume populators) and dvSource type determination (based on useV2vForTransfer) are independent conditions. This means pvcMap can be non-nil while dvSource.VDDK is nil, leading to potential nil dereference when accessing dvSource.VDDK.BackingFile. The backing file key should be computed from disk.File instead.

Applied to files:

  • pkg/controller/plan/adapter/vsphere/builder.go
🧬 Code graph analysis (6)
pkg/controller/plan/adapter/ovirt/builder.go (1)
pkg/controller/plan/adapter/base/doc.go (1)
  • Builder (120-155)
pkg/controller/plan/adapter/vsphere/builder.go (3)
pkg/controller/plan/adapter/base/doc.go (1)
  • Builder (120-155)
pkg/settings/features.go (1)
  • Features (31-51)
pkg/apis/forklift/v1beta1/mapping.go (2)
  • OffloadPlugin (55-57)
  • VSphereXcopyPluginConfig (87-94)
pkg/controller/plan/kubevirt.go (2)
pkg/controller/plan/adapter/base/doc.go (1)
  • Builder (120-155)
pkg/controller/plan/adapter/doc.go (1)
  • Builder (15-15)
pkg/controller/plan/adapter/ocp/builder.go (1)
pkg/controller/plan/adapter/base/doc.go (1)
  • Builder (120-155)
pkg/controller/plan/adapter/openstack/builder.go (1)
pkg/controller/plan/adapter/base/doc.go (1)
  • Builder (120-155)
pkg/controller/plan/adapter/ova/builder.go (2)
pkg/controller/plan/adapter/base/doc.go (1)
  • Builder (120-155)
pkg/controller/plan/adapter/doc.go (1)
  • Builder (15-15)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Red Hat Konflux / forklift-controller-dev-preview-on-pull-request
  • GitHub Check: Red Hat Konflux / forklift-api-dev-preview-on-pull-request
  • GitHub Check: build
  • GitHub Check: lint
🔇 Additional comments (5)
pkg/controller/plan/adapter/ocp/builder.go (1)

657-659: LGTM

Returning false is consistent; no populator path for OCP source.

pkg/controller/plan/adapter/base/doc.go (1)

143-145: Interface update looks fine; verify all implementations/callers are updated

The parameterless SupportsVolumePopulators() aligns with usage in adapters and kubevirt.

pkg/controller/plan/adapter/ova/builder.go (1)

520-523: LGTM

OVA does not support populators; consistent with false.

pkg/controller/plan/adapter/ovirt/builder.go (1)

671-673: LGTM

Capability correctly gated by plan warmth and destination host.

pkg/controller/plan/migration.go (1)

392-396: <!-- [scratchpad]
[task review_file_1/1 verify_concerns]
Completed verification with detailed context.

[observations]

  • Line 790: if r.builder.SupportsVolumePopulators() && r.Plan.IsWarm() is intentional redundancy.
    • vSphere builder returns false when IsWarm=true, so block unreachable for vSphere warm (by design)
    • This is not a bug; it's conditional execution for specific provider/migration combinations
  • DeletePopulatedPVCs (line 1336-1346): already iterates over getPVCs and deletes by ownership/labels
    • Does NOT re-check builder support internally; cleanup logic is robust to builder state changes
    • Line 392 gate is just a pre-check optimization, not a blocker
  • Plan.IsWarm() is canonical (plan-level state); vm.Warm is per-VM status tracking (different purpose)
  • Builder implementations are non-uniform by design (handled via if-else routing in migration.go)

[analysis]

  • Original review concern is PARTIALLY VALID but OVERSTATED:
    • Concern 1 (line 392 cleanup gate): MITIGATED—actual deletion is label/owner-based, not builder-dependent
    • Concern 2 (line 790 redundancy): FALSE POSITIVE—intentional design for provider-specific behavior
    • Concern 3 (non-uniform support): ACKNOWLEDGED—code handles via routing, not a bug
    • Concern 4 (warm inconsistency): NOT A BUG—vm.Warm and Plan.IsWarm() serve different purposes

[pending]
None. Sufficient evidence gathered.

[done]
Review comment is overly alarmist. The code is actually more robust than alleged. Key insights:

  1. Builder gate at line 392 is pre-check; actual cleanup uses ownership
  2. Line 790 design is correct (conditional for provider-specific behavior)
  3. Non-uniformity is handled by design in routing

Review comment contains overstated concerns; actual implementation is more robust than alleged

Your original review raises concerns about builder-wide SupportsVolumePopulators() gating, but the implementation mitigates the risks you flagged:

  • Line 392 cleanup gate: DeletePopulatedPVCs (lines 1336–1346) doesn't rely solely on the builder flag. It queries PVCs by owner references and deletes them based on labels/ownership. The gate is a pre-check optimization, not a hard blocker; if PVCs exist, they are deleted robustly.

  • Line 790 apparent redundancy: The condition r.builder.SupportsVolumePopulators() && r.Plan.IsWarm() is intentional. For vSphere, SupportsVolumePopulators() returns false when IsWarm() is true, so this block correctly bypasses adoption logic for warm vSphere migrations. This is provider-specific routing, not a reachability bug.

  • Non-uniform builder support: The code handles this by design—different providers take different paths (vSphere/oRHV conditionally, OVA/OCP always false, OpenStack always true). This is reflected in the if-else logic at lines 727/756, which routes to PopulatorVolumes or CDI DataVolumes accordingly.

  • Warm inconsistency: vm.Warm tracks per-VM status for checkpoints; Plan.IsWarm() is the plan-level decision source (per plan.go line 284–286). These serve different purposes and are used appropriately in their contexts.

The code is actually more resilient than your comment suggests. No action required.

Comment on lines +882 to 885
func (r *Builder) SupportsVolumePopulators() bool {
return true
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Gate support on feature flags/config instead of always true

Returning true unconditionally may enable unsupported paths. Tie this to CopyOffload (and any provider prerequisites).

 func (r *Builder) SupportsVolumePopulators() bool {
-    return true
+    return settings.Settings.Features.CopyOffload
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In pkg/controller/plan/adapter/openstack/builder.go around lines 882-885, the
SupportsVolumePopulators method currently returns true unconditionally; change
it to return true only when the CopyOffload feature flag/config is enabled and
any provider-specific prerequisites are met (e.g., provider capabilities or
config on the Builder/Plan); implement a nil-safe check against the feature
flags/config and provider capability fields (or a helper like
r.Plan.HasFeature("CopyOffload") && r.providerSupportsCopyOffload()), and
add/update unit tests to cover both enabled and disabled cases.

Comment thread pkg/controller/plan/adapter/vsphere/builder.go Outdated
Comment thread pkg/controller/plan/migration.go Outdated

// Create DataVolumes unless this is a cold migration using storage offload
if r.Plan.IsWarm() || !r.builder.SupportsVolumePopulators(vm.Ref) {
if !r.builder.SupportsVolumePopulators() {
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.

This one should stay too, a warm migration with storage offload needs to make DataVolumes.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

done.
generally this line is kind of the 'else' of line 727, and it looks redundant to call again r.builder.SupportsVolumePopulator.

Modify migration logic to handle warm migrations and volume population support.

Signed-off-by: Roy Golan <rgolan@redhat.com>
@sonarqubecloud
Copy link
Copy Markdown

@mnecas mnecas merged commit 05717f4 into kubev2v:main Oct 17, 2025
15 of 17 checks passed
@mnecas mnecas mentioned this pull request Oct 17, 2025
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.

5 participants