Skip to content

Reduce vCenter API load for large vSphere clusters#1515

Draft
jcpowermac wants to merge 7 commits into
openshift:mainfrom
jcpowermac:vsphere-perf-improvements
Draft

Reduce vCenter API load for large vSphere clusters#1515
jcpowermac wants to merge 7 commits into
openshift:mainfrom
jcpowermac:vsphere-perf-improvements

Conversation

@jcpowermac

@jcpowermac jcpowermac commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Stable Running machines were fully re-reconciled every 10 minutes with ~25-30 serial vCenter API calls, starving newly created machines of queue time on large clusters. Implements the plan in docs/proposals/vsphere-perf-improvements.md:

  • Short-circuit Exists()/Update() for stable machines via a provider spec hash + 1-hour TTL annotation pair, eliminating vCenter calls entirely for machines that haven't changed.
  • Reuse the machineScope, VM reference, UUID, and power state across Exists()/Update() within a single reconciliation instead of re-fetching them, and convert TaskIDCache to sync.Map.
  • Increase the default SyncPeriod from 10 to 60 minutes with a new --sync-period flag, backstopping drift detection.
  • Wire through --max-concurrent-reconciles (3 for vSphere) so the short-circuit and cache changes can be exploited safely under concurrent reconciliation.

Summary by CodeRabbit

  • New Features

    • Added vSphere runtime configuration for cache sync cadence and max concurrent reconciles (via new CLI flags).
    • Improved reconciliation efficiency by short-circuiting full vCenter reconciliation for stable, running machines.
  • Bug Fixes

    • Hardened caching for concurrent reconciliation to avoid unsafe access.
    • Improved task handling to requeue while vSphere operations are still running.
    • Reduced redundant vCenter lookups and reused previously retrieved VM details during reconciliation.
  • Tests

    • Added unit and concurrency coverage for cache reuse, skip logic, and requeue behavior.

@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jul 7, 2026
@openshift-ci

openshift-ci Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@jcpowermac

Copy link
Copy Markdown
Contributor Author

/test ?

@openshift-ci

openshift-ci Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

[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 theobarberbany 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

@jcpowermac

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Walkthrough

Adds configurable sync-period and reconciliation-concurrency flags to the vSphere controller, migrates actuator caches to concurrent storage, introduces stable-machine reconcile skipping and scope reuse, memoizes VM lookups, returns timed task requeues, and configures vSphere operator concurrency defaults.

Changes

vSphere reconciliation and concurrency

Layer / File(s) Summary
Controller runtime configuration
cmd/vsphere/main.go, pkg/operator/sync.go, pkg/operator/sync_test.go
Adds sync-period and maximum-concurrency flags, wires controller options, migrates the task cache to *sync.Map, and sets vSphere’s operator default to 3 concurrent reconciles.
Actuator scope and task caching
pkg/controller/vsphere/actuator.go, pkg/controller/vsphere/actuator_cache_test.go, pkg/controller/vsphere/actuator_test.go
Reuses scopes between Exists and Update, uses concurrent task caching, cleans cache entries, and tests concurrent cache access.
Stable-machine reconcile skipping
pkg/controller/vsphere/actuator.go, pkg/controller/vsphere/reconcile_skip_test.go
Adds provider-spec hash and timestamp annotations, TTL-based skip checks, and short-circuit behavior for stable machines.
VM memoization and task requeueing
pkg/controller/vsphere/machine_scope.go, pkg/controller/vsphere/reconciler.go, pkg/controller/vsphere/virtualmachine_cache_test.go, pkg/controller/vsphere/reconciler_test.go
Caches VM references, UUIDs, and power states, and returns timed requeue errors for active provider tasks.
Concurrent reconciliation validation
pkg/controller/vsphere/actuator_concurrency_test.go
Runs concurrent simulator-backed Exists/Update operations and verifies cache cleanup.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MachineController
  participant Actuator
  participant ScopeCache
  participant vCenter
  MachineController->>Actuator: Exists(machine)
  Actuator->>Actuator: canSkipFullReconcile(machine)
  Actuator->>vCenter: findVM()
  Actuator->>ScopeCache: Store(machineUID, machineScope)
  MachineController->>Actuator: Update(machine)
  Actuator->>ScopeCache: getOrCreateScope(machineUID)
  Actuator->>vCenter: reconcile VM
  Actuator->>Actuator: markFullReconcileComplete(machine)
Loading
🚥 Pre-merge checks | ✅ 14 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Test Structure And Quality ⚠️ Warning TestTaskRunningReturnsRequeueAfterError still uses context.TODO() in its wait helper and mutates simulator.TaskDelay globally without restoration; the errors.As check can also panic on fail... Use timeout-bearing contexts (or Eventually) in the wait helper, restore simulator.TaskDelay with t.Cleanup/defer, and guard the errors.As result before dereferencing.
✅ 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 matches the main goal: reducing vCenter API load in large vSphere clusters.
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 PASS: No Ginkgo titles were added, and all new test/subtest names are static strings without generated IDs, timestamps, or other run-specific data.
Microshift Test Compatibility ✅ Passed No new Ginkgo/e2e tests were added; the changed tests are plain Go unit tests, and none reference MicroShift-blocked OpenShift APIs or namespaces.
Single Node Openshift (Sno) Test Compatibility ✅ Passed PASS: The PR only changes vSphere controller/unit test code; no new Ginkgo e2e tests or multi-node/SNO-sensitive assumptions were added.
Topology-Aware Scheduling Compatibility ✅ Passed The PR only changes controller concurrency/cache behavior; it adds no new anti-affinity, topology spread, nodeSelector, or replica logic.
Ote Binary Stdout Contract ✅ Passed No new stdout writes were introduced in main/init/suite setup; klog stays on stderr and the PR only adds flags/caches.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No new Ginkgo e2e tests were added; the changed tests are standard Go unit tests using govmomi simulator/fake clients, with no external connectivity requirement.
No-Weak-Crypto ✅ Passed PR uses only SHA-256 (standard library, cryptographically strong) for non-security provider spec change detection. No weak crypto (MD5, SHA1, DES, RC4, 3DES, Blowfish, ECB) or custom crypto impleme...
Container-Privileges ✅ Passed Current commit only changes vSphere actuator/reconciler logic; no touched manifest or securityContext fields set privileged, hostNetwork, hostPID/IPC, SYS_ADMIN, or allowPrivilegeEscalation.
No-Sensitive-Data-In-Logs ✅ Passed No sensitive data (passwords, tokens, API keys, PII, session IDs) is logged. All logging uses safe values like machine names, namespace names, and status information only.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

Error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions
The command is terminated due to an error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions


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

@jcpowermac

Copy link
Copy Markdown
Contributor Author

/test e2e-vsphere-ovn-multi-vcenter
/test unit

@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.

🧹 Nitpick comments (2)
pkg/controller/vsphere/reconciler.go (1)

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

Minor inconsistency: empty-string sentinel for cachedUUID.

Unlike cachedPowerState (pointer, unambiguous "unset" state), cachedUUID uses "" as the sentinel. If vm.Obj.UUID() legitimately returns empty (VM not fully provisioned), caching is silently skipped every call — safe, but inconsistent with the pattern used for power state.

🤖 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 `@pkg/controller/vsphere/reconciler.go` around lines 1498 - 1517, The getUUID
cache in virtualMachine currently uses an empty string sentinel, which makes
repeated calls re-fetch UUIDs when object UUID() legitimately returns empty.
Update getUUID and the cachedUUID field to use an explicit “set/unset” pattern
consistent with cachedPowerState (for example, a separate boolean or
pointer-like sentinel) so a real empty UUID is still cached and returned without
re-querying. Keep the change localized to virtualMachine and getUUID so the
memoization behavior matches the existing caching style.
cmd/vsphere/main.go (1)

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

Consider renaming timeout now that its only use is as the --sync-period default.

The generic name risks confusion with other unrelated timeouts (context/HTTP) as the file grows.

✏️ Suggested rename
-const timeout = 60 * time.Minute
+const defaultSyncPeriod = 60 * time.Minute

And update the reference at Line 105 accordingly.

🤖 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 `@cmd/vsphere/main.go` at line 38, Rename the generic timeout constant used by
the vSphere CLI default so it clearly reflects that it is the default sync
period rather than a request or context timeout. Update the constant in the main
package and the `--sync-period` default reference in the CLI setup so both use
the new name consistently.
🤖 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.

Nitpick comments:
In `@cmd/vsphere/main.go`:
- Line 38: Rename the generic timeout constant used by the vSphere CLI default
so it clearly reflects that it is the default sync period rather than a request
or context timeout. Update the constant in the main package and the
`--sync-period` default reference in the CLI setup so both use the new name
consistently.

In `@pkg/controller/vsphere/reconciler.go`:
- Around line 1498-1517: The getUUID cache in virtualMachine currently uses an
empty string sentinel, which makes repeated calls re-fetch UUIDs when object
UUID() legitimately returns empty. Update getUUID and the cachedUUID field to
use an explicit “set/unset” pattern consistent with cachedPowerState (for
example, a separate boolean or pointer-like sentinel) so a real empty UUID is
still cached and returned without re-querying. Keep the change localized to
virtualMachine and getUUID so the memoization behavior matches the existing
caching style.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: fb6d1f73-584b-4e2d-8b5a-47b4eb629123

📥 Commits

Reviewing files that changed from the base of the PR and between c0b2d2c and 07c6232.

📒 Files selected for processing (12)
  • cmd/vsphere/main.go
  • docs/proposals/vsphere-perf-improvements.md
  • pkg/controller/vsphere/actuator.go
  • pkg/controller/vsphere/actuator_cache_test.go
  • pkg/controller/vsphere/actuator_concurrency_test.go
  • pkg/controller/vsphere/actuator_test.go
  • pkg/controller/vsphere/machine_scope.go
  • pkg/controller/vsphere/reconcile_skip_test.go
  • pkg/controller/vsphere/reconciler.go
  • pkg/controller/vsphere/virtualmachine_cache_test.go
  • pkg/operator/sync.go
  • pkg/operator/sync_test.go

jcpowermac and others added 6 commits July 7, 2026 18:07
Machines that are fully stable (Phase=Running, ProviderID set, NodeRef
present, addresses populated, not deleting) and whose provider spec has
not changed since the last full reconciliation are short-circuited in
both Exists() and Update(), avoiding all vCenter API calls. A SHA-256
hash of the provider spec is stored in an annotation to detect spec
changes, and an RFC3339 timestamp annotation enforces a 1-hour TTL to
guarantee periodic drift detection regardless of spec changes.

For a 200-machine stable cluster this eliminates ~30,000 unnecessary
vCenter API calls per hour, reducing new-machine queue wait from 10+
minutes to near-zero.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change the informer resync interval from 10 minutes to 60 minutes,
reducing the frequency of resync-driven reconciliation 6x. Combined
with the per-machine 1-hour TTL from the short-circuit annotation,
stable machines get exactly 1 full reconciliation per hour.

A --sync-period flag is added for operational flexibility. Watch-driven
reconciliation (spec changes, status updates) still fires immediately
regardless of this setting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cache machineScope between Exists() and Update() via a sync.Map-based
scopeCache on the Actuator, avoiding redundant session validation and
credential reads. Cache the VM reference from exists() for reuse in
update(), eliminating a duplicate FindByUuid call. Cache UUID() and
getPowerState() results on the virtualMachine struct to avoid duplicate
property collector calls within reconcileMachineWithCloudState.

Convert TaskIDCache from map[string]string to *sync.Map to make it safe
for concurrent reconciliation.

Combined savings: ~5 SOAP + 2 REST calls per full reconciliation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a --max-concurrent-reconciles flag to the vSphere machine controller
binary, defaulting to 1, and pass it through to controller-runtime via
AddWithActuatorOpts. The operator (sync.go) sets it to 3 for vSphere,
conservative compared to Azure/GCP's 10 since vCenter is a shared
appliance with lower API throughput.

Combined with the short-circuit from commit 1, stable machines complete
reconciliation in microseconds, so 3 workers handles concurrent creation
bursts without multiplying vCenter API load.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change the default value of --max-concurrent-reconciles from 1 to 3 so
the concurrency improvement takes effect without requiring the operator
to pass the flag. This commit should be dropped before merge.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename the generic 'timeout' constant to 'defaultSyncPeriod' to clarify
its purpose as the informer resync interval default.

Change cachedUUID from string to *string so that a legitimately empty
UUID is cached and not re-fetched, matching the cachedPowerState pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jcpowermac jcpowermac force-pushed the vsphere-perf-improvements branch from 07c6232 to 66146f4 Compare July 7, 2026 22:17
@jcpowermac

Copy link
Copy Markdown
Contributor Author

/test e2e-vsphere-ovn-multi-vcenter
/test e2e-vsphere-ovn
/test unit

@openshift-ci

openshift-ci Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

@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-ovn-multi-vcenter 66146f4 link false /test e2e-vsphere-ovn-multi-vcenter

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.

When a vCenter task (clone, power-on, delete) is still running,
reconciler.go returned a plain error, triggering controller-runtime's
5ms exponential backoff. This produced rapid-fire GetTask() API calls
and spurious FailedCreate/Update/Delete Warning events for normal
task-in-progress polling.

Return RequeueAfterError (20s fixed interval) instead, and short-circuit
before handleMachineError in the Actuator to suppress Warning events and
Error logs for expected intermediate state — mirroring the existing
TaskIDCache pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@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: 3

Caution

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

⚠️ Outside diff range comments (1)
pkg/controller/vsphere/reconciler.go (1)

1509-1524: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clear cached power state after powerOffVM(). In the delete flow, setProviderStatus(..., vm) reuses the same wrapper after a power transition, so getPowerState() can return the pre-off value from cachedPowerState. Reset the cache after a successful power-off, or recreate the wrapper before reading state again.

🤖 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 `@pkg/controller/vsphere/reconciler.go` around lines 1509 - 1524, Clear the
stale power-state cache after a successful power transition in the delete flow:
update powerOffVM() to set the virtualMachine.cachedPowerState cache to nil once
the VM is powered off, before setProviderStatus(..., vm) reads it again.
Preserve the cache on failure and use the existing cachedPowerState field and
powerOffVM symbol to locate the change.
🧹 Nitpick comments (1)
pkg/controller/vsphere/actuator_test.go (1)

429-452: 🎯 Functional Correctness | 🔵 Trivial | 🏗️ Heavy lift

Exercise the actuator path, not only %w unwrapping.

These subtests only construct reconcilerFailFmt and call errors.As; they never invoke Actuator.Create, Update, or Delete, nor verify that handleMachineError is bypassed. The test would still pass if the short-circuit were removed. Either rename it to reflect the narrower wrapping contract or add an actuator-level regression assertion.

🤖 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 `@pkg/controller/vsphere/actuator_test.go` around lines 429 - 452, The test
only verifies fmt.Errorf wrapping and does not exercise the actuator
short-circuit behavior. Extend
TestRequeueAfterErrorShortCircuitsHandleMachineError to invoke the relevant
Actuator Create, Update, or Delete path with a RequeueAfterError and assert
handleMachineError is bypassed, or rename the test to describe only the
reconcilerFailFmt unwrapping contract.
🤖 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 `@pkg/controller/vsphere/reconciler_test.go`:
- Around line 3708-3710: Guard the RequeueAfterError assertion in the test
before dereferencing requeueErr: make errors.As a fatal assertion or explicitly
verify requeueErr is non-nil, then assert RequeueAfter only after that check.
- Line 3714: Bound waitForTaskToComplete with a timeout so simulator regressions
cannot hang the test suite indefinitely. Update waitForTaskToComplete to accept
and use a timeout-bearing context for both session.GetTask and taskObj.Wait, or
wrap its invocation with Eventually using an explicit timeout.
- Around line 3592-3594: Update the clone-task test setup around
simulator.TaskDelay.MethodDelay to avoid relying on the 500ms delay: save the
existing MethodDelay map, register t.Cleanup or defer to restore it after the
test, and use a deterministic mechanism to keep the clone task pending until the
second create() rather than elapsed timing. Remove the post-t.Run assignment to
nil and ensure the original simulator state is restored even when the test
fails.

---

Outside diff comments:
In `@pkg/controller/vsphere/reconciler.go`:
- Around line 1509-1524: Clear the stale power-state cache after a successful
power transition in the delete flow: update powerOffVM() to set the
virtualMachine.cachedPowerState cache to nil once the VM is powered off, before
setProviderStatus(..., vm) reads it again. Preserve the cache on failure and use
the existing cachedPowerState field and powerOffVM symbol to locate the change.

---

Nitpick comments:
In `@pkg/controller/vsphere/actuator_test.go`:
- Around line 429-452: The test only verifies fmt.Errorf wrapping and does not
exercise the actuator short-circuit behavior. Extend
TestRequeueAfterErrorShortCircuitsHandleMachineError to invoke the relevant
Actuator Create, Update, or Delete path with a RequeueAfterError and assert
handleMachineError is bypassed, or rename the test to describe only the
reconcilerFailFmt unwrapping contract.
🪄 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: 89164d3f-44f6-42ca-a95b-e867bf963cf1

📥 Commits

Reviewing files that changed from the base of the PR and between 66146f4 and 666b13f.

📒 Files selected for processing (4)
  • pkg/controller/vsphere/actuator.go
  • pkg/controller/vsphere/actuator_test.go
  • pkg/controller/vsphere/reconciler.go
  • pkg/controller/vsphere/reconciler_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/controller/vsphere/actuator.go

Comment on lines +3592 to +3594
simulator.TaskDelay.MethodDelay = map[string]int{
"CloneVM": 500,
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

set -euo pipefail

# Inspect the relevant sections of the test file
sed -n '3540,3665p' pkg/controller/vsphere/reconciler_test.go
printf '\n---\n'
sed -n '3680,3755p' pkg/controller/vsphere/reconciler_test.go

printf '\n=== SEARCH ===\n'
rg -n "TaskDelay\.MethodDelay|CloneVM|SetDelay|MethodDelay =" pkg/controller/vsphere/reconciler_test.go pkg -g '!**/vendor/**'

Repository: openshift/machine-api-operator

Length of output: 5910


🏁 Script executed:

set -euo pipefail

# Inspect simulator.TaskDelay definition and any other use patterns.
rg -n "type .*TaskDelay|var TaskDelay|MethodDelay|TaskDelay" "$(go env GOPATH 2>/dev/null || true)" -g '!**/vendor/**' || true

# Search within the module cache is unreliable; inspect repo references too.
rg -n "TaskDelay|MethodDelay" . -g '!**/vendor/**' -g '!**/dist/**' -g '!**/build/**'

Repository: openshift/machine-api-operator

Length of output: 2489


🏁 Script executed:

set -euo pipefail

# Search the repository for all direct references to simulator.TaskDelay / MethodDelay.
rg -n "TaskDelay|MethodDelay" . -g '!**/vendor/**' -g '!**/dist/**' -g '!**/build/**'

# If the simulator package is vendored, inspect the declaration there.
fd -a "simulator" vendor . 2>/dev/null | head -n 20

Repository: openshift/machine-api-operator

Length of output: 1716


🏁 Script executed:

set -euo pipefail

# Inspect the nearby test that also mutates MethodDelay and whether it restores state.
sed -n '240,330p' pkg/controller/vsphere/session/session_test.go

# Inspect the TaskDelay implementation in vendored govmomi to understand nil/restore semantics.
sed -n '1,120p' vendor/github.com/vmware/govmomi/simulator/task.go
sed -n '1,120p' vendor/github.com/vmware/govmomi/simulator/model.go

Repository: openshift/machine-api-operator

Length of output: 10869


🏁 Script executed:

set -euo pipefail

# Check whether the vsphere reconciler tests use parallelism or other shared-state patterns.
rg -n "Parallel\(|t\.Parallel\(|RunParallel|TaskDelay\.MethodDelay" pkg/controller/vsphere -g '!**/vendor/**'

# Show the exact reconciler test block with line numbers for context.
cat -n pkg/controller/vsphere/reconciler_test.go | sed -n '3580,3725p'

Repository: openshift/machine-api-operator

Length of output: 5851


🏁 Script executed:

set -euo pipefail

# Inspect the nearby test that also mutates MethodDelay and whether it restores state.
cat -n pkg/controller/vsphere/session/session_test.go | sed -n '260,310p'

# Inspect the vendored implementation to confirm what nil means for MethodDelay.
cat -n vendor/github.com/vmware/govmomi/simulator/task.go | sed -n '1,140p'
cat -n vendor/github.com/vmware/govmomi/simulator/model.go | sed -n '1,80p'

Repository: openshift/machine-api-operator

Length of output: 10309


🏁 Script executed:

set -euo pipefail

# Confirm whether this package's tests run sequentially and whether cleanup should be deferred here.
cat -n pkg/controller/vsphere/session/session_test.go | sed -n '260,310p'
printf '\n---\n'
cat -n vendor/github.com/vmware/govmomi/simulator/task.go | sed -n '1,140p'
printf '\n---\n'
cat -n vendor/github.com/vmware/govmomi/simulator/model.go | sed -n '1,80p'

Repository: openshift/machine-api-operator

Length of output: 10319


Make the clone-task test less timing-sensitive and restore simulator state in cleanup.
The 500ms CloneVM delay still depends on elapsed time to keep the task running, so a slow CI worker can let the task finish before the second create(). Save the previous simulator.TaskDelay.MethodDelay and restore it with defer/t.Cleanup instead of assigning nil after t.Run.

🤖 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 `@pkg/controller/vsphere/reconciler_test.go` around lines 3592 - 3594, Update
the clone-task test setup around simulator.TaskDelay.MethodDelay to avoid
relying on the 500ms delay: save the existing MethodDelay map, register
t.Cleanup or defer to restore it after the test, and use a deterministic
mechanism to keep the clone task pending until the second create() rather than
elapsed timing. Remove the post-t.Run assignment to nil and ensure the original
simulator state is restored even when the test fails.

Comment on lines +3708 to +3710
var requeueErr *machinecontroller.RequeueAfterError
g.Expect(errors.As(err, &requeueErr)).To(BeTrue(), "expected RequeueAfterError, got: %v", err)
g.Expect(requeueErr.RequeueAfter).To(Equal(time.Duration(requeueAfterSeconds) * time.Second))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '3675,3725p' pkg/controller/vsphere/reconciler_test.go | cat -n

Repository: openshift/machine-api-operator

Length of output: 2351


Guard the RequeueAfterError assertion before dereferencing it. If errors.As fails, requeueErr stays nil and the next line can panic; use a fatal assertion or check requeueErr before reading RequeueAfter.

🤖 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 `@pkg/controller/vsphere/reconciler_test.go` around lines 3708 - 3710, Guard
the RequeueAfterError assertion in the test before dereferencing requeueErr:
make errors.As a fatal assertion or explicitly verify requeueErr is non-nil,
then assert RequeueAfter only after that check.


// Wait for the clone task to complete before exiting, so the simulator
// goroutine finishes and doesn't race with clearing TaskDelay.
g.Expect(waitForTaskToComplete(session, reconciler)).To(Succeed())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the helper and its usages
rg -n "waitForTaskToComplete" pkg/controller/vsphere/reconciler_test.go

# Show the function implementation and nearby context
line=$(rg -n "func waitForTaskToComplete" pkg/controller/vsphere/reconciler_test.go | head -n1 | cut -d: -f1)
if [ -n "${line:-}" ]; then
  start=$((line-30))
  end=$((line+120))
  sed -n "${start},${end}p" pkg/controller/vsphere/reconciler_test.go
fi

Repository: openshift/machine-api-operator

Length of output: 4638


Bound the task wait. waitForTaskToComplete uses context.TODO() for both session.GetTask and taskObj.Wait, so a simulator regression can stall the suite indefinitely. Pass a timeout-bearing context through this helper or wrap the wait in Eventually.

🤖 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 `@pkg/controller/vsphere/reconciler_test.go` at line 3714, Bound
waitForTaskToComplete with a timeout so simulator regressions cannot hang the
test suite indefinitely. Update waitForTaskToComplete to accept and use a
timeout-bearing context for both session.GetTask and taskObj.Wait, or wrap its
invocation with Eventually using an explicit timeout.

Source: Path instructions

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

Labels

do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant