Browse Source

Ensure only affected services are updated on Pod/NetworkPolicy events

When Pods or Network Policies are created/updated/deleted, only the affected
service(s) should have the SG updated. Right now, all the services are updated.

This commit fixes the issue, on the Network Policy side, by checking if any of
the pods selected by a Service is also selected by a Network Policy, and if so
update the SG of that LBaaS.
And on the Pods side, by matching the Service selectors and Network Policy
selectors, when this NP got the pointed pods SG updated. If the selectors
match the LBaaS SG is updated.

Closes-Bug: 1818203
Change-Id: Id996651a7d03bc7621e57b46825ddfa9d98e48ce
Maysa Macedo 1 month ago
parent
commit
660bbf039a

+ 6
- 0
kuryr_kubernetes/controller/drivers/base.py View File

@@ -251,6 +251,8 @@ class PodSecurityGroupsDriver(DriverBase):
251 251
         """Create security group rules for a pod.
252 252
 
253 253
         :param pod: dict containing Kubernetes Pod object
254
+        :return: a list containing podSelectors of CRDs
255
+        that had security group rules created
254 256
         """
255 257
         raise NotImplementedError()
256 258
 
@@ -258,6 +260,8 @@ class PodSecurityGroupsDriver(DriverBase):
258 260
         """Delete security group rules for a pod
259 261
 
260 262
         :param pod: dict containing Kubernetes Pod object
263
+        :return: a list containing podSelectors of CRDs
264
+        that had security group rules deleted
261 265
         """
262 266
         raise NotImplementedError()
263 267
 
@@ -265,6 +269,8 @@ class PodSecurityGroupsDriver(DriverBase):
265 269
         """Update security group rules for a pod
266 270
 
267 271
         :param pod: dict containing Kubernetes Pod object
272
+        :return: a list containing podSelectors of CRDs
273
+        that had security group rules updated
268 274
         """
269 275
         raise NotImplementedError()
270 276
 

+ 10
- 3
kuryr_kubernetes/controller/drivers/network_policy_security_groups.py View File

@@ -215,6 +215,7 @@ class NetworkPolicySecurityGroupsDriver(base.PodSecurityGroupsDriver):
215 215
 
216 216
     def create_sg_rules(self, pod):
217 217
         LOG.debug("Creating sg rule for pod: %s", pod['metadata']['name'])
218
+        crd_pod_selectors = []
218 219
         knp_crds = driver_utils.get_kuryrnetpolicy_crds()
219 220
         for crd in knp_crds.get('items'):
220 221
             crd_selector = crd['spec'].get('podSelector')
@@ -225,11 +226,13 @@ class NetworkPolicySecurityGroupsDriver(base.PodSecurityGroupsDriver):
225 226
             if i_matched or e_matched:
226 227
                 driver_utils.patch_kuryr_crd(crd, i_rules,
227 228
                                              e_rules, crd_selector)
229
+                crd_pod_selectors.append(crd_selector)
230
+        return crd_pod_selectors
228 231
 
229 232
     def delete_sg_rules(self, pod):
230 233
         LOG.debug("Deleting sg rule for pod: %s", pod['metadata']['name'])
231 234
         pod_ip = driver_utils.get_pod_ip(pod)
232
-
235
+        crd_pod_selectors = []
233 236
         knp_crds = driver_utils.get_kuryrnetpolicy_crds()
234 237
         for crd in knp_crds.get('items'):
235 238
             crd_selector = crd['spec'].get('podSelector')
@@ -264,11 +267,15 @@ class NetworkPolicySecurityGroupsDriver(base.PodSecurityGroupsDriver):
264 267
             if matched:
265 268
                 driver_utils.patch_kuryr_crd(crd, i_rules, e_rules,
266 269
                                              crd_selector)
270
+                crd_pod_selectors.append(crd_selector)
271
+        return crd_pod_selectors
267 272
 
268 273
     def update_sg_rules(self, pod):
269 274
         LOG.debug("Updating sg rule for pod: %s", pod['metadata']['name'])
270
-        self.delete_sg_rules(pod)
271
-        self.create_sg_rules(pod)
275
+        crd_pod_selectors = []
276
+        crd_pod_selectors.extend(self.delete_sg_rules(pod))
277
+        crd_pod_selectors.extend(self.create_sg_rules(pod))
278
+        return crd_pod_selectors
272 279
 
273 280
     def delete_namespace_sg_rules(self, namespace):
274 281
         ns_name = namespace['metadata']['name']

+ 18
- 0
kuryr_kubernetes/controller/drivers/utils.py View File

@@ -405,3 +405,21 @@ def get_services(namespace):
405 405
                       'namespace %s', namespace)
406 406
         raise
407 407
     return services
408
+
409
+
410
+def service_matches_affected_pods(service, pod_selectors):
411
+    """Returns if the service is affected by the pod selectors
412
+
413
+    Checks if the service selector matches the labelSelectors of
414
+    NetworkPolicies.
415
+
416
+    param service: k8s service
417
+    param pod_selectors: a list of kubernetes labelSelectors
418
+    return: True if the service is selected by any of the labelSelectors
419
+            and False otherwise.
420
+    """
421
+    svc_selector = service['spec'].get('selector')
422
+    for selector in pod_selectors:
423
+        if match_selector(selector, svc_selector):
424
+            return True
425
+    return False

+ 16
- 1
kuryr_kubernetes/controller/handlers/pod_label.py View File

@@ -39,9 +39,11 @@ class PodLabelHandler(k8s_base.ResourceEventHandler):
39 39
         super(PodLabelHandler, self).__init__()
40 40
         self._drv_project = drivers.PodProjectDriver.get_instance()
41 41
         self._drv_sg = drivers.PodSecurityGroupsDriver.get_instance()
42
+        self._drv_svc_sg = drivers.ServiceSecurityGroupsDriver.get_instance()
42 43
         self._drv_vif_pool = drivers.VIFPoolDriver.get_instance(
43 44
             specific_driver='multi_pool')
44 45
         self._drv_vif_pool.set_vif_driver()
46
+        self._drv_lbaas = drivers.LBaaSDriver.get_instance()
45 47
 
46 48
     def on_present(self, pod):
47 49
         if driver_utils.is_host_network(pod) or not self._has_pod_state(pod):
@@ -57,13 +59,16 @@ class PodLabelHandler(k8s_base.ResourceEventHandler):
57 59
         if current_pod_labels == previous_pod_labels:
58 60
             return
59 61
 
60
-        self._drv_sg.update_sg_rules(pod)
62
+        crd_pod_selectors = self._drv_sg.update_sg_rules(pod)
61 63
 
62 64
         project_id = self._drv_project.get_project(pod)
63 65
         security_groups = self._drv_sg.get_security_groups(pod, project_id)
64 66
         self._drv_vif_pool.update_vif_sgs(pod, security_groups)
65 67
         self._set_pod_labels(pod, current_pod_labels)
66 68
 
69
+        services = driver_utils.get_services(pod['metadata']['namespace'])
70
+        self._update_services(services, crd_pod_selectors, project_id)
71
+
67 72
     def _get_pod_labels(self, pod):
68 73
         try:
69 74
             annotations = pod['metadata']['annotations']
@@ -94,3 +99,13 @@ class PodLabelHandler(k8s_base.ResourceEventHandler):
94 99
         except KeyError:
95 100
             return False
96 101
         return True
102
+
103
+    def _update_services(self, services, crd_pod_selectors, project_id):
104
+        for service in services.get('items'):
105
+            if (service['metadata']['name'] == 'kubernetes' or not
106
+                    driver_utils.service_matches_affected_pods(
107
+                        service, crd_pod_selectors)):
108
+                continue
109
+            sgs = self._drv_svc_sg.get_security_groups(service,
110
+                                                       project_id)
111
+            self._drv_lbaas.update_lbaas_sg(service, sgs)

+ 11
- 2
kuryr_kubernetes/controller/handlers/policy.py View File

@@ -85,7 +85,8 @@ class NetworkPolicyHandler(k8s_base.ResourceEventHandler):
85 85
             for service in services.get('items'):
86 86
                 # TODO(ltomasbo): Skip other services that are not affected
87 87
                 # by the policy
88
-                if service['metadata']['name'] == 'kubernetes':
88
+                if (service['metadata']['name'] == 'kubernetes' or not
89
+                        self._is_service_affected(service, pods_to_update)):
89 90
                     continue
90 91
                 sgs = self._drv_svc_sg.get_security_groups(service,
91 92
                                                            project_id)
@@ -119,7 +120,8 @@ class NetworkPolicyHandler(k8s_base.ResourceEventHandler):
119 120
             services = driver_utils.get_services(
120 121
                 policy['metadata']['namespace'])
121 122
             for service in services.get('items'):
122
-                if service['metadata']['name'] == 'kubernetes':
123
+                if (service['metadata']['name'] == 'kubernetes' or not
124
+                        self._is_service_affected(service, pods_to_update)):
123 125
                     continue
124 126
                 sgs = self._drv_svc_sg.get_security_groups(service,
125 127
                                                            project_id)
