Kubernetes integration with OpenStack networking
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

test_network_policy.py 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. # Copyright 2018 Red Hat, Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import mock
  15. from kuryr_kubernetes import constants
  16. from kuryr_kubernetes.controller.drivers import network_policy
  17. from kuryr_kubernetes import exceptions
  18. from kuryr_kubernetes.tests import base as test_base
  19. from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix
  20. from kuryr_kubernetes import utils
  21. from neutronclient.common import exceptions as n_exc
  22. def get_pod_obj():
  23. return {
  24. 'status': {
  25. 'qosClass': 'BestEffort',
  26. 'hostIP': '192.168.1.2',
  27. },
  28. 'kind': 'Pod',
  29. 'spec': {
  30. 'schedulerName': 'default-scheduler',
  31. 'containers': [{
  32. 'name': 'busybox',
  33. 'image': 'busybox',
  34. 'resources': {}
  35. }],
  36. 'nodeName': 'kuryr-devstack'
  37. },
  38. 'metadata': {
  39. 'name': 'busybox-sleep1',
  40. 'namespace': 'default',
  41. 'resourceVersion': '53808',
  42. 'selfLink': '/api/v1/namespaces/default/pods/busybox-sleep1',
  43. 'uid': '452176db-4a85-11e7-80bd-fa163e29dbbb',
  44. 'annotations': {
  45. 'openstack.org/kuryr-vif': {}
  46. }
  47. }}
  48. class TestNetworkPolicyDriver(test_base.TestCase):
  49. def setUp(self):
  50. super(TestNetworkPolicyDriver, self).setUp()
  51. self._project_id = mock.sentinel.project_id
  52. self._policy_name = 'np-test'
  53. self._policy_uid = mock.sentinel.policy_uid
  54. self._policy_link = mock.sentinel.policy_link
  55. self._sg_id = mock.sentinel.sg_id
  56. self._i_rules = [{'security_group_rule': {'id': ''}}]
  57. self._e_rules = [{'security_group_rule': {'id': ''}}]
  58. self._policy = {
  59. 'apiVersion': u'networking.k8s.io/v1',
  60. 'kind': u'NetworkPolicy',
  61. 'metadata': {
  62. 'name': self._policy_name,
  63. 'resourceVersion': u'2259309',
  64. 'generation': 1,
  65. 'creationTimestamp': u'2018-09-18T14:09:51Z',
  66. 'namespace': u'default',
  67. 'annotations': {},
  68. 'selfLink': self._policy_link,
  69. 'uid': self._policy_uid
  70. },
  71. 'spec': {
  72. 'egress': [{'ports':
  73. [{'port': 5978, 'protocol': 'TCP'}],
  74. 'to':
  75. [{'namespaceSelector': {
  76. 'matchLabels': {
  77. 'project': 'myproject'}}}]}],
  78. 'ingress': [{'ports':
  79. [{'port': 6379, 'protocol': 'TCP'}],
  80. 'from':
  81. [{'namespaceSelector': {
  82. 'matchLabels': {
  83. 'project': 'myproject'}}}]}],
  84. 'policyTypes': ['Ingress', 'Egress']
  85. }
  86. }
  87. self._crd = {
  88. 'metadata': {'name': mock.sentinel.name,
  89. 'selfLink': mock.sentinel.selfLink},
  90. 'spec': {
  91. 'egressSgRules': [
  92. {'security_group_rule':
  93. {'description': 'Kuryr-Kubernetes NetPolicy SG rule',
  94. 'direction': 'egress',
  95. 'ethertype': 'IPv4',
  96. 'port_range_max': 5978,
  97. 'port_range_min': 5978,
  98. 'protocol': 'tcp',
  99. 'security_group_id': self._sg_id,
  100. 'id': mock.sentinel.id
  101. }}],
  102. 'ingressSgRules': [
  103. {'security_group_rule':
  104. {'description': 'Kuryr-Kubernetes NetPolicy SG rule',
  105. 'direction': 'ingress',
  106. 'ethertype': 'IPv4',
  107. 'port_range_max': 6379,
  108. 'port_range_min': 6379,
  109. 'protocol': 'tcp',
  110. 'security_group_id': self._sg_id,
  111. 'id': mock.sentinel.id
  112. }}],
  113. 'networkpolicy_spec': self._policy['spec'],
  114. 'securityGroupId': self._sg_id,
  115. 'securityGroupName': mock.sentinel.sg_name}}
  116. self.neutron = self.useFixture(k_fix.MockNeutronClient()).client
  117. self.kubernetes = self.useFixture(k_fix.MockK8sClient()).client
  118. self._driver = network_policy.NetworkPolicyDriver()
  119. @mock.patch.object(network_policy.NetworkPolicyDriver,
  120. 'get_kuryrnetpolicy_crd', return_value=False)
  121. @mock.patch.object(network_policy.NetworkPolicyDriver,
  122. 'create_security_group_rules_from_network_policy')
  123. @mock.patch.object(network_policy.NetworkPolicyDriver,
  124. 'update_security_group_rules_from_network_policy')
  125. def test_ensure_network_policy(self, m_update, m_create, m_get_crd):
  126. self._driver.ensure_network_policy(self._policy, self._project_id)
  127. m_get_crd.assert_called_once_with(self._policy)
  128. m_create.assert_called_once_with(self._policy, self._project_id)
  129. m_update.assert_not_called()
  130. @mock.patch.object(network_policy.NetworkPolicyDriver, 'affected_pods')
  131. @mock.patch.object(network_policy.NetworkPolicyDriver, 'namespaced_pods')
  132. @mock.patch.object(network_policy.NetworkPolicyDriver,
  133. 'get_kuryrnetpolicy_crd', return_value=True)
  134. @mock.patch.object(network_policy.NetworkPolicyDriver,
  135. 'create_security_group_rules_from_network_policy')
  136. @mock.patch.object(network_policy.NetworkPolicyDriver,
  137. 'update_security_group_rules_from_network_policy')
  138. def test_ensure_network_policy_with_existing_crd(
  139. self, m_update, m_create, m_get_crd, m_namespaced, m_affected):
  140. previous_selector = mock.sentinel.previous_selector
  141. m_update.return_value = previous_selector
  142. self._driver.ensure_network_policy(self._policy, self._project_id)
  143. m_get_crd.assert_called_once_with(self._policy)
  144. m_create.assert_not_called()
  145. m_update.assert_called_once_with(self._policy)
  146. m_affected.assert_called_once_with(self._policy, previous_selector)
  147. m_namespaced.assert_not_called()
  148. @mock.patch.object(network_policy.NetworkPolicyDriver, 'affected_pods')
  149. @mock.patch.object(network_policy.NetworkPolicyDriver, 'namespaced_pods')
  150. @mock.patch.object(network_policy.NetworkPolicyDriver,
  151. 'get_kuryrnetpolicy_crd', return_value=True)
  152. @mock.patch.object(network_policy.NetworkPolicyDriver,
  153. 'create_security_group_rules_from_network_policy')
  154. @mock.patch.object(network_policy.NetworkPolicyDriver,
  155. 'update_security_group_rules_from_network_policy')
  156. def test_ensure_network_policy_with_existing_crd_no_selector(
  157. self, m_update, m_create, m_get_crd, m_namespaced, m_affected):
  158. m_update.return_value = None
  159. self._driver.ensure_network_policy(self._policy, self._project_id)
  160. m_get_crd.assert_called_once_with(self._policy)
  161. m_create.assert_not_called()
  162. m_update.assert_called_once_with(self._policy)
  163. m_affected.assert_not_called()
  164. m_namespaced.assert_called_once_with(self._policy)
  165. @mock.patch.object(network_policy.NetworkPolicyDriver,
  166. 'get_kuryrnetpolicy_crd')
  167. @mock.patch.object(network_policy.NetworkPolicyDriver,
  168. '_add_kuryrnetpolicy_crd')
  169. @mock.patch.object(network_policy.NetworkPolicyDriver,
  170. 'parse_network_policy_rules')
  171. @mock.patch.object(utils, 'get_subnet_cidr')
  172. def test_create_security_group_rules_from_network_policy(self, m_utils,
  173. m_parse,
  174. m_add_crd,
  175. m_get_crd):
  176. self._driver.neutron.create_security_group.return_value = {
  177. 'security_group': {'id': mock.sentinel.id}}
  178. m_utils.get_subnet_cidr.return_value = {
  179. 'subnet': {'cidr': mock.sentinel.cidr}}
  180. m_parse.return_value = (self._i_rules, self._e_rules)
  181. self._driver.neutron.create_security_group_rule.return_value = {
  182. 'security_group_rule': {'id': mock.sentinel.id}}
  183. self._driver.create_security_group_rules_from_network_policy(
  184. self._policy, self._project_id)
  185. m_get_crd.assert_called_once()
  186. m_add_crd.assert_called_once()
  187. @mock.patch.object(network_policy.NetworkPolicyDriver,
  188. 'get_kuryrnetpolicy_crd')
  189. @mock.patch.object(network_policy.NetworkPolicyDriver,
  190. '_add_kuryrnetpolicy_crd')
  191. @mock.patch.object(network_policy.NetworkPolicyDriver,
  192. 'parse_network_policy_rules')
  193. @mock.patch.object(utils, 'get_subnet_cidr')
  194. def test_create_security_group_rules_with_k8s_exc(self, m_utils, m_parse,
  195. m_add_crd, m_get_crd):
  196. self._driver.neutron.create_security_group.return_value = {
  197. 'security_group': {'id': mock.sentinel.id}}
  198. m_utils.get_subnet_cidr.return_value = {
  199. 'subnet': {'cidr': mock.sentinel.cidr}}
  200. m_parse.return_value = (self._i_rules, self._e_rules)
  201. m_get_crd.side_effect = exceptions.K8sClientException
  202. self._driver.neutron.create_security_group_rule.return_value = {
  203. 'security_group_rule': {'id': mock.sentinel.id}}
  204. self.assertRaises(
  205. exceptions.K8sClientException,
  206. self._driver.create_security_group_rules_from_network_policy,
  207. self._policy, self._project_id)
  208. m_add_crd.assert_called_once()
  209. @mock.patch.object(network_policy.NetworkPolicyDriver,
  210. 'get_kuryrnetpolicy_crd')
  211. @mock.patch.object(network_policy.NetworkPolicyDriver,
  212. '_add_kuryrnetpolicy_crd')
  213. @mock.patch.object(network_policy.NetworkPolicyDriver,
  214. 'parse_network_policy_rules')
  215. @mock.patch.object(utils, 'get_subnet_cidr')
  216. def test_create_security_group_rules_error_add_crd(self, m_utils, m_parse,
  217. m_add_crd, m_get_crd):
  218. self._driver.neutron.create_security_group.return_value = {
  219. 'security_group': {'id': mock.sentinel.id}}
  220. m_utils.get_subnet_cidr.return_value = {
  221. 'subnet': {'cidr': mock.sentinel.cidr}}
  222. m_parse.return_value = (self._i_rules, self._e_rules)
  223. m_add_crd.side_effect = exceptions.K8sClientException
  224. self._driver.neutron.create_security_group_rule.return_value = {
  225. 'security_group_rule': {'id': mock.sentinel.id}}
  226. self.assertRaises(
  227. exceptions.K8sClientException,
  228. self._driver.create_security_group_rules_from_network_policy,
  229. self._policy, self._project_id)
  230. m_get_crd.assert_not_called()
  231. def test_create_security_group_rules_with_n_exc(self):
  232. self._driver.neutron.create_security_group.side_effect = (
  233. n_exc.NeutronClientException())
  234. self.assertRaises(
  235. n_exc.NeutronClientException,
  236. self._driver.create_security_group_rules_from_network_policy,
  237. self._policy, self._project_id)
  238. @mock.patch('kuryr_kubernetes.controller.drivers.utils.'
  239. 'create_security_group_rule')
  240. @mock.patch.object(network_policy.NetworkPolicyDriver,
  241. 'get_kuryrnetpolicy_crd')
  242. @mock.patch.object(network_policy.NetworkPolicyDriver,
  243. 'parse_network_policy_rules')
  244. def test_update_security_group_rules(self, m_parse, m_get_crd,
  245. m_create_sgr):
  246. policy = self._policy.copy()
  247. policy['spec']['podSelector'] = {'matchLabels': {'test': 'test'}}
  248. m_get_crd.return_value = self._crd
  249. m_parse.return_value = (self._i_rules, self._e_rules)
  250. self._driver.update_security_group_rules_from_network_policy(
  251. policy)
  252. m_parse.assert_called_with(policy, self._sg_id)
  253. @mock.patch('kuryr_kubernetes.controller.drivers.utils.'
  254. 'create_security_group_rule')
  255. @mock.patch.object(network_policy.NetworkPolicyDriver,
  256. 'get_kuryrnetpolicy_crd')
  257. @mock.patch.object(network_policy.NetworkPolicyDriver,
  258. 'parse_network_policy_rules')
  259. def test_update_security_group_rules_with_k8s_exc(self, m_parse, m_get_crd,
  260. m_create_sgr):
  261. self._driver.kubernetes.patch.side_effect = (
  262. exceptions.K8sClientException())
  263. m_get_crd.return_value = self._crd
  264. m_parse.return_value = (self._i_rules, self._e_rules)
  265. self.assertRaises(
  266. exceptions.K8sClientException,
  267. self._driver.update_security_group_rules_from_network_policy,
  268. self._policy)
  269. m_parse.assert_called_with(self._policy, self._sg_id)
  270. def test_get_namespaces_cidr(self):
  271. namespace_selector = {'matchLabels': {'test': 'test'}}
  272. pod = get_pod_obj()
  273. annotation = mock.sentinel.annotation
  274. subnet_cidr = mock.sentinel.subnet_cidr
  275. net_crd = {'spec': {'subnetCIDR': subnet_cidr}}
  276. pod['metadata']['annotations'][constants.K8S_ANNOTATION_NET_CRD] = (
  277. annotation)
  278. self.kubernetes.get.side_effect = [{'items': [pod]}, net_crd]
  279. resp = self._driver._get_namespaces_cidr(namespace_selector)
  280. self.assertEqual(subnet_cidr, resp[0].get('cidr'))
  281. self.kubernetes.get.assert_called()
  282. def test_get_namespaces_cidr_no_matches(self):
  283. namespace_selector = {'matchLabels': {'test': 'test'}}
  284. self.kubernetes.get.return_value = {'items': []}
  285. resp = self._driver._get_namespaces_cidr(namespace_selector)
  286. self.assertEqual([], resp)
  287. self.kubernetes.get.assert_called_once()
  288. def test_get_namespaces_cidr_no_annotations(self):
  289. namespace_selector = {'matchLabels': {'test': 'test'}}
  290. pod = get_pod_obj()
  291. self.kubernetes.get.return_value = {'items': [pod]}
  292. self.assertRaises(exceptions.ResourceNotReady,
  293. self._driver._get_namespaces_cidr,
  294. namespace_selector)
  295. self.kubernetes.get.assert_called_once()
  296. @mock.patch.object(network_policy.NetworkPolicyDriver,
  297. '_get_namespaces_cidr')
  298. @mock.patch('kuryr_kubernetes.controller.drivers.utils.'
  299. 'create_security_group_rule_body')
  300. def test_parse_network_policy_rules_with_rules(self, m_create,
  301. m_get_ns_cidr):
  302. subnet_cidr = '10.10.0.0/24'
  303. m_get_ns_cidr.return_value = [{'cidr': subnet_cidr, 'namespace': ''}]
  304. self._driver.parse_network_policy_rules(self._policy, self._sg_id)
  305. m_create.assert_called()
  306. m_get_ns_cidr.assert_called()
  307. @mock.patch.object(network_policy.NetworkPolicyDriver,
  308. '_get_namespaces_cidr')
  309. @mock.patch('kuryr_kubernetes.controller.drivers.utils.'
  310. 'create_security_group_rule_body')
  311. def test_parse_network_policy_rules_with_no_rules(self, m_create,
  312. m_get_ns_cidr):
  313. policy = self._policy.copy()
  314. policy['spec']['ingress'] = [{}]
  315. policy['spec']['egress'] = [{}]
  316. self._driver.parse_network_policy_rules(policy, self._sg_id)
  317. m_get_ns_cidr.assert_not_called()
  318. calls = [mock.call(self._sg_id, 'ingress', port_range_min=1,
  319. port_range_max=65535),
  320. mock.call(self._sg_id, 'egress', port_range_min=1,
  321. port_range_max=65535)]
  322. m_create.assert_has_calls(calls)
  323. @mock.patch.object(network_policy.NetworkPolicyDriver,
  324. '_get_namespaces_cidr')
  325. @mock.patch('kuryr_kubernetes.controller.drivers.utils.'
  326. 'create_security_group_rule_body')
  327. def test_parse_network_policy_rules_with_no_pod_selector(self, m_create,
  328. m_get_ns_cidr):
  329. policy = self._policy.copy()
  330. policy['spec']['ingress'] = [{'ports':
  331. [{'port': 6379, 'protocol': 'TCP'}]}]
  332. policy['spec']['egress'] = [{'ports':
  333. [{'port': 6379, 'protocol': 'TCP'}]}]
  334. self._driver.parse_network_policy_rules(policy, self._sg_id)
  335. m_create.assert_called()
  336. m_get_ns_cidr.assert_not_called()
  337. @mock.patch.object(network_policy.NetworkPolicyDriver,
  338. '_get_namespaces_cidr')
  339. @mock.patch('kuryr_kubernetes.controller.drivers.utils.'
  340. 'create_security_group_rule_body')
  341. def test_parse_network_policy_rules_with_no_ports(self, m_create,
  342. m_get_ns_cidr):
  343. subnet_cidr = '10.10.0.0/24'
  344. m_get_ns_cidr.return_value = [{'cidr': subnet_cidr, 'namespace': ''}]
  345. policy = self._policy.copy()
  346. selectors = {'namespaceSelector': {
  347. 'matchLabels': {
  348. 'project': 'myproject'}}}
  349. policy['spec']['egress'] = [
  350. {'to':
  351. [selectors]}]
  352. policy['spec']['ingress'] = [
  353. {'from':
  354. [selectors]}]
  355. selectors = {'namespace_selector': selectors['namespaceSelector']}
  356. self._driver.parse_network_policy_rules(policy, self._sg_id)
  357. m_get_ns_cidr.assert_called()
  358. calls = [mock.call(self._sg_id, 'ingress', port_range_min=1,
  359. port_range_max=65535, cidr=subnet_cidr,
  360. namespace=''),
  361. mock.call(self._sg_id, 'egress', port_range_min=1,
  362. port_range_max=65535, cidr=subnet_cidr,
  363. namespace='')]
  364. m_create.assert_has_calls(calls)
  365. def test_knps_on_namespace(self):
  366. self.kubernetes.get.return_value = {'items': ['not-empty']}
  367. namespace = 'test1'
  368. resp = self._driver.knps_on_namespace(namespace)
  369. self.assertTrue(resp)
  370. def test_knps_on_namespace_empty(self):
  371. self.kubernetes.get.return_value = {'items': []}
  372. namespace = 'test1'
  373. resp = self._driver.knps_on_namespace(namespace)
  374. self.assertFalse(resp)
  375. @mock.patch.object(network_policy.NetworkPolicyDriver, 'namespaced_pods')
  376. def test_affected_pods(self, m_namespaced):
  377. self._driver.affected_pods(self._policy)
  378. m_namespaced.assert_called_once_with(self._policy)
  379. self.kubernetes.assert_not_called()
  380. @mock.patch.object(network_policy.NetworkPolicyDriver, 'namespaced_pods')
  381. def test_affected_pods_with_podselector(self, m_namespaced):
  382. self.kubernetes.get.return_value = {'items': []}
  383. selector = {'matchLabels': {'test': 'test'}}
  384. self._driver.affected_pods(self._policy, selector)
  385. m_namespaced.assert_not_called()
  386. def test_namespaced_pods(self):
  387. self.kubernetes.get.return_value = {'items': []}
  388. resp = self._driver.namespaced_pods(self._policy)
  389. self.assertEqual([], resp)