Skip to content
Merged
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
10 changes: 9 additions & 1 deletion pkg/webhook/nad/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ func (v *Validator) Create(_ *admission.Request, newObj runtime.Object) error {
return fmt.Errorf(createErr, nad.Namespace, nad.Name, err)
}

//allow overlay nad creation only if subnet crd/kubeovn is enabled
if conf.IsKubeOVNCNI() {
Copy link
Copy Markdown
Member

@w13915984028 w13915984028 Mar 25, 2026

Choose a reason for hiding this comment

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

check !v.subnetEnabled to allow deletion is fine.

for create case, need to double check if this will affect those automatic tests:

if nad is created but the related vendor is not ready, normally the vm's backend pod will not be created successful as kubelet does not get response from cni driver

we need to do a test:

create overlay-nad & VM before OVN is enabled; or nad is leftover when ovn is disabled (e.g. the upgrade case or on old version without this check): will the VM just stuck on starting;

then enable OVN, could the VM turn to normal running status.

cc @irishgordo @TachunLin @khushboo-rancher to check if some test scripts could be affected, thanks.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yeah that is a good case.Though we cannot create VMs in custom subnets when ovn is disabled, but if we create an overlay network when ovn is disabled and attach a vm to overlay interface, it will be stuck but once the ovn is enabled, the VM will be able to get ip address from default subnet ovn-default and will move to running state.
With this observation, I think we should allow creation of overlay networks even if ovn is disabled. WDYT ?

Copy link
Copy Markdown
Contributor

@Vicente-Cheng Vicente-Cheng Mar 26, 2026

Choose a reason for hiding this comment

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

Hi @rrajendran17, @w13915984028,

Since I would like to have this on today's RC3, I will merge this PR first.
Could you create another issue to track the above case?
Thanks!

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.

We can have a follow-up issue to track this (for whatever we should allow users to pre-create overlay networks and their VMs, or the test plan adaptation).

if !v.subnetEnabled {
return fmt.Errorf(createErr, nad.Namespace, nad.Name, fmt.Errorf("kubeovn is not yet enabled"))
}
}

if err := v.checkNadConfig(conf, nad); err != nil {
return fmt.Errorf(createErr, nad.Namespace, nad.Name, err)
}
Expand Down Expand Up @@ -135,8 +142,9 @@ func (v *Validator) Delete(_ *admission.Request, oldObj runtime.Object) error {
// do not delete nad when a subnet is using it
// This will also make sure nad is not deleted when VMIs,VMs are using it
if nadConf.IsKubeOVNCNI() {
//allow deletion of overlay nad even if kubeovn is not enabled.
if !v.subnetEnabled {
return fmt.Errorf("operation is not permitted as kubeovn is not yet enabled")
return nil
}
return v.checkSubnetsUsingNAD(nad)
}
Expand Down
105 changes: 105 additions & 0 deletions pkg/webhook/nad/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -946,3 +946,108 @@ func TestDeleteNAD(t *testing.T) {
})
}
}

func TestCreateOverlayNADWithNoSubnetCRD(t *testing.T) {
tests := []struct {
name string
returnErr bool
errKey string
currentNAD *cniv1.NetworkAttachmentDefinition
}{
{
name: "NAD can't be created as it has no subnet CRD",
returnErr: true,
errKey: "kubeovn is not yet enabled",
currentNAD: &cniv1.NetworkAttachmentDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: testKubeOVNNadName,
Namespace: testKubeOVNNamespace,
},
Spec: cniv1.NetworkAttachmentDefinitionSpec{
Config: testKubeOVNNadConfig,
},
},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
assert.NotNil(t, tc.currentNAD)
if tc.currentNAD == nil {
return
}

nchclientset := fake.NewSimpleClientset()
vmCache := fakeclients.VirtualMachineCache(nchclientset.KubevirtV1().VirtualMachines)
vmiCache := fakeclients.VirtualMachineInstanceCache(nchclientset.KubevirtV1().VirtualMachineInstances)
cnCache := fakeclients.ClusterNetworkCache(nchclientset.NetworkV1beta1().ClusterNetworks)
vcCache := fakeclients.VlanConfigCache(nchclientset.NetworkV1beta1().VlanConfigs)
subnetCache := fakeclients.SubnetCache(nchclientset.KubeovnV1().Subnets)
nadCache := fakeclients.NetworkAttachmentDefinitionCache(nchclientset.K8sCniCncfIoV1().NetworkAttachmentDefinitions)
hncCache := fakeclients.HostNetworkConfigCache(nchclientset.NetworkV1beta1().HostNetworkConfigs)
validator := NewNadValidator(vmCache, vmiCache, cnCache, vcCache, subnetCache, false, hncCache, nadCache)

err := validator.Create(nil, tc.currentNAD)
logrus.Infof("Create NAD test case '%s' result error: %v", tc.name, err)
assert.True(t, tc.returnErr == (err != nil))
if tc.returnErr {
assert.NotNil(t, err)
if err != nil {
assert.True(t, strings.Contains(err.Error(), tc.errKey))
}
}
})
}
}

func TestDeleteOverlayNADWithNoSubnetCRD(t *testing.T) {
tests := []struct {
name string
returnErr bool
errKey string
currentNAD *cniv1.NetworkAttachmentDefinition
}{
{
name: "NAD can be deleted when kubeovn is not enabled",
returnErr: false,
currentNAD: &cniv1.NetworkAttachmentDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: testKubeOVNNadName,
Namespace: testKubeOVNNamespace,
},
Spec: cniv1.NetworkAttachmentDefinitionSpec{
Config: testKubeOVNNadConfig,
},
},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
assert.NotNil(t, tc.currentNAD)
if tc.currentNAD == nil {
return
}

nchclientset := fake.NewSimpleClientset()
vmCache := fakeclients.VirtualMachineCache(nchclientset.KubevirtV1().VirtualMachines)
vmiCache := fakeclients.VirtualMachineInstanceCache(nchclientset.KubevirtV1().VirtualMachineInstances)
cnCache := fakeclients.ClusterNetworkCache(nchclientset.NetworkV1beta1().ClusterNetworks)
vcCache := fakeclients.VlanConfigCache(nchclientset.NetworkV1beta1().VlanConfigs)
subnetCache := fakeclients.SubnetCache(nchclientset.KubeovnV1().Subnets)
nadCache := fakeclients.NetworkAttachmentDefinitionCache(nchclientset.K8sCniCncfIoV1().NetworkAttachmentDefinitions)
hncCache := fakeclients.HostNetworkConfigCache(nchclientset.NetworkV1beta1().HostNetworkConfigs)
validator := NewNadValidator(vmCache, vmiCache, cnCache, vcCache, subnetCache, false, hncCache, nadCache)

err := validator.Delete(nil, tc.currentNAD)
logrus.Infof("Delete NAD test case '%s' result error: %v", tc.name, err)
assert.True(t, tc.returnErr == (err != nil))
if tc.returnErr {
assert.NotNil(t, err)
if err != nil {
assert.True(t, strings.Contains(err.Error(), tc.errKey))
}
}
})
}
}
Loading