@@ -138,3 +140,10 @@ class NetworkPolicyHandler(k8s_base.ResourceEventHandler):
138 140
         if utils.has_limit(sg_quota):
139 141
             return utils.is_available('security_groups', sg_quota, sg_func)
140 142
         return True
143
+
144
+    def _is_service_affected(self, service, affected_pods):
145
+        svc_namespace = service['metadata']['namespace']
146
+        svc_selector = service['spec'].get('selector')
147
+        svc_pods = driver_utils.get_pods({'selector': svc_selector},
148
+                                         svc_namespace).get('items')
149
+        return any(pod in svc_pods for pod in affected_pods)

+ 9
- 6
kuryr_kubernetes/controller/handlers/vif.py View File

@@ -126,12 +126,13 @@ class VIFHandler(k8s_base.ResourceEventHandler):
126 126
                     changed = True
127 127
             if changed:
128 128
                 self._set_pod_state(pod, state)
129
-                self._drv_sg.create_sg_rules(pod)
129
+                crd_pod_selectors = self._drv_sg.create_sg_rules(pod)
130 130
 
131 131
                 if self._is_network_policy_enabled():
132 132
                     services = driver_utils.get_services(
133 133
                         pod['metadata']['namespace'])
134
-                    self._update_services(services, project_id)
134
+                    self._update_services(
135
+                        services, crd_pod_selectors, project_id)
135 136
 
136 137
     def on_deleted(self, pod):
137 138
         if driver_utils.is_host_network(pod):
@@ -139,7 +140,7 @@ class VIFHandler(k8s_base.ResourceEventHandler):
139 140
 
140 141
         services = driver_utils.get_services(pod['metadata']['namespace'])
141 142
         project_id = self._drv_project.get_project(pod)
142
-        self._drv_sg.delete_sg_rules(pod)
143
+        crd_pod_selectors = self._drv_sg.delete_sg_rules(pod)
143 144
         try:
144 145
             security_groups = self._drv_sg.get_security_groups(pod, project_id)
145 146
         except k_exc.ResourceNotReady:
@@ -158,7 +159,7 @@ class VIFHandler(k8s_base.ResourceEventHandler):
158 159
                 self._drv_vif_pool.release_vif(pod, vif, project_id,
159 160
                                                security_groups)
160 161
         if self._is_network_policy_enabled():
161
-            self._update_services(services, project_id)
162
+            self._update_services(services, crd_pod_selectors, project_id)
162 163
 
163 164
     @MEMOIZE
164 165
     def is_ready(self, quota):
@@ -208,9 +209,11 @@ class VIFHandler(k8s_base.ResourceEventHandler):
208 209
                       constants.K8S_ANNOTATION_LABEL: labels_annotation},
209 210
                      resource_version=pod['metadata']['resourceVersion'])
210 211
 
211
-    def _update_services(self, services, project_id):
212
+    def _update_services(self, services, crd_pod_selectors, project_id):
212 213
         for service in services.get('items'):
213
-            if service['metadata']['name'] == 'kubernetes':
214
+            if (service['metadata']['name'] == 'kubernetes' or not
215
+                    driver_utils.service_matches_affected_pods(
216
+                        service, crd_pod_selectors)):
214 217
                 continue
215 218
             sgs = self._drv_svc_sg.get_security_groups(service,
216 219
                                                        project_id)

+ 5
- 2
kuryr_kubernetes/tests/unit/controller/handlers/test_pod_label.py View File

@@ -32,7 +32,8 @@ class TestPodLabelHandler(test_base.TestCase):
32 32
         self._pod_link = mock.sentinel.pod_link
33 33
         self._pod = {
34 34
             'metadata': {'resourceVersion': self._pod_version,
35
-                         'selfLink': self._pod_link},
35
+                         'selfLink': self._pod_link,
36
+                         'namespace': 'default'},
36 37
             'status': {'phase': k_const.K8S_POD_STATUS_PENDING},
37 38
             'spec': {'hostNetwork': False,
38 39
                      'nodeName': 'hostname'}
@@ -72,7 +73,9 @@ class TestPodLabelHandler(test_base.TestCase):
72 73
         self.assertEqual(sg_driver, handler._drv_sg)
73 74
         self.assertEqual(vif_pool_driver, handler._drv_vif_pool)
74 75
 
75
-    def test_on_present(self):
76
+    @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_services')
77
+    def test_on_present(self,  m_get_services):
78
+        m_get_services.return_value = {"items": []}
76 79
         self._has_pod_state.return_value = True
77 80
         self._get_pod_labels.return_value = {'test1': 'test'}
78 81
 

Loading…
Cancel
Save