Skip to content

OCPBUGS-87991: validate additionalNetworks name format in KubeVirt NodePools#8710

Open
chdeshpa-hue wants to merge 1 commit into
openshift:mainfrom
chdeshpa-hue:OCPBUGS-87991-kubevirt-validate-additional-networks
Open

OCPBUGS-87991: validate additionalNetworks name format in KubeVirt NodePools#8710
chdeshpa-hue wants to merge 1 commit into
openshift:mainfrom
chdeshpa-hue:OCPBUGS-87991-kubevirt-validate-additional-networks

Conversation

@chdeshpa-hue

@chdeshpa-hue chdeshpa-hue commented Jun 10, 2026

Copy link
Copy Markdown

Summary

KubeVirt NodePools accept invalid additionalNetworks[].name values without any error at admission or reconcile time. The field requires Multus NAD references in <namespace>/<name> format, but no validation enforces this. Users who omit the namespace prefix get a HostedCluster that appears healthy (ValidPlatformConfig: True "All is well") while VMs silently fail to start with errors buried in the hosted control plane namespace.

This PR adds ValidateAdditionalNetworks() called from both:

  • Admission webhook → instant rejection at oc apply time
  • PlatformValidation() → reconcile-time safety net

Checks added:

  1. Format: exactly one / with non-empty namespace and name segments
  2. Uniqueness: reject duplicate network names
  3. Length: generated KubeVirt interface name must not exceed 63 characters

Problem

Without this fix, the debugging path requires navigating 6 layers before finding the root cause (VM conditions in the HC namespace set by virt-controller). NodePool status actively misleads with "All is well". Time to root cause ranges from 30 minutes (expert) to impossible (app team without HC namespace RBAC).

User Experience After Fix

$ oc apply -f nodepool.yaml
Error from server: admission webhook "nodepool.hypershift.openshift.io" denied the request:
  additionalNetworks[0].name "storage-net" must be in the format <namespace>/<name>

Testing

  • 7 new unit test cases covering invalid format, duplicates, and overlength
  • All 10 pre-existing test cases pass unchanged (no regression)
  • Live cluster validation on OCP 4.22.0 + CNV 4.21.8
  • gofmt and go vet clean

Backward Compatibility

  • No additionalNetworks configured → validation skipped entirely
  • Valid ns/name entries → pass all checks, zero behavior change
  • Existing clusters with valid configs → unaffected

Fixes: https://redhat.atlassian.net/browse/OCPBUGS-87991

Made with Cursor

Summary by CodeRabbit

  • Bug Fixes
    • Improved validation for KubeVirt network configurations to enforce proper naming conventions and enforce maximum length constraints on network names.

@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Pipeline controller notification
This repo is configured to use the pipeline controller. Second-stage tests will be triggered either automatically or after lgtm label is added, depending on the repository configuration. The pipeline controller will automatically detect which contexts are required and will utilize /test Prow commands to trigger the second stage.

For optional jobs, comment /test ? to see a list of all defined jobs. To trigger manually all jobs from second stage use /pipeline required command.

This repository is configured in: LGTM mode

@openshift-ci-robot openshift-ci-robot added jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. labels Jun 10, 2026
@openshift-ci-robot

Copy link
Copy Markdown

@chdeshpa-hue: This pull request references Jira Issue OCPBUGS-87991, which is invalid:

  • expected the bug to target the "5.0.0" version, but no target version was set

Comment /jira refresh to re-evaluate validity if changes to the Jira bug are made, or edit the title of this pull request to link to a different bug.

The bug has been updated to refer to the pull request using the external bug tracker.

Details

In response to this:

Summary

KubeVirt NodePools accept invalid additionalNetworks[].name values without any error at admission or reconcile time. The field requires Multus NAD references in <namespace>/<name> format, but no validation enforces this. Users who omit the namespace prefix get a HostedCluster that appears healthy (ValidPlatformConfig: True "All is well") while VMs silently fail to start with errors buried in the hosted control plane namespace.

This PR adds ValidateAdditionalNetworks() called from both:

  • Admission webhook → instant rejection at oc apply time
  • PlatformValidation() → reconcile-time safety net

Checks added:

  1. Format: exactly one / with non-empty namespace and name segments
  2. Uniqueness: reject duplicate network names
  3. Length: generated KubeVirt interface name must not exceed 63 characters

Problem

Without this fix, the debugging path requires navigating 6 layers before finding the root cause (VM conditions in the HC namespace set by virt-controller). NodePool status actively misleads with "All is well". Time to root cause ranges from 30 minutes (expert) to impossible (app team without HC namespace RBAC).

User Experience After Fix

$ oc apply -f nodepool.yaml
Error from server: admission webhook "nodepool.hypershift.openshift.io" denied the request:
 additionalNetworks[0].name "storage-net" must be in the format <namespace>/<name>

Testing

  • 7 new unit test cases covering invalid format, duplicates, and overlength
  • All 10 pre-existing test cases pass unchanged (no regression)
  • Live cluster validation on OCP 4.22.0 + CNV 4.21.8
  • gofmt and go vet clean

Backward Compatibility

  • No additionalNetworks configured → validation skipped entirely
  • Valid ns/name entries → pass all checks, zero behavior change
  • Existing clusters with valid configs → unaffected

Fixes: https://redhat.atlassian.net/browse/OCPBUGS-87991

Made with Cursor

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.

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

In api/hypershift/v1beta1/kubevirt.go, two sets of kubebuilder markers are added. The AdditionalNetworks field on KubevirtNodePoolPlatform gains +listType=map and +listMapKey=name markers, making the slice behave as a map keyed by the name field for strategic merge patch and server-side apply. The Name field on KubevirtNetwork gains a +kubebuilder:validation:MaxLength=55 constraint and an +kubebuilder:validation:XValidation rule enforcing the <namespace>/<name> format required for Multus network attachment definition references.

🚥 Pre-merge checks | ✅ 11
✅ Passed checks (11 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding validation for additionalNetworks name format in KubeVirt NodePools, which directly corresponds to the PR objective and code modifications.
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 11 test names in stable.nodepools.kubevirt.testsuite.yaml are stable and deterministic. No dynamic content found (timestamps, UUIDs, generated suffixes, pod/node/namespace names, IP addresses)....
Test Structure And Quality ✅ Passed The custom check requires reviewing Ginkgo test code for specific quality patterns (BeforeEach/AfterEach, timeouts, assertion messages, etc.). However, this PR adds envtest YAML-driven tests, not G...
Topology-Aware Scheduling Compatibility ✅ Passed PR modifies only API type validation annotations in kubevirt.go (format/length constraints on network names). No deployment manifests, operators, controllers, or scheduling configurations are intro...
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No new Ginkgo e2e tests added. The only test file added is stable.nodepools.kubevirt.testsuite.yaml, an envtest CRD validation suite with declarative YAML test cases that don't contain IPv4 assumpt...
No-Weak-Crypto ✅ Passed This PR contains no cryptographic code. It modifies API type definitions by adding validation markers and CEL rules for network name format validation in KubeVirt NodePools.
Container-Privileges ✅ Passed PR modifies only Go API type definitions (kubevirt.go) with kubebuilder annotations; contains no container manifests or K8s specs to check for privileged settings.
No-Sensitive-Data-In-Logs ✅ Passed The PR modifies only api/hypershift/v1beta1/kubevirt.go, a pure API types file with no logging statements. No passwords, tokens, API keys, PII, or sensitive data are logged. Validation messages con...

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@openshift-ci openshift-ci Bot added area/hypershift-operator Indicates the PR includes changes for the hypershift operator and API - outside an OCP release area/platform/kubevirt PR/issue for KubeVirt (KubevirtPlatform) platform and removed do-not-merge/needs-area labels Jun 10, 2026
@openshift-ci openshift-ci Bot requested review from bryan-cox and enxebre June 10, 2026 07:07
@codecov

codecov Bot commented Jun 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 41.66%. Comparing base (44f5195) to head (9580fed).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #8710   +/-   ##
=======================================
  Coverage   41.66%   41.66%           
=======================================
  Files         758      758           
  Lines       93929    93929           
=======================================
  Hits        39135    39135           
  Misses      52046    52046           
  Partials     2748     2748           
Flag Coverage Δ
cmd-support 34.96% <ø> (ø)
cpo-hostedcontrolplane 44.00% <ø> (ø)
cpo-other 43.45% <ø> (ø)
hypershift-operator 51.65% <ø> (ø)
other 31.56% <ø> (ø)

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

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
hypershift-operator/controllers/nodepool/kubevirt/kubevirt.go (1)

142-162: ⚡ Quick win

Consider validating namespace and name parts against Kubernetes DNS subdomain rules.

The current validation checks that parts are non-empty after trimming, but doesn't enforce Kubernetes DNS subdomain naming conventions (RFC 1123). This means names like "my ns/nad" (with space) or "my_ns/nad" (with underscore) pass validation but will cause VM startup failures when KubeVirt attempts to reference a non-existent NetworkAttachmentDefinition.

Kubernetes resource names must be lowercase alphanumeric with - and ., starting and ending with alphanumeric. Adding a regex check like ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ for each part would catch these at admission time with clearer error messages.

♻️ Example validation with DNS subdomain rules
+import (
+	"regexp"
+)
+
+var dnsSubdomainRegex = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`)
+
 func ValidateAdditionalNetworks(networks []hyperv1.KubevirtNetwork) error {
 	if len(networks) == 0 {
 		return nil
 	}
 	seen := make(map[string]bool, len(networks))
 	for idx, network := range networks {
 		parts := strings.Split(network.Name, "/")
 		if len(parts) != 2 || strings.TrimSpace(parts[0]) == "" || strings.TrimSpace(parts[1]) == "" {
 			return fmt.Errorf("additionalNetworks[%d].name %q must be in the format <namespace>/<name>", idx, network.Name)
 		}
+		namespace := strings.TrimSpace(parts[0])
+		name := strings.TrimSpace(parts[1])
+		if !dnsSubdomainRegex.MatchString(namespace) {
+			return fmt.Errorf("additionalNetworks[%d].name %q has invalid namespace part %q (must be a valid DNS subdomain)", idx, network.Name, namespace)
+		}
+		if !dnsSubdomainRegex.MatchString(name) {
+			return fmt.Errorf("additionalNetworks[%d].name %q has invalid name part %q (must be a valid DNS subdomain)", idx, network.Name, name)
+		}
 		if seen[network.Name] {
 			return fmt.Errorf("additionalNetworks[%d].name %q is duplicated", idx, network.Name)
 		}
🤖 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 `@hypershift-operator/controllers/nodepool/kubevirt/kubevirt.go` around lines
142 - 162, The ValidateAdditionalNetworks function currently only checks for
non-empty namespace/name parts; update it to enforce Kubernetes DNS
subdomain/name rules by validating each part (the namespace and the name from
strings.Split(network.Name, "/")) against the RFC1123 regex (e.g.
^[a-z0-9]([-a-z0-9]*[a-z0-9])?$), and return clear formatted errors like
"additionalNetworks[%d].name %q: namespace %q is invalid" or "…: name %q is
invalid" when a part fails; keep the existing duplicate check and
virtualMachineInterfaceName length check intact, and reference the same symbols
(ValidateAdditionalNetworks, virtualMachineInterfaceName) so the change is
localized to this function.
🤖 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 `@hypershift-operator/controllers/nodepool/kubevirt/kubevirt.go`:
- Around line 142-162: The ValidateAdditionalNetworks function currently only
checks for non-empty namespace/name parts; update it to enforce Kubernetes DNS
subdomain/name rules by validating each part (the namespace and the name from
strings.Split(network.Name, "/")) against the RFC1123 regex (e.g.
^[a-z0-9]([-a-z0-9]*[a-z0-9])?$), and return clear formatted errors like
"additionalNetworks[%d].name %q: namespace %q is invalid" or "…: name %q is
invalid" when a part fails; keep the existing duplicate check and
virtualMachineInterfaceName length check intact, and reference the same symbols
(ValidateAdditionalNetworks, virtualMachineInterfaceName) so the change is
localized to this function.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 098416f3-4a27-47b2-8089-b95cf2b80aec

📥 Commits

Reviewing files that changed from the base of the PR and between 832b848 and 53adcee.

📒 Files selected for processing (3)
  • hypershift-operator/controllers/hostedcluster/hostedcluster_webhook.go
  • hypershift-operator/controllers/nodepool/kubevirt/kubevirt.go
  • hypershift-operator/controllers/nodepool/kubevirt/kubevirt_test.go

@hypershift-jira-solve-ci

Copy link
Copy Markdown

I now have all the information needed to produce the final report. The root cause is clear.

Test Failure Analysis Complete

Job Information

Test Failure Analysis

Error

1: CT1 Title does not start with one of fix, feat, chore, docs, style, refactor, perf, test, revert, ci, build: "OCPBUGS-87991: validate additionalNetworks name format in KubeVirt NodePools"
make: *** [Makefile:614: run-gitlint] Error 1

Summary

The gitlint check failed because the commit message title "OCPBUGS-87991: validate additionalNetworks name format in KubeVirt NodePools" does not follow the Conventional Commits format required by the repository. The .gitlint config enforces contrib-title-conventional-commits (rule CT1), which requires the title to begin with one of: fix, feat, chore, docs, style, refactor, perf, test, revert, ci, build. The title starts with a Jira key (OCPBUGS-87991:) instead.

Root Cause

The commit message title uses a JIRA-KEY: description format (OCPBUGS-87991: validate additionalNetworks name format in KubeVirt NodePools) instead of the required Conventional Commits format (type(scope): description).

The openshift/hypershift repository enforces Conventional Commits via gitlint's built-in contrib-title-conventional-commits rule, configured in .gitlint:

[general]
contrib=contrib-title-conventional-commits

[contrib-title-conventional-commits]
types = fix,feat,chore,docs,style,refactor,perf,test,revert,ci,build

The CT1 rule requires the title to match the pattern <type>(<optional-scope>): <description>, where <type> must be one of the 11 allowed prefixes. The Jira key OCPBUGS-87991 is not a valid conventional commit type, so gitlint rejects it.

This is purely a commit message formatting issue — it is not a code or test problem.

Recommendations

Amend the commit message to use the Conventional Commits format. Since this PR adds validation logic (a new feature), the title should use the feat type. The Jira key can be included in the commit body or as a footer:

feat(kubevirt): validate additionalNetworks name format in NodePools

The additionalNetworks[].name field in KubeVirt NodePools requires Multus NAD
references in <namespace>/<name> format, but no validation enforces this.

Add ValidateAdditionalNetworks() called from both the admission webhook
and PlatformValidation():
- Format: exactly one slash with non-empty namespace and name segments
- Uniqueness: reject duplicate network names
- Length: generated interface name must not exceed 63 characters

Fixes: https://redhat.atlassian.net/browse/OCPBUGS-87991

Alternatively, if this is considered a bug fix (enforcing a contract that should have existed), use fix instead of feat:

fix(kubevirt): validate additionalNetworks name format in NodePools

To amend: git commit --amend and update the title, then force-push.

Evidence
Evidence Detail
Failing step Run make run-gitlint (step 3)
Rule violated CT1contrib-title-conventional-commits
Actual title OCPBUGS-87991: validate additionalNetworks name format in KubeVirt NodePools
Required format <type>(<scope>): <description> where type ∈ {fix, feat, chore, docs, style, refactor, perf, test, revert, ci, build}
Config file .gitlint at repo root
Gitlint version 0.19.1
Title max length 120 characters (would pass)
Body max line length 140 characters (would pass)

@bryan-cox bryan-cox left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for tackling this — the validation gap is real and the user experience improvement is clear. The checks themselves are the right ones to add.

However, this validation should live in the API types (CEL markers) rather than the webhook. Our project rule (.claude/rules/webhook-validation.md) is explicit:

"HyperShift uses CRD CEL validation rules instead of webhooks. The webhook exists only for KubeVirt platform-specific needs (defaulting and JSON patch annotation validation). Do not add new validation or defaulting logic here."

All three checks are expressible declaratively:

1. Format check — CEL regex on KubevirtNetwork.Name:

// +kubebuilder:validation:XValidation:rule="self.matches('^[^/]+/[^/]+$')",message="name must be in the format <namespace>/<name> to reference a multus network attachment definition"

There's direct precedent for this pattern in our Azure subnet ID validation (api/hypershift/v1beta1/azure.go).

2. Uniqueness — use Kubernetes list map semantics on AdditionalNetworks:

// +listType=map
// +listMapKey=name

No CEL needed. Schema-level enforcement with proper server-side-apply merge semantics.

3. Interface name length — tighten MaxLength instead of computing it in Go. The generated name is iface{N}_{name} where the prefix is at most 8 chars (iface20_ at MaxItems=20). So:

// +kubebuilder:validation:MaxLength=55

This replaces the entire Go length check. It's slightly conservative but avoids coupling the API to the controller's internal virtualMachineInterfaceName() implementation.

What I'd suggest:

  • Move format + uniqueness to API type markers (blocking — per project convention)
  • Tighten MaxLength from 255 to 55 on Name
  • Remove the webhook additions
  • Keep the PlatformValidation() call as a defense-in-depth safety net
  • Add envtest YAML test coverage for the new CEL rules (see test/envtest/README.md)

Happy to help if you have questions about the CEL/envtest patterns — there are good examples to follow in the existing codebase.

@chdeshpa-hue

Copy link
Copy Markdown
Author

Thanks @bryan-cox — you're right, this belongs in the API types, not the webhook. Redesigning from scratch. Here's the approach I'm planning — wanted to align before writing code, especially since you mentioned there are good examples to follow.

Planned changes

1. Format check — CEL regex on KubevirtNetwork.Name

// +kubebuilder:validation:XValidation:rule="self.matches('^[^/]+/[^/]+$')",message="name must be in the format <namespace>/<name> to reference a multus network attachment definition"

Following the Azure SubnetID validation pattern in azure.go.

2. Uniqueness — list map semantics on AdditionalNetworks

// +listType=map
// +listMapKey=name
AdditionalNetworks []KubevirtNetwork `json:"additionalNetworks,omitempty"`

Schema-level enforcement, no CEL needed. Follows the pattern in endpointservice_types.go.

3. Interface name length — tighten MaxLength from 255 to 55

// +kubebuilder:validation:MaxLength=55

The generated name is iface{N}_{name} where the prefix is at most 8 chars (iface20_ at MaxItems=20). MaxLength=55 ensures the generated name stays under 63 chars without coupling the API to the controller's virtualMachineInterfaceName() internals.

Ratcheting note: Reducing MaxLength is technically a breaking change. CRD ratcheting allows unchanged values through on update, but any cluster with names >55 chars would fail on the next modification of that field. In practice, names >55 already fail at runtime (63-char interface name limit), so this tightens admission to match existing runtime behavior.

What stays

  • ValidateAdditionalNetworks() in PlatformValidation() as defense-in-depth
  • Existing Go unit tests

What gets removed

  • Webhook additions in hostedcluster_webhook.go

What gets added

  • Envtest YAML test suite for KubeVirt additional networks

Questions

  1. You mentioned there are good examples to follow for the CEL/envtest patterns — could you point me to specific files? I found stable.nodepools.validation.testsuite.yaml and the Azure SubnetID CEL rules — are there others I should reference?

  2. On the MaxLength change (255 → 55): do you see any concerns with the ratcheting behavior, or is this acceptable given that >55 already fails at runtime?

@bryan-cox

Copy link
Copy Markdown
Member

1. Examples to follow for CEL/envtest patterns:

For the envtest YAML test suites, the existing NodePool test suites are the best reference:

  • cmd/install/assets/crds/hypershift-operator/tests/nodepools.hypershift.openshift.io/stable.nodepools.azure.testsuite.yaml — full NodePool boilerplate with platform-specific fields, both passing and failing cases with expectedError
  • cmd/install/assets/crds/hypershift-operator/tests/nodepools.hypershift.openshift.io/stable.nodepools.validation.testsuite.yaml — general NodePool validation (taints etc.)

Create your new suite as stable.nodepools.kubevirt.testsuite.yaml in the same directory (tests/nodepools.hypershift.openshift.io/). The framework auto-discovers YAML files there — no Go code changes needed.

For CEL marker patterns on the API types, api/hypershift/v1beta1/etcdbackup_types.go is the canonical best-practices example (per api/AGENTS.md). For regex validation, the Azure types in azure.go have good XValidation examples.

For the +listType=map / +listMapKey pattern, gcp.go lines 157-158 (ResourceLabels with +listMapKey=key) is the closest analog — it's a user-defined list with a string key field, which is what you need. The endpointservice_types.go example you found is for Conditions, which is a different pattern.

Run make test-envtest-ocp to validate the suite locally. See test/envtest/README.md for the full format reference and api/AGENTS.md for all API conventions.

2. MaxLength 255 → 55 ratcheting:

This is acceptable. Names >55 already fail at runtime due to the 63-char interface name limit, so you're moving the failure left to admission time. CRD ratcheting handles the transition — unchanged values pass through on update. The math checks out: worst-case prefix is iface20_ (8 chars) + 55 = 63.

3. A few things to flag on the approach:

CEL regex: self.matches('^[^/]+/[^/]+$') doesn't reject whitespace-only segments (e.g., " "/name passes). Consider tightening to self.matches('^[^/\\s]+/[^/\\s]+$') or enforcing Kubernetes name/namespace character rules directly.

Defense-in-depth: I'd push back on keeping ValidateAdditionalNetworks() in PlatformValidation(). Once CEL covers format, uniqueness (via list map), and length at admission time, invalid values can never reach the controller — the Go validation becomes dead code. Our policy from api/AGENTS.md: "Always prefer admission-time via CEL over controller-time validation." Remove the function and the webhook additions cleanly.

Webhook cleanup: Make sure the removal includes reverting any changes to validateCreateKubevirtNodePool / validateUpdateKubevirtNodePool and removing the kubevirt import the PR added to hostedcluster_webhook.go.

The additionalNetworks[].name field in KubeVirt NodePools requires Multus NAD
references in <namespace>/<name> format, but no validation enforces this. Invalid
names are silently accepted — NodePool reports ValidPlatformConfig=True — while
VMs fail to start with errors buried 4 layers deep in the hosted control plane
namespace (VM conditions set by virt-controller).

Add admission-time CEL validation on the KubevirtNetwork API type:
- Format: XValidation regex enforcing exactly one slash with non-empty,
  non-whitespace namespace and name segments
- Uniqueness: listType=map with listMapKey=name for schema-level duplicate
  rejection
- Length: MaxLength reduced from 255 to 55 to ensure the generated interface
  name (iface{idx+1}_{name}, max prefix iface20_ at MaxItems=20) stays within
  the 63-character Linux interface name limit

Fixes: https://redhat.atlassian.net/browse/OCPBUGS-87991
@chdeshpa-hue chdeshpa-hue force-pushed the OCPBUGS-87991-kubevirt-validate-additional-networks branch from 53adcee to 9580fed Compare June 16, 2026 08:08
@chdeshpa-hue

Copy link
Copy Markdown
Author

Thanks @bryan-cox — redesigned from scratch based on your feedback. Force-pushed a single commit.

What changed

Removed

  • ValidateAdditionalNetworks() from kubevirt.go and its call in PlatformValidation()
  • All webhook additions in hostedcluster_webhook.go (import + validation calls)
  • All 7 Go unit tests from kubevirt_test.go

Added (API types only)

api/hypershift/v1beta1/kubevirt.go:

  1. Format — CEL regex on KubevirtNetwork.Name:

    +kubebuilder:validation:XValidation:rule="self.matches('^[^/\\s]+/[^/\\s]+$')"
    

    Rejects missing slash, empty segments, and whitespace (tightened per your suggestion).

  2. Uniqueness — list map semantics on AdditionalNetworks:

    +listType=map
    +listMapKey=name
    

    Following gcp.go ResourceLabels pattern.

  3. LengthMaxLength reduced from 255 to 55:
    Worst-case prefix iface20_ (8 chars) + 55 = 63. Matches existing runtime limit.

Tests

New stable.nodepools.kubevirt.testsuite.yaml with 11 envtest cases — valid format, no slash, multiple slashes, empty segments, whitespace in both namespace and name, duplicates, and MaxLength boundary (55 pass, 56 fail). All 818 specs pass across k8s 1.33/1.34/1.35.

What was NOT kept

Per your guidance: no defense-in-depth ValidateAdditionalNetworks() in Go — CEL covers it at admission, so the Go validation would be dead code.

/cc @bryan-cox

@openshift-ci openshift-ci Bot requested a review from bryan-cox June 16, 2026 08:08
@openshift-ci openshift-ci Bot added area/api Indicates the PR includes changes for the API area/cli Indicates the PR includes changes for CLI labels Jun 16, 2026
@openshift-ci

openshift-ci Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: chdeshpa-hue
Once this PR has been reviewed and has the lgtm label, please assign sjenning 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
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@api/hypershift/v1beta1/kubevirt.go`:
- Around line 204-205: The XValidation rule introduced in
api/hypershift/v1beta1/kubevirt.go for validating the multus network attachment
definition reference format lacks corresponding envtest coverage. Following the
coding guidelines that require all API CEL validations to be covered with
envtests, add envtest cases to validate both valid and invalid inputs for the
format constraint that ensures the field matches the pattern for namespace/name
format. Refer to test/envtest/README.md for guidance on structuring these tests,
and ensure the tests verify that the validation rule correctly accepts properly
formatted namespace/name references and rejects improperly formatted ones.
🪄 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 YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 09a3bf35-1698-497b-8b4b-9654ce14e281

📥 Commits

Reviewing files that changed from the base of the PR and between 53adcee and 9580fed.

⛔ Files ignored due to path filters (9)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/AAA_ungated.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/GCPPlatform.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/OSStreams.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/OpenStack.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • cmd/install/assets/crds/hypershift-operator/tests/nodepools.hypershift.openshift.io/stable.nodepools.kubevirt.testsuite.yaml is excluded by !cmd/install/assets/**/*.yaml
  • cmd/install/assets/crds/hypershift-operator/zz_generated.crd-manifests/nodepools-CustomNoUpgrade.crd.yaml is excluded by !**/zz_generated.crd-manifests/**, !cmd/install/assets/**/*.yaml
  • cmd/install/assets/crds/hypershift-operator/zz_generated.crd-manifests/nodepools-Default.crd.yaml is excluded by !**/zz_generated.crd-manifests/**, !cmd/install/assets/**/*.yaml
  • cmd/install/assets/crds/hypershift-operator/zz_generated.crd-manifests/nodepools-TechPreviewNoUpgrade.crd.yaml is excluded by !**/zz_generated.crd-manifests/**, !cmd/install/assets/**/*.yaml
  • vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/kubevirt.go is excluded by !vendor/**, !**/vendor/**
📒 Files selected for processing (1)
  • api/hypershift/v1beta1/kubevirt.go

Comment on lines +204 to +205
// +kubebuilder:validation:MaxLength=55
// +kubebuilder:validation:XValidation:rule="self.matches('^[^/\\\\s]+/[^/\\\\s]+$')",message="name must be in the format <namespace>/<name> to reference a multus network attachment definition"

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add envtest coverage for the new CEL/list-map validation contract.

This introduces admission-time validation behavior (XValidation) and schema semantics (listType=map/listMapKey=name), but no corresponding envtest evidence is present in the provided changes.

As per coding guidelines, "All API CEL validations must be covered with envtests, see test/envtest/README.md for details."

🤖 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 `@api/hypershift/v1beta1/kubevirt.go` around lines 204 - 205, The XValidation
rule introduced in api/hypershift/v1beta1/kubevirt.go for validating the multus
network attachment definition reference format lacks corresponding envtest
coverage. Following the coding guidelines that require all API CEL validations
to be covered with envtests, add envtest cases to validate both valid and
invalid inputs for the format constraint that ensures the field matches the
pattern for namespace/name format. Refer to test/envtest/README.md for guidance
on structuring these tests, and ensure the tests verify that the validation rule
correctly accepts properly formatted namespace/name references and rejects
improperly formatted ones.

Source: Coding guidelines

@openshift-ci

openshift-ci Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

@chdeshpa-hue: all tests passed!

Full PR test history. Your PR dashboard.

Details

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

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

Labels

area/api Indicates the PR includes changes for the API area/cli Indicates the PR includes changes for CLI area/hypershift-operator Indicates the PR includes changes for the hypershift operator and API - outside an OCP release area/platform/kubevirt PR/issue for KubeVirt (KubevirtPlatform) platform jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. 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.

4 participants