Skip to content

SPLAT-2813: Create VAP E2Es#1517

Draft
vr4manta wants to merge 1 commit into
openshift:mainfrom
vr4manta:SPLAT-2813
Draft

SPLAT-2813: Create VAP E2Es#1517
vr4manta wants to merge 1 commit into
openshift:mainfrom
vr4manta:SPLAT-2813

Conversation

@vr4manta

@vr4manta vr4manta commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

SPLAT-2813

Changes

  • Created E2Es for new vSphere VAP

Summary by CodeRabbit

  • Tests
    • Added end-to-end coverage for validating vSphere failure domain changes in validating admission policies.
    • Verified that unused vSphere failure domains can be removed successfully.
    • Verified that removal is blocked when the failure domain is referenced by a Machine, MachineSet, or ControlPlaneMachineSet.
    • Added coverage confirming removal succeeds after the referencing MachineSet is deleted and fully cleaned up.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Jul 14, 2026
@openshift-ci-robot

openshift-ci-robot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

@vr4manta: This pull request references SPLAT-2813 which is a valid jira issue.

Details

In response to this:

SPLAT-2813

Changes

  • Created E2Es for new vSphere VAP

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 openshift-eng/jira-lifecycle-plugin repository.

@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 14, 2026
@openshift-ci

openshift-ci Bot commented Jul 14, 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

@coderabbitai

coderabbitai Bot commented Jul 14, 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: bbef37ae-a7ee-474c-82df-0522f39bae6a

📥 Commits

Reviewing files that changed from the base of the PR and between f89a6ba and 6d353ef.

📒 Files selected for processing (1)
  • test/e2e/vsphere/failure_domain_vap.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/e2e/vsphere/failure_domain_vap.go

Walkthrough

Adds a vSphere end-to-end test suite for failure-domain ValidatingAdmissionPolicies. The tests verify policy deployment and Infrastructure failure-domain removal behavior when domains are referenced by Machines, MachineSets, or ControlPlaneMachineSets.

Changes

vSphere failure-domain VAP tests

Layer / File(s) Summary
Failure-domain selection and test resource helpers
test/e2e/vsphere/failure_domain_vap.go
Adds helpers to remove failure domains, identify domains referenced by existing resources, select ControlPlaneMachineSet domains, and create zero-replica MachineSets targeting specific domains.
VAP deployment verification
test/e2e/vsphere/failure_domain_vap.go
Verifies that three vSphere failure-domain ValidatingAdmissionPolicies and their corresponding bindings are deployed.
Failure-domain admission flows
test/e2e/vsphere/failure_domain_vap.go
Covers permitted removal of unused domains, rejection of domains referenced by Machines, MachineSets, or ControlPlaneMachineSets, and successful removal after deleting a referencing MachineSet.

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

Sequence Diagram(s)

sequenceDiagram
  participant Ginkgo
  participant Infrastructure
  participant AdmissionPolicy
  participant MachineSet
  participant ControlPlaneMachineSet
  Ginkgo->>Infrastructure: Request failure-domain removal
  Infrastructure->>AdmissionPolicy: Validate update
  AdmissionPolicy->>MachineSet: Check failure-domain references
  AdmissionPolicy->>ControlPlaneMachineSet: Check failure-domain references
  AdmissionPolicy-->>Infrastructure: Allow or deny update
  Infrastructure-->>Ginkgo: Return update result
Loading
🚥 Pre-merge checks | ✅ 14 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Test Structure And Quality ⚠️ Warning FAIL: The suite mutates cluster-scoped Infrastructure in several tests without deferred restore; if an update unexpectedly succeeds or a later step fails, state can leak. Some setup expects also la... Add DeferCleanup/AfterEach to always restore Infrastructure after any mutation, and wrap created test MachineSets/other cluster-scoped resources so cleanup runs even on failure. Add context to bare setup assertions.
✅ 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 matches the PR’s main change: adding vSphere VAP end-to-end tests.
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 new Ginkgo titles are static strings; dynamic fmt.Sprintf usage appears only in By/Skip messages, not test names.
Microshift Test Compatibility ✅ Passed All new e2e tests are tagged [apigroup:machine.openshift.io], which MicroShift auto-skips; no unguarded MicroShift-incompatible test was added.
Single Node Openshift (Sno) Test Compatibility ✅ Passed No node-count, scheduling, or HA failover assumptions; tests either use OpenShift APIs or skip when worker FDs/MachineSets are absent.
Topology-Aware Scheduling Compatibility ✅ Passed Only a new e2e test file was added; no deployment manifests, operator code, or controllers were modified, and no scheduling constraints were introduced.
Ote Binary Stdout Contract ✅ Passed PASS: The new vsphere e2e file has no main/init/TestMain/BeforeSuite code or stdout writes; all formatting/logging is inside It/DeferCleanup blocks.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed The new Ginkgo tests only use cluster API clients and failure-domain labels; no IPv4 literals, IP parsing, host URL construction, or external connectivity.
No-Weak-Crypto ✅ Passed New file is e2e test code only; searches found no MD5/SHA1/DES/RC4/3DES/Blowfish/ECB, no custom crypto, and no secret/token comparisons.
Container-Privileges ✅ Passed The PR only adds a Go e2e test; no K8s/container manifests or privileged fields are present in the diff.
No-Sensitive-Data-In-Logs ✅ Passed The new test file only logs generic cleanup warnings and test resource names; no passwords, tokens, PII, session IDs, hostnames, or customer data were found.
✨ 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.

