[kubernetes] Fix CiliumNetworkPolicy endpointSelector for multi-node RWX volumes (#2227)
## What this PR does When an NFS-backed RWX volume is published to multiple VMs, the `CiliumNetworkPolicy` `endpointSelector.matchLabels` only included the first VM. Subsequent `ControllerPublishVolume` calls added `ownerReferences` but never broadened the selector, causing Cilium to block NFS egress — mounts hang on all nodes except the first. This PR switches from `matchLabels` to `matchExpressions` (`operator: In`) so the selector can list multiple VM names, and rebuilds it whenever ownerReferences are added or removed. ### Release note ```release-note [kubernetes] Fixed CiliumNetworkPolicy endpointSelector not being updated when NFS-backed RWX volumes are published to multiple VMs, which caused NFS mounts to hang on all nodes except the first. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * KubeVirt CSI driver now supports selecting and targeting multiple virtual machines for volume publishing. * **Improvements** * Network policy targets are rebuilt automatically when VM ownership references change, improving correctness and lifecycle handling in multi-VM scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
commit
9fb9354fd2
1 changed files with 46 additions and 5 deletions
|
|
@ -317,11 +317,7 @@ func (w *WrappedControllerService) ControllerPublishVolume(ctx context.Context,
|
|||
"ownerReferences": []interface{}{vmiOwnerRef},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"endpointSelector": map[string]interface{}{
|
||||
"matchLabels": map[string]interface{}{
|
||||
"kubevirt.io/vm": vmName,
|
||||
},
|
||||
},
|
||||
"endpointSelector": buildEndpointSelector([]string{vmName}),
|
||||
"egress": []interface{}{
|
||||
map[string]interface{}{
|
||||
"toEndpoints": []interface{}{
|
||||
|
|
@ -441,6 +437,13 @@ func (w *WrappedControllerService) addCNPOwnerReference(ctx context.Context, nam
|
|||
if err := unstructured.SetNestedSlice(existing.Object, ownerRefs, "metadata", "ownerReferences"); err != nil {
|
||||
return status.Errorf(codes.Internal, "failed to set ownerReferences: %v", err)
|
||||
}
|
||||
|
||||
// Rebuild endpointSelector to include all VMs
|
||||
selector := buildEndpointSelector(vmNamesFromOwnerRefs(ownerRefs))
|
||||
if err := unstructured.SetNestedField(existing.Object, selector, "spec", "endpointSelector"); err != nil {
|
||||
return status.Errorf(codes.Internal, "failed to set endpointSelector: %v", err)
|
||||
}
|
||||
|
||||
if _, err := w.dynamicClient.Resource(ciliumNetworkPolicyGVR).Namespace(namespace).Update(ctx, existing, metav1.UpdateOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -486,6 +489,13 @@ func (w *WrappedControllerService) removeCNPOwnerReference(ctx context.Context,
|
|||
if err := unstructured.SetNestedSlice(existing.Object, remaining, "metadata", "ownerReferences"); err != nil {
|
||||
return status.Errorf(codes.Internal, "failed to set ownerReferences: %v", err)
|
||||
}
|
||||
|
||||
// Rebuild endpointSelector from remaining VMs
|
||||
selector := buildEndpointSelector(vmNamesFromOwnerRefs(remaining))
|
||||
if err := unstructured.SetNestedField(existing.Object, selector, "spec", "endpointSelector"); err != nil {
|
||||
return status.Errorf(codes.Internal, "failed to set endpointSelector: %v", err)
|
||||
}
|
||||
|
||||
if _, err := w.dynamicClient.Resource(ciliumNetworkPolicyGVR).Namespace(namespace).Update(ctx, existing, metav1.UpdateOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -494,6 +504,37 @@ func (w *WrappedControllerService) removeCNPOwnerReference(ctx context.Context,
|
|||
})
|
||||
}
|
||||
|
||||
// buildEndpointSelector returns an endpointSelector using matchExpressions
|
||||
// so that multiple VMs can be listed in a single selector.
|
||||
func buildEndpointSelector(vmNames []string) map[string]interface{} {
|
||||
values := make([]interface{}, len(vmNames))
|
||||
for i, name := range vmNames {
|
||||
values[i] = name
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"matchExpressions": []interface{}{
|
||||
map[string]interface{}{
|
||||
"key": "kubevirt.io/vm",
|
||||
"operator": "In",
|
||||
"values": values,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// vmNamesFromOwnerRefs extracts VM names from ownerReferences.
|
||||
func vmNamesFromOwnerRefs(ownerRefs []interface{}) []string {
|
||||
var names []string
|
||||
for _, ref := range ownerRefs {
|
||||
if refMap, ok := ref.(map[string]interface{}); ok {
|
||||
if name, ok := refMap["name"].(string); ok {
|
||||
names = append(names, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
func hasRWXAccessMode(pvc *corev1.PersistentVolumeClaim) bool {
|
||||
for _, mode := range pvc.Spec.AccessModes {
|
||||
if mode == corev1.ReadWriteMany {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue