Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,15 @@ repos:
description: Runs `make verify`.
entry: make verify
language: system
pass_filenames: false
stages: [pre-push]
require_serial: true
- id: make-test
name: Run make test
description: Runs `make test`.
entry: make test
language: system
pass_filenames: false
stages: [ pre-push ]
exclude: '^vendor/|^hack/tools/vendor/|^api/vendor/'
fail_fast: true
54 changes: 54 additions & 0 deletions api/hypershift/v1beta1/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ type ClusterNetworkOperatorSpec struct {
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.ipv6) || has(self.ipv6)", message="ipv6 is immutable once set and cannot be removed"
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.ipv6) || !has(oldSelf.ipv6.internalJoinSubnet) || (has(self.ipv6) && has(self.ipv6.internalJoinSubnet))", message="ipv6.internalJoinSubnet cannot be removed once set"
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.ipv6) || !has(oldSelf.ipv6.internalTransitSwitchSubnet) || (has(self.ipv6) && has(self.ipv6.internalTransitSwitchSubnet))", message="ipv6.internalTransitSwitchSubnet cannot be removed once set"
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.v4InternalSubnet) || has(self.v4InternalSubnet)",message="v4InternalSubnet is immutable once set and cannot be removed"
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.v6InternalSubnet) || has(self.v6InternalSubnet)",message="v6InternalSubnet is immutable once set and cannot be removed"
// +kubebuilder:validation:MinProperties=1
type OVNKubernetesConfig struct {
// ipv4 allows users to configure IP settings for IPv4 connections. When omitted,
Expand Down Expand Up @@ -115,6 +117,58 @@ type OVNKubernetesConfig struct {
// +kubebuilder:validation:Maximum=9216
// +optional
MTU int32 `json:"mtu,omitempty"`

// v4InternalSubnet configures the IPv4 subnet used by OVN-Kubernetes for gateway
// router logical router port (LRP) addresses and masquerade/SNAT traffic within
// the OVN logical topology. It must not overlap with any other subnet being used
// by OpenShift or by the node network. The size of the subnet must be larger than
// the number of nodes.
// This field is distinct from ipv4.internalJoinSubnet, which configures the subnet
// for the join switch that interconnects per-node gateway routers with the cluster
// router. Both default to 100.64.0.0/16 but control different OVN-Kubernetes
// internal networks and can be configured independently to avoid overlaps with
// existing network infrastructure.
// Once set, the value is immutable and cannot be modified in subsequent updates.
// The default is 100.64.0.0/16.
// The value must be in IPv4 CIDR notation (e.g., 192.168.0.0/16), consisting of
// four decimal octets (0-255) separated by dots, followed by a slash and a prefix
// length. The prefix length must be between 0 and 30 inclusive, and the first
// octet must not be 0.
// The value must be between 9 and 18 characters in length.
// This field is immutable once set.
// +kubebuilder:validation:MaxLength=18
// +kubebuilder:validation:MinLength=9
Comment thread
everettraven marked this conversation as resolved.
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="v4InternalSubnet is immutable once set"
// +kubebuilder:validation:XValidation:rule="isCIDR(self) && cidr(self).ip().family() == 4",message="Subnet must be in a valid IPv4 CIDR format"
// +kubebuilder:validation:XValidation:rule="isCIDR(self) && cidr(self).prefixLength() <= 30",message="subnet must be in the range /0 to /30 inclusive"
// +kubebuilder:validation:XValidation:rule="isCIDR(self) && cidr(self).ip().family() == 4 && int(self.split('.')[0]) > 0",message="first IP address octet must not be 0"
// +optional
V4InternalSubnet string `json:"v4InternalSubnet,omitempty"`
Comment thread
everettraven marked this conversation as resolved.

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.

These new fields use isCIDR()/cidr()/prefixLength() CEL functions that require the CEL CIDR library (k8s 1.31+). The existing fields use simpler regex+int CEL. Consider adding envtest YAML test cases in stable.hostedclusters.networking.testsuite.yaml to cover:

  • Valid/invalid CIDR format rejection (IPv6 in V4 field, bad prefix lengths)
  • Immutability (self == oldSelf + parent-level !has(oldSelf.x) || has(self.x))
  • Cross-version compatibility (the CEL CIDR lib is newer than what existing fields use)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done. Added envtest YAML test cases in stable.hostedclusters.networking.testsuite.yaml covering:

onCreate:

  • Valid IPv4/IPv6 CIDR acceptance
  • IPv6 value in v4InternalSubnet field → rejection
  • IPv4 value in v6InternalSubnet field → rejection
  • Invalid CIDR format → rejection
  • Prefix length exceeding /30 (v4) and /125 (v6) → rejection
  • First octet = 0 → rejection

onUpdate:

  • Value change → rejection (field-level immutability)
  • Value removal → rejection (type-level !has(oldSelf.x) || has(self.x) guard)

These use the CEL CIDR library functions (isCIDR()/cidr()/prefixLength()) and will exercise them against the envtest kube-apiserver.


AI-assisted response via Claude Code


// v6InternalSubnet configures the IPv6 subnet used by OVN-Kubernetes for gateway
// router logical router port (LRP) addresses and masquerade/SNAT traffic within
// the OVN logical topology. It must not overlap with any other subnet being used
// by OpenShift or by the node network. The size of the subnet must be larger than
// the number of nodes.
// This field is distinct from ipv6.internalJoinSubnet, which configures the subnet
// for the join switch that interconnects per-node gateway routers with the cluster
// router. Both default to fd98::/64 but control different OVN-Kubernetes internal
// networks and can be configured independently to avoid overlaps with existing
// network infrastructure.
// Once set, the value is immutable and cannot be modified in subsequent updates.
// The default is fd98::/64.
// The value must be in IPv6 CIDR notation (e.g., fd98::/64), consisting of an
// IPv6 address followed by a slash and a prefix length. The prefix length must
// be between 0 and 125 inclusive.
// The value must be between 4 and 48 characters in length.
// This field is immutable once set.
// +kubebuilder:validation:MaxLength=48
// +kubebuilder:validation:MinLength=4
Comment thread
everettraven marked this conversation as resolved.
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="v6InternalSubnet is immutable once set"
// +kubebuilder:validation:XValidation:rule="isCIDR(self) && cidr(self).ip().family() == 6",message="Subnet must be in valid IPv6 CIDR format"
// +kubebuilder:validation:XValidation:rule="isCIDR(self) && cidr(self).prefixLength() <= 125",message="subnet must be in the range /0 to /125 inclusive"
Comment thread
everettraven marked this conversation as resolved.
// +optional
V6InternalSubnet string `json:"v6InternalSubnet,omitempty"`
}

// OVNIPv4Config contains IPv4-specific configuration options for OVN-Kubernetes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3282,6 +3282,68 @@ spec:
x-kubernetes-validations:
- message: mtu is immutable once set
rule: self == oldSelf
v4InternalSubnet:
description: |-
v4InternalSubnet configures the IPv4 subnet used by OVN-Kubernetes for gateway
router logical router port (LRP) addresses and masquerade/SNAT traffic within
the OVN logical topology. It must not overlap with any other subnet being used
by OpenShift or by the node network. The size of the subnet must be larger than
the number of nodes.
This field is distinct from ipv4.internalJoinSubnet, which configures the subnet
for the join switch that interconnects per-node gateway routers with the cluster
router. Both default to 100.64.0.0/16 but control different OVN-Kubernetes
internal networks and can be configured independently to avoid overlaps with
existing network infrastructure.
Once set, the value is immutable and cannot be modified in subsequent updates.
The default is 100.64.0.0/16.
The value must be in IPv4 CIDR notation (e.g., 192.168.0.0/16), consisting of
four decimal octets (0-255) separated by dots, followed by a slash and a prefix
length. The prefix length must be between 0 and 30 inclusive, and the first
octet must not be 0.
The value must be between 9 and 18 characters in length.
This field is immutable once set.
maxLength: 18
minLength: 9
type: string
x-kubernetes-validations:
- message: v4InternalSubnet is immutable once set
rule: self == oldSelf
- message: Subnet must be in a valid IPv4 CIDR format
rule: isCIDR(self) && cidr(self).ip().family() == 4
- message: subnet must be in the range /0 to /30 inclusive
rule: isCIDR(self) && cidr(self).prefixLength() <= 30
- message: first IP address octet must not be 0
rule: isCIDR(self) && cidr(self).ip().family() == 4
&& int(self.split('.')[0]) > 0
v6InternalSubnet:
description: |-
v6InternalSubnet configures the IPv6 subnet used by OVN-Kubernetes for gateway
router logical router port (LRP) addresses and masquerade/SNAT traffic within
the OVN logical topology. It must not overlap with any other subnet being used
by OpenShift or by the node network. The size of the subnet must be larger than
the number of nodes.
This field is distinct from ipv6.internalJoinSubnet, which configures the subnet
for the join switch that interconnects per-node gateway routers with the cluster
router. Both default to fd98::/64 but control different OVN-Kubernetes internal
networks and can be configured independently to avoid overlaps with existing
network infrastructure.
Once set, the value is immutable and cannot be modified in subsequent updates.
The default is fd98::/64.
The value must be in IPv6 CIDR notation (e.g., fd98::/64), consisting of an
IPv6 address followed by a slash and a prefix length. The prefix length must
be between 0 and 125 inclusive.
The value must be between 4 and 48 characters in length.
This field is immutable once set.
maxLength: 48
minLength: 4
type: string
x-kubernetes-validations:
- message: v6InternalSubnet is immutable once set
rule: self == oldSelf
- message: Subnet must be in valid IPv6 CIDR format
rule: isCIDR(self) && cidr(self).ip().family() == 6
- message: subnet must be in the range /0 to /125 inclusive
rule: isCIDR(self) && cidr(self).prefixLength() <= 125
type: object
x-kubernetes-validations:
- message: internalJoinSubnet and internalTransitSwitchSubnet
Expand All @@ -3306,6 +3368,12 @@ spec:
once set
rule: '!has(oldSelf.ipv6) || !has(oldSelf.ipv6.internalTransitSwitchSubnet)
|| (has(self.ipv6) && has(self.ipv6.internalTransitSwitchSubnet))'
- message: v4InternalSubnet is immutable once set and cannot
be removed
rule: '!has(oldSelf.v4InternalSubnet) || has(self.v4InternalSubnet)'
- message: v6InternalSubnet is immutable once set and cannot
be removed
rule: '!has(oldSelf.v6InternalSubnet) || has(self.v6InternalSubnet)'
type: object
x-kubernetes-validations:
- message: ovnKubernetesConfig is immutable once set and cannot
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3273,6 +3273,68 @@ spec:
x-kubernetes-validations:
- message: mtu is immutable once set
rule: self == oldSelf
v4InternalSubnet:
description: |-
v4InternalSubnet configures the IPv4 subnet used by OVN-Kubernetes for gateway
router logical router port (LRP) addresses and masquerade/SNAT traffic within
the OVN logical topology. It must not overlap with any other subnet being used
by OpenShift or by the node network. The size of the subnet must be larger than
the number of nodes.
This field is distinct from ipv4.internalJoinSubnet, which configures the subnet
for the join switch that interconnects per-node gateway routers with the cluster
router. Both default to 100.64.0.0/16 but control different OVN-Kubernetes
internal networks and can be configured independently to avoid overlaps with
existing network infrastructure.
Once set, the value is immutable and cannot be modified in subsequent updates.
The default is 100.64.0.0/16.
The value must be in IPv4 CIDR notation (e.g., 192.168.0.0/16), consisting of
four decimal octets (0-255) separated by dots, followed by a slash and a prefix
length. The prefix length must be between 0 and 30 inclusive, and the first
octet must not be 0.
The value must be between 9 and 18 characters in length.
This field is immutable once set.
maxLength: 18
minLength: 9
type: string
x-kubernetes-validations:
- message: v4InternalSubnet is immutable once set
rule: self == oldSelf
- message: Subnet must be in a valid IPv4 CIDR format
rule: isCIDR(self) && cidr(self).ip().family() == 4
- message: subnet must be in the range /0 to /30 inclusive
rule: isCIDR(self) && cidr(self).prefixLength() <= 30
- message: first IP address octet must not be 0
rule: isCIDR(self) && cidr(self).ip().family() == 4
&& int(self.split('.')[0]) > 0
v6InternalSubnet:
description: |-
v6InternalSubnet configures the IPv6 subnet used by OVN-Kubernetes for gateway
router logical router port (LRP) addresses and masquerade/SNAT traffic within
the OVN logical topology. It must not overlap with any other subnet being used
by OpenShift or by the node network. The size of the subnet must be larger than
the number of nodes.
This field is distinct from ipv6.internalJoinSubnet, which configures the subnet
for the join switch that interconnects per-node gateway routers with the cluster
router. Both default to fd98::/64 but control different OVN-Kubernetes internal
networks and can be configured independently to avoid overlaps with existing
network infrastructure.
Once set, the value is immutable and cannot be modified in subsequent updates.
The default is fd98::/64.
The value must be in IPv6 CIDR notation (e.g., fd98::/64), consisting of an
IPv6 address followed by a slash and a prefix length. The prefix length must
be between 0 and 125 inclusive.
The value must be between 4 and 48 characters in length.
This field is immutable once set.
maxLength: 48
minLength: 4
type: string
x-kubernetes-validations:
- message: v6InternalSubnet is immutable once set
rule: self == oldSelf
- message: Subnet must be in valid IPv6 CIDR format
rule: isCIDR(self) && cidr(self).ip().family() == 6
- message: subnet must be in the range /0 to /125 inclusive
rule: isCIDR(self) && cidr(self).prefixLength() <= 125
type: object
x-kubernetes-validations:
- message: internalJoinSubnet and internalTransitSwitchSubnet
Expand All @@ -3297,6 +3359,12 @@ spec:
once set
rule: '!has(oldSelf.ipv6) || !has(oldSelf.ipv6.internalTransitSwitchSubnet)
|| (has(self.ipv6) && has(self.ipv6.internalTransitSwitchSubnet))'
- message: v4InternalSubnet is immutable once set and cannot
be removed
rule: '!has(oldSelf.v4InternalSubnet) || has(self.v4InternalSubnet)'
- message: v6InternalSubnet is immutable once set and cannot
be removed
rule: '!has(oldSelf.v6InternalSubnet) || has(self.v6InternalSubnet)'
type: object
x-kubernetes-validations:
- message: ovnKubernetesConfig is immutable once set and cannot
Expand Down
Loading