@vr4manta

Copy link
Copy Markdown
Contributor Author

/payload-job periodic-ci-openshift-release-main-nightly-5.0-e2e-vsphere-ovn-zones

@openshift-ci

openshift-ci Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

@vr4manta: trigger 1 job(s) for the /payload-(with-prs|job|aggregate|job-with-prs|aggregate-with-prs) command

  • periodic-ci-openshift-release-main-nightly-5.0-e2e-vsphere-ovn-zones

See details on https://pr-payload-tests.ci.openshift.org/runs/ci/81721120-7f9f-11f1-941e-018542fca9e5-0

@openshift-ci

openshift-ci Bot commented Jul 14, 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 radekmanak for approval. For more information see the Code Review Process.

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

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

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
test/e2e/vsphere/failure_domain_vap.go (3)

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

Local variable shadows builtin copy.

Naming the DeepCopy() result copy shadows the built-in copy() function within this scope. Harmless here since the builtin isn't otherwise needed, but worth avoiding for clarity.

♻️ Rename to avoid shadowing
-	copy := infra.DeepCopy()
-	fds := copy.Spec.PlatformSpec.VSphere.FailureDomains
+	infraCopy := infra.DeepCopy()
+	fds := infraCopy.Spec.PlatformSpec.VSphere.FailureDomains
 	filtered := fds[:0]
 	for _, fd := range fds {
 		if fd.Name != fdName {
 			filtered = append(filtered, fd)
 		}
 	}
-	copy.Spec.PlatformSpec.VSphere.FailureDomains = filtered
-	return copy
+	infraCopy.Spec.PlatformSpec.VSphere.FailureDomains = filtered
+	return infraCopy
🤖 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 `@test/e2e/vsphere/failure_domain_vap.go` around lines 34 - 48, Rename the
DeepCopy() result local variable in infraWithFDRemoved from copy to a
non-shadowing name, and update its subsequent references when accessing and
assigning Spec.PlatformSpec.VSphere.FailureDomains. Preserve the existing
filtering behavior.

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

defer GinkgoRecover() at the top of the Describe closure has no effect.

This closure runs once during spec-tree construction, not during spec execution, so the deferred recover doesn't protect the It/BeforeEach bodies where assertions actually run. GinkgoRecover() is only needed inside goroutines spawned from spec code; none are spawned in this file, so this line is effectively dead code.

♻️ Remove the ineffective defer
 	func() {
-		defer GinkgoRecover()
-
 		ctx := context.Background()
🤖 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 `@test/e2e/vsphere/failure_domain_vap.go` at line 234, Remove the ineffective
defer GinkgoRecover() from the Describe closure in failure_domain_vap.go; no
replacement is needed because this file does not spawn goroutines requiring
recovery.

77-78: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Region/zone label keys hardcoded and duplicated across the file.

The literal strings "machine.openshift.io/region" and "machine.openshift.io/zone" are repeated at least seven times (lines 77-78, 120-121, 127-128, 214-215, 323-324, 334-335, 462-463). These same labels are read by the CEL-based VAPs in pkg/webhooks/vap.go via machineRegionLabel/machineZoneLabel constants. Duplicating the literals here risks silent drift (a typo just causes a Skip or a false pass instead of a compile error), and any future rename in the webhooks package would require finding every copy in this test file.

Extract local package-level constants for these two labels and reference them everywhere in this file, mirroring the naming used in pkg/webhooks/vap.go to make the coupling explicit.

const (
	machineRegionLabel = "machine.openshift.io/region"
	machineZoneLabel   = "machine.openshift.io/zone"
)

Also applies to: 120-121, 127-128, 214-215, 323-324, 334-335, 462-463

🤖 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 `@test/e2e/vsphere/failure_domain_vap.go` around lines 77 - 78, Define
package-level machineRegionLabel and machineZoneLabel constants in
test/e2e/vsphere/failure_domain_vap.go, matching the values and names used by
the VAP implementation, then replace every repeated region and zone label
literal throughout the file with these constants.
🤖 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 `@test/e2e/vsphere/failure_domain_vap.go`:
- Around line 379-380: Update the error assertions in the Machine and MachineSet
VAP tests to use their exact MessageExpression phrasing, such as “in use by
Machine '” and “in use by MachineSet '”, instead of the ambiguous “Machine” and
“MachineSet” substrings; leave the ControlPlaneMachineSet assertion unchanged.
- Around line 91-160: Update findFDUsedByCPMS to identify when its returned
failure domain comes from the worker-shared fallback rather than being
CPMS-only, and propagate that ambiguity to the callers around the CPMS admission
assertion. When no CPMS-only FD exists, skip or relax the expected
“ControlPlaneMachineSet” substring check while preserving the strict assertion
for an exclusive FD.

---

Nitpick comments:
In `@test/e2e/vsphere/failure_domain_vap.go`:
- Around line 34-48: Rename the DeepCopy() result local variable in
infraWithFDRemoved from copy to a non-shadowing name, and update its subsequent
references when accessing and assigning
Spec.PlatformSpec.VSphere.FailureDomains. Preserve the existing filtering
behavior.
- Line 234: Remove the ineffective defer GinkgoRecover() from the Describe
closure in failure_domain_vap.go; no replacement is needed because this file
does not spawn goroutines requiring recovery.
- Around line 77-78: Define package-level machineRegionLabel and
machineZoneLabel constants in test/e2e/vsphere/failure_domain_vap.go, matching
the values and names used by the VAP implementation, then replace every repeated
region and zone label literal throughout the file with these constants.
🪄 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: 692848d5-6190-4e89-856f-4cf264817a6a

📥 Commits

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

📒 Files selected for processing (1)
  • test/e2e/vsphere/failure_domain_vap.go

Comment on lines +91 to +160
// findFDUsedByCPMS returns the name of a vSphere failure domain that is referenced by the
// ControlPlaneMachineSet but is NOT referenced by any worker Machine or MachineSet. Picking
// such an FD ensures the CPMS VAP fires first when we attempt to remove it, producing the
// "ControlPlaneMachineSet" error message we assert on. If every CPMS FD is also used by a
// worker resource, the function falls back to returning the first CPMS FD regardless — in
// that case the test caller should still pass since one of the VAPs will block the removal,
// though the error may mention a different resource type.
//
// Returns ("", false) when the CPMS has no vsphere failure domain entries.
func findFDUsedByCPMS(
cpms *machinev1.ControlPlaneMachineSet,
infra *configv1.Infrastructure,
machines *machinev1beta1.MachineList,
machineSets *machinev1beta1.MachineSetList,
) (string, bool) {
template := cpms.Spec.Template.OpenShiftMachineV1Beta1Machine
if template == nil {
return "", false
}
cpmsFDs := template.FailureDomains
if cpmsFDs == nil || len(cpmsFDs.VSphere) == 0 {
return "", false
}

// Build a set of (region, zone) pairs used by worker Machines and MachineSets so we can
// avoid picking an FD that would also trigger the Machine or MachineSet VAP.
type regionZone struct{ region, zone string }
workerFDs := make(map[regionZone]bool)
for _, m := range machines.Items {
r := m.Labels["machine.openshift.io/region"]
z := m.Labels["machine.openshift.io/zone"]
if r != "" && z != "" {
workerFDs[regionZone{r, z}] = true
}
}
for _, ms := range machineSets.Items {
r := ms.Spec.Template.ObjectMeta.Labels["machine.openshift.io/region"]
z := ms.Spec.Template.ObjectMeta.Labels["machine.openshift.io/zone"]
if r != "" && z != "" {
workerFDs[regionZone{r, z}] = true
}
}

// Build a lookup of infra FD name → spec for quick resolution.
infraFDByName := make(map[string]configv1.VSpherePlatformFailureDomainSpec)
for _, fd := range infra.Spec.PlatformSpec.VSphere.FailureDomains {
infraFDByName[fd.Name] = fd
}

var firstMatch string
for _, cpmsFD := range cpmsFDs.VSphere {
infraFD, ok := infraFDByName[cpmsFD.Name]
if !ok {
continue // CPMS references an FD not present in infra — skip.
}
if firstMatch == "" {
firstMatch = infraFD.Name // record the first valid match as fallback
}
// Prefer an FD not shared with any worker Machine or MachineSet.
if !workerFDs[regionZone{infraFD.Region, infraFD.Zone}] {
return infraFD.Name, true
}
}

// Fall back to the first CPMS FD that exists in infra, even if workers also use it.
if firstMatch != "" {
return firstMatch, 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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file map =="
git ls-files 'test/e2e/vsphere/failure_domain_vap.go' 'test/e2e/vsphere/*' | sed -n '1,120p'

echo
echo "== outline for target file =="
ast-grep outline test/e2e/vsphere/failure_domain_vap.go --view expanded || true

echo
echo "== relevant line ranges =="
sed -n '1,260p' test/e2e/vsphere/failure_domain_vap.go | cat -n | sed -n '1,260p'

echo
echo "== search for helper usage and substring assertion =="
rg -n "findFDUsedByCPMS|ControlPlaneMachineSet|ContainSubstring" test/e2e/vsphere/failure_domain_vap.go test/e2e/vsphere -S

Repository: openshift/machine-api-operator

Length of output: 16253


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Searching target file and assertion context...\n'
rg -n -C 3 'findFDUsedByCPMS|ControlPlaneMachineSet|ContainSubstring' test/e2e/vsphere/failure_domain_vap.go

Repository: openshift/machine-api-operator

Length of output: 4071


🌐 Web query:

Kubernetes ValidatingAdmissionPolicy multiple matching policies denial error message aggregated policy names

💡 Result:

In Kubernetes, when multiple ValidatingAdmissionPolicy bindings apply to a single request, the API server evaluates each binding. By design, the admission control process for ValidatingAdmissionPolicy typically stops evaluation after the first encountered validation failure that results in a denial (Deny action) [1][2]. Consequently, error messages returned to the client generally reflect only the first failed validation from the first policy that triggered a denial [2][3][4]. Key points regarding the behavior and aggregation of these errors include: 1. Stopping at the First Failure: The evaluation engine is optimized to stop at the first denial to prevent unnecessary processing and to limit information exposure, which is particularly relevant for security constraints [2]. 2. Error Message Content: The standard error response provided to the user includes the name of the specific ValidatingAdmissionPolicy and the Binding that caused the denial, along with the associated failure message [1][2][3]. 3. Audit vs. API Response: While API response errors remain restricted to the first failure, improvements have been made to audit logging. Recent updates to the Kubernetes API server have addressed cases where multiple audit-action failures were previously dropped; for instance, failures are now buffered and aggregated into a single annotation in audit logs to ensure all audit-related policy failures are captured [5]. 4. Developer Context: There has been active community discussion regarding surfacing all validation errors to the client rather than just the first one [2][6]. While this is a recognized desire—particularly for improving the user experience and reducing the trial-and-error cycle—current stable behavior remains focused on the first denial [2][6]. In summary, you cannot currently expect an aggregated list of multiple policy denial messages in the direct API error response to your client; the response will be dominated by the first failure that triggered the denial [1][2][4].

Citations:


Guard the CPMS check on the shared-FD fallback path. In test/e2e/vsphere/failure_domain_vap.go:100-157, 429-441, findFDUsedByCPMS can fall back to a failure domain that is also used by a worker Machine/MachineSet, so the admission error may mention Machine or MachineSet instead of ControlPlaneMachineSet. Return a flag for that ambiguous fallback and skip or relax the substring assertion when no CPMS-only FD exists.

🤖 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 `@test/e2e/vsphere/failure_domain_vap.go` around lines 91 - 160, Update
findFDUsedByCPMS to identify when its returned failure domain comes from the
worker-shared fallback rather than being CPMS-only, and propagate that ambiguity
to the callers around the CPMS admission assertion. When no CPMS-only FD exists,
skip or relax the expected “ControlPlaneMachineSet” substring check while
preserving the strict assertion for an exclusive FD.

Comment thread test/e2e/vsphere/failure_domain_vap.go Outdated
@vr4manta

Copy link
Copy Markdown
Contributor Author

/payload-job periodic-ci-openshift-release-main-nightly-5.0-e2e-vsphere-ovn-techpreview

@openshift-ci

openshift-ci Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

@vr4manta: trigger 1 job(s) for the /payload-(with-prs|job|aggregate|job-with-prs|aggregate-with-prs) command

  • periodic-ci-openshift-release-main-nightly-5.0-e2e-vsphere-ovn-techpreview

See details on https://pr-payload-tests.ci.openshift.org/runs/ci/452dd3d0-7fb7-11f1-8e5e-bc54849f548e-0

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. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants