Skip to content

Commit aebf471

Browse files
authored
Fix EndpointSlice reconciliation (#518)
Upstream fixes: - kubevirt/cloud-provider-kubevirt#335 - kubevirt/cloud-provider-kubevirt#336 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes - **New Features** - Incremented Kubernetes chart version to 0.14.1. - Introduced a new cloud provider controller for managing EndpointSlices in KubeVirt, enhancing responsiveness to service changes. - **Improvements** - Updated Docker image tag for kubevirt-cloud-provider to use the latest version. - Enhanced handling of EndpointSlices for LoadBalancer services, improving service management. - **Bug Fixes** - Improved error handling and logging for service retrieval and EndpointSlice management. - **Documentation** - Updated version mappings in the versions map file for clarity and tracking. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
1 parent d14b66c commit aebf471

File tree

7 files changed

+157
-1895
lines changed

7 files changed

+157
-1895
lines changed

packages/apps/kubernetes/Chart.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ type: application
1616
# This is the chart version. This version number should be incremented each time you make changes
1717
# to the chart and its templates, including the app version.
1818
# Versions are expected to follow Semantic Versioning (https://semver.org/)
19-
version: 0.14.0
19+
version: 0.14.1
2020

2121
# This is the version number of the application being deployed. This version number should be
2222
# incremented each time you make changes to the application. Versions are not expected to
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
ghcr.io/aenix-io/cozystack/kubevirt-cloud-provider:0.14.0@sha256:55b78220b60773eefb7b7d3451d7ab9fe89fb6b989e8fe2ae214aab164f00293
1+
ghcr.io/aenix-io/cozystack/kubevirt-cloud-provider:latest@sha256:8fc186c44669c15d001d84035caae2fe4676dc8eb0bce75496cff500d36e7570

packages/apps/kubernetes/images/kubevirt-cloud-provider/Dockerfile

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ FROM --platform=linux/amd64 golang:1.20.6 AS builder
33

44
RUN git clone https://github.com/kubevirt/cloud-provider-kubevirt /go/src/kubevirt.io/cloud-provider-kubevirt \
55
&& cd /go/src/kubevirt.io/cloud-provider-kubevirt \
6-
&& git checkout adbd6c27468b86b020cf38490e84f124ef24ab62
6+
&& git checkout da9e0cf
77

88
WORKDIR /go/src/kubevirt.io/cloud-provider-kubevirt
99

10-
# see: https://github.com/kubevirt/cloud-provider-kubevirt/pull/291
10+
# see: https://github.com/kubevirt/cloud-provider-kubevirt/pull/335
11+
# see: https://github.com/kubevirt/cloud-provider-kubevirt/pull/336
1112
ADD patches /patches
12-
RUN git apply /patches/external-traffic-policy-local.diff
13+
RUN git apply /patches/*.diff
1314
RUN go get 'k8s.io/endpointslice/util@v0.28' 'k8s.io/apiserver@v0.28'
1415
RUN go mod tidy
1516
RUN go mod vendor
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
diff --git a/pkg/controller/kubevirteps/kubevirteps_controller.go b/pkg/controller/kubevirteps/kubevirteps_controller.go
2+
index a3c1aa33..95c31438 100644
3+
--- a/pkg/controller/kubevirteps/kubevirteps_controller.go
4+
+++ b/pkg/controller/kubevirteps/kubevirteps_controller.go
5+
@@ -412,11 +412,11 @@ func (c *Controller) reconcileByAddressType(service *v1.Service, tenantSlices []
6+
// Create the desired port configuration
7+
var desiredPorts []discovery.EndpointPort
8+
9+
- for _, port := range service.Spec.Ports {
10+
+ for i := range service.Spec.Ports {
11+
desiredPorts = append(desiredPorts, discovery.EndpointPort{
12+
- Port: &port.TargetPort.IntVal,
13+
- Protocol: &port.Protocol,
14+
- Name: &port.Name,
15+
+ Port: &service.Spec.Ports[i].TargetPort.IntVal,
16+
+ Protocol: &service.Spec.Ports[i].Protocol,
17+
+ Name: &service.Spec.Ports[i].Name,
18+
})
19+
}
20+
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
diff --git a/pkg/controller/kubevirteps/kubevirteps_controller.go b/pkg/controller/kubevirteps/kubevirteps_controller.go
2+
index a3c1aa33..6f6e3d32 100644
3+
--- a/pkg/controller/kubevirteps/kubevirteps_controller.go
4+
+++ b/pkg/controller/kubevirteps/kubevirteps_controller.go
5+
@@ -108,32 +108,24 @@ func newRequest(reqType ReqType, obj interface{}, oldObj interface{}) *Request {
6+
}
7+
8+
func (c *Controller) Init() error {
9+
-
10+
- // Act on events from Services on the infra cluster. These are created by the EnsureLoadBalancer function.
11+
- // We need to watch for these events so that we can update the EndpointSlices in the infra cluster accordingly.
12+
+ // Existing Service event handlers...
13+
_, err := c.infraFactory.Core().V1().Services().Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
14+
AddFunc: func(obj interface{}) {
15+
- // cast obj to Service
16+
svc := obj.(*v1.Service)
17+
- // Only act on Services of type LoadBalancer
18+
if svc.Spec.Type == v1.ServiceTypeLoadBalancer {
19+
klog.Infof("Service added: %v/%v", svc.Namespace, svc.Name)
20+
c.queue.Add(newRequest(AddReq, obj, nil))
21+
}
22+
},
23+
UpdateFunc: func(oldObj, newObj interface{}) {
24+
- // cast obj to Service
25+
newSvc := newObj.(*v1.Service)
26+
- // Only act on Services of type LoadBalancer
27+
if newSvc.Spec.Type == v1.ServiceTypeLoadBalancer {
28+
klog.Infof("Service updated: %v/%v", newSvc.Namespace, newSvc.Name)
29+
c.queue.Add(newRequest(UpdateReq, newObj, oldObj))
30+
}
31+
},
32+
DeleteFunc: func(obj interface{}) {
33+
- // cast obj to Service
34+
svc := obj.(*v1.Service)
35+
- // Only act on Services of type LoadBalancer
36+
if svc.Spec.Type == v1.ServiceTypeLoadBalancer {
37+
klog.Infof("Service deleted: %v/%v", svc.Namespace, svc.Name)
38+
c.queue.Add(newRequest(DeleteReq, obj, nil))
39+
@@ -144,7 +136,7 @@ func (c *Controller) Init() error {
40+
return err
41+
}
42+
43+
- // Monitor endpoint slices that we are interested in based on known services in the infra cluster
44+
+ // Existing EndpointSlice event handlers in tenant cluster...
45+
_, err = c.tenantFactory.Discovery().V1().EndpointSlices().Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
46+
AddFunc: func(obj interface{}) {
47+
eps := obj.(*discovery.EndpointSlice)
48+
@@ -194,10 +186,80 @@ func (c *Controller) Init() error {
49+
return err
50+
}
51+
52+
- //TODO: Add informer for EndpointSlices in the infra cluster to watch for (unwanted) changes
53+
+ // Add an informer for EndpointSlices in the infra cluster
54+
+ _, err = c.infraFactory.Discovery().V1().EndpointSlices().Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
55+
+ AddFunc: func(obj interface{}) {
56+
+ eps := obj.(*discovery.EndpointSlice)
57+
+ if c.managedByController(eps) {
58+
+ svc, svcErr := c.getInfraServiceForEPS(context.TODO(), eps)
59+
+ if svcErr != nil {
60+
+ klog.Errorf("Failed to get infra Service for EndpointSlice %s/%s: %v", eps.Namespace, eps.Name, svcErr)
61+
+ return
62+
+ }
63+
+ if svc != nil {
64+
+ klog.Infof("Infra EndpointSlice added: %v/%v, requeuing Service: %v/%v", eps.Namespace, eps.Name, svc.Namespace, svc.Name)
65+
+ c.queue.Add(newRequest(AddReq, svc, nil))
66+
+ }
67+
+ }
68+
+ },
69+
+ UpdateFunc: func(oldObj, newObj interface{}) {
70+
+ eps := newObj.(*discovery.EndpointSlice)
71+
+ if c.managedByController(eps) {
72+
+ svc, svcErr := c.getInfraServiceForEPS(context.TODO(), eps)
73+
+ if svcErr != nil {
74+
+ klog.Errorf("Failed to get infra Service for EndpointSlice %s/%s: %v", eps.Namespace, eps.Name, svcErr)
75+
+ return
76+
+ }
77+
+ if svc != nil {
78+
+ klog.Infof("Infra EndpointSlice updated: %v/%v, requeuing Service: %v/%v", eps.Namespace, eps.Name, svc.Namespace, svc.Name)
79+
+ c.queue.Add(newRequest(UpdateReq, svc, nil))
80+
+ }
81+
+ }
82+
+ },
83+
+ DeleteFunc: func(obj interface{}) {
84+
+ eps := obj.(*discovery.EndpointSlice)
85+
+ if c.managedByController(eps) {
86+
+ svc, svcErr := c.getInfraServiceForEPS(context.TODO(), eps)
87+
+ if svcErr != nil {
88+
+ klog.Errorf("Failed to get infra Service for EndpointSlice %s/%s on delete: %v", eps.Namespace, eps.Name, svcErr)
89+
+ return
90+
+ }
91+
+ if svc != nil {
92+
+ klog.Infof("Infra EndpointSlice deleted: %v/%v, requeuing Service: %v/%v", eps.Namespace, eps.Name, svc.Namespace, svc.Name)
93+
+ c.queue.Add(newRequest(DeleteReq, svc, nil))
94+
+ }
95+
+ }
96+
+ },
97+
+ })
98+
+ if err != nil {
99+
+ return err
100+
+ }
101+
+
102+
return nil
103+
}
104+
105+
+// getInfraServiceForEPS returns the Service in the infra cluster associated with the given EndpointSlice.
106+
+// It does this by reading the "kubernetes.io/service-name" label from the EndpointSlice, which should correspond
107+
+// to the Service name. If not found or if the Service doesn't exist, it returns nil.
108+
+func (c *Controller) getInfraServiceForEPS(ctx context.Context, eps *discovery.EndpointSlice) (*v1.Service, error) {
109+
+ svcName := eps.Labels[discovery.LabelServiceName]
110+
+ if svcName == "" {
111+
+ // No service name label found, can't determine infra service.
112+
+ return nil, nil
113+
+ }
114+
+
115+
+ svc, err := c.infraClient.CoreV1().Services(c.infraNamespace).Get(ctx, svcName, metav1.GetOptions{})
116+
+ if err != nil {
117+
+ if k8serrors.IsNotFound(err) {
118+
+ // Service doesn't exist
119+
+ return nil, nil
120+
+ }
121+
+ return nil, err
122+
+ }
123+
+
124+
+ return svc, nil
125+
+}
126+
+
127+
// Run starts an asynchronous loop that monitors and updates GKENetworkParamSet in the cluster.
128+
func (c *Controller) Run(numWorkers int, stopCh <-chan struct{}, controllerManagerMetrics *controllersmetrics.ControllerManagerMetrics) {
129+
defer utilruntime.HandleCrash()

0 commit comments

Comments
 (0)