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_security_groups.py 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  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.controller.drivers import network_policy_security_groups
  16. from kuryr_kubernetes.tests import base as test_base
  17. from kuryr_kubernetes.tests.unit import kuryr_fixtures as k_fix
  18. from oslo_config import cfg
  19. class TestNetworkPolicySecurityGroupsDriver(test_base.TestCase):
  20. def setUp(self):
  21. super(TestNetworkPolicySecurityGroupsDriver, self).setUp()
  22. self._project_id = mock.sentinel.project_id
  23. self._sg_id = mock.sentinel.sg_id
  24. self._sg_id2 = mock.sentinel._sg_id2
  25. self._namespace = 'default'
  26. self._crd = {
  27. 'metadata': {'name': mock.sentinel.name,
  28. 'selfLink': mock.sentinel.selfLink},
  29. 'spec': {
  30. 'egressSgRules': [
  31. {'security_group_rule':
  32. {'description': 'Kuryr-Kubernetes NetPolicy SG rule',
  33. 'direction': 'egress',
  34. 'ethertype': 'IPv4',
  35. 'port_range_max': 5978,
  36. 'port_range_min': 5978,
  37. 'protocol': 'tcp',
  38. 'security_group_id': self._sg_id,
  39. 'id': mock.sentinel.id
  40. }}],
  41. 'ingressSgRules': [
  42. {'security_group_rule':
  43. {'description': 'Kuryr-Kubernetes NetPolicy SG rule',
  44. 'direction': 'ingress',
  45. 'ethertype': 'IPv4',
  46. 'port_range_max': 6379,
  47. 'port_range_min': 6379,
  48. 'protocol': 'tcp',
  49. 'security_group_id': self._sg_id,
  50. 'id': mock.sentinel.id
  51. }}],
  52. 'podSelector': {
  53. 'matchExpressions': [
  54. {
  55. 'key': 'environment',
  56. 'operator': 'In',
  57. 'values': [
  58. 'production']}],
  59. 'matchLabels': {
  60. 'run': 'demo'
  61. }},
  62. 'securityGroupId': self._sg_id,
  63. 'securityGroupName': mock.sentinel.sg_name}}
  64. self._crd2 = {
  65. 'metadata': {'name': mock.sentinel.name3,
  66. 'selfLink': mock.sentinel.selfLink},
  67. 'spec': {
  68. 'ingressSgRules': [
  69. {'security_group_rule':
  70. {'description': 'Kuryr-Kubernetes NetPolicy SG rule',
  71. 'direction': 'ingress',
  72. 'ethertype': 'IPv4',
  73. 'port_range_max': 8080,
  74. 'port_range_min': 8080,
  75. 'protocol': 'tcp',
  76. 'security_group_id': self._sg_id2,
  77. 'id': mock.sentinel.id
  78. }}],
  79. 'podSelector': {},
  80. 'securityGroupId': self._sg_id2,
  81. 'securityGroupName': mock.sentinel.sg_name}}
  82. self._crds = {
  83. "apiVersion": "v1",
  84. "items": [self._crd],
  85. "kind": "List",
  86. "metadata": {
  87. "resourceVersion": "",
  88. "selfLink": mock.sentinel.selfLink}}
  89. self._multiple_crds = {
  90. "apiVersion": "v1",
  91. "items": [self._crd, self._crd2],
  92. "kind": "List",
  93. "metadata": {
  94. "resourceVersion": "",
  95. "selfLink": mock.sentinel.selfLink}}
  96. self._empty_crds = {
  97. "apiVersion": "v1",
  98. "items": [],
  99. "kind": "List",
  100. "metadata": {
  101. "resourceVersion": "",
  102. "selfLink": mock.sentinel.selfLink}}
  103. self._pod = {
  104. 'apiVersion': 'v1',
  105. 'kind': 'Pod',
  106. 'metadata': {
  107. 'name': mock.sentinel.pod_name,
  108. 'namespace': self._namespace,
  109. 'labels': {
  110. 'run': 'demo',
  111. 'environment': 'production'}},
  112. 'spec': {
  113. 'containers': [{
  114. 'image': 'kuryr/demo',
  115. 'imagePullPolicy': 'Always',
  116. 'name': mock.sentinel.pod_name
  117. }]
  118. }}
  119. self._pod2 = {
  120. 'apiVersion': 'v1',
  121. 'kind': 'Pod',
  122. 'metadata': {
  123. 'name': mock.sentinel.pod_name,
  124. 'namespace': self._namespace,
  125. 'labels': {
  126. 'run': 'demo',
  127. 'environment': 'development'},
  128. 'annotations': {
  129. 'openstack.org/kuryr-pod-label': '{'
  130. '"run": "demo","environment": "development"}'}},
  131. 'spec': {
  132. 'containers': [{
  133. 'image': 'kuryr/demo',
  134. 'imagePullPolicy': 'Always',
  135. 'name': mock.sentinel.pod_name
  136. }]
  137. }}
  138. self._pod_without_label = {
  139. 'apiVersion': 'v1',
  140. 'kind': 'Pod',
  141. 'metadata': {
  142. 'name': mock.sentinel.pod_name,
  143. 'namespace': self._namespace},
  144. 'spec': {
  145. 'containers': [{
  146. 'image': 'kuryr/demo',
  147. 'imagePullPolicy': 'Always',
  148. 'name': mock.sentinel.pod_name
  149. }]
  150. }}
  151. self.kubernetes = self.useFixture(k_fix.MockK8sClient()).client
  152. self._driver = (
  153. network_policy_security_groups.NetworkPolicySecurityGroupsDriver())
  154. self._crd_sg_id = mock.sentinel.crd_sg_id
  155. self._crd_without_rules = {
  156. "apiVersion": "openstack.org/v1",
  157. "kind": "KuryrNetPolicy",
  158. "metadata": {"name": "np-test-network-policy",
  159. "namespace": "default"},
  160. "spec": {
  161. "egressSgRules": [],
  162. "ingressSgRules": [],
  163. "networkpolicy_spec": {
  164. "ingress": [
  165. {"from": [
  166. {"namespaceSelector": {
  167. "matchLabels": {"name": "dev"}},
  168. "podSelector": {
  169. "matchLabels": {"tier": "backend"}}}],
  170. "ports": [
  171. {"port": 6379,
  172. "protocol": "TCP"}]}],
  173. "podSelector": {"matchLabels": {"app": "demo"}},
  174. "policyTypes": ["Ingress"]},
  175. "podSelector": {"matchLabels": {"app": "demo"}},
  176. "securityGroupId": self._crd_sg_id}}
  177. self._pod_ip = mock.sentinel.pod_ip
  178. self._pod_dev_namespace = {
  179. 'apiVersion': 'v1',
  180. 'kind': 'Pod',
  181. 'metadata': {
  182. 'name': mock.sentinel.pod_name,
  183. 'namespace': 'dev',
  184. 'labels': {
  185. 'tier': 'backend'},
  186. 'annotations': {
  187. 'openstack.org/kuryr-pod-label': '{"tier": "backend"}'}},
  188. 'spec': {
  189. 'containers': [{
  190. 'image': 'kuryr/demo',
  191. 'imagePullPolicy': 'Always',
  192. 'name': mock.sentinel.pod_name
  193. }]},
  194. 'status': {'podIP': self._pod_ip}}
  195. self._sg_rule_body = {
  196. u'security_group_rule': {
  197. u'direction': 'ingress',
  198. u'protocol': u'tcp',
  199. u'description': 'Kuryr-Kubernetes NetPolicy SG rule',
  200. u'ethertype': 'IPv4',
  201. u'port_range_max': 6379,
  202. u'security_group_id': self._crd_sg_id,
  203. u'port_range_min': 6379,
  204. u'remote_ip_prefix': self._pod_ip}}
  205. self._new_rule_id = mock.sentinel.id
  206. self._crd_with_rule = {
  207. "apiVersion": "openstack.org/v1",
  208. "kind": "KuryrNetPolicy",
  209. "metadata": {"name": "np-test-network-policy",
  210. "namespace": "default"},
  211. "spec": {
  212. "egressSgRules": [],
  213. "ingressSgRules": [{
  214. "security_group_rule": {
  215. "description": "Kuryr-Kubernetes NetPolicy SG rule",
  216. "direction": "ingress",
  217. "ethertype": "IPv4",
  218. "id": self._new_rule_id,
  219. "port_range_max": 6379,
  220. "port_range_min": 6379,
  221. "protocol": "tcp",
  222. "remote_ip_prefix": self._pod_ip,
  223. "security_group_id": self._crd_sg_id}}],
  224. "networkpolicy_spec": {
  225. "ingress": [
  226. {"from": [
  227. {"namespaceSelector": {
  228. "matchLabels": {"name": "dev"}},
  229. "podSelector": {
  230. "matchLabels": {"tier": "backend"}}}],
  231. "ports": [
  232. {"port": 6379,
  233. "protocol": "TCP"}]}],
  234. "podSelector": {"matchLabels": {"app": "demo"}},
  235. "policyTypes": ["Ingress"]},
  236. "podSelector": {"matchLabels": {"app": "demo"}},
  237. "securityGroupId": self._crd_sg_id}}
  238. @mock.patch('kuryr_kubernetes.controller.drivers.utils.'
  239. 'create_security_group_rule')
  240. @mock.patch('kuryr_kubernetes.controller.drivers.utils.'
  241. 'create_security_group_rule_body')
  242. @mock.patch.object(network_policy_security_groups,
  243. '_match_selector', return_value=True)
  244. @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_pod_ip')
  245. def test__create_sg_rules(self, m_get_pod_ip,
  246. m_match_selector,
  247. m_create_sg_rule_body,
  248. m_create_sg_rule):
  249. m_get_pod_ip.return_value = self._pod_ip
  250. m_create_sg_rule_body.return_value = self._sg_rule_body
  251. sgr_id = mock.sentinel.sgr_id
  252. m_create_sg_rule.return_value = sgr_id
  253. crd = self._crd_without_rules
  254. pod = self._pod_dev_namespace
  255. matched = False
  256. new_sg_rule = self._sg_rule_body
  257. policy = crd['spec']['networkpolicy_spec']
  258. rule_list = policy.get('ingress', None)
  259. crd_rules = crd['spec'].get('ingressSgRules')
  260. for rule_block in rule_list:
  261. for rule in rule_block.get('from', []):
  262. namespace_selector = rule.get('namespaceSelector')
  263. pod_selector = rule.get('podSelector')
  264. matched = network_policy_security_groups._create_sg_rules(
  265. crd, pod, namespace_selector, pod_selector, rule_block,
  266. crd_rules, 'ingress', matched)
  267. new_sg_rule['namespaceSelector'] = namespace_selector
  268. new_sg_rule['podSelector'] = pod_selector
  269. new_sg_rule['security_group_rule']['id'] = sgr_id
  270. m_match_selector.assert_called_once_with(
  271. pod_selector, pod['metadata']['labels'])
  272. m_get_pod_ip.assert_called_once_with(pod)
  273. m_create_sg_rule_body.assert_called_once()
  274. m_create_sg_rule.assert_called_once()
  275. self.assertEqual([new_sg_rule], crd_rules)
  276. self.assertEqual(matched, True)
  277. @mock.patch.object(network_policy_security_groups,
  278. '_match_selector', return_value=False)
  279. def test__create_sg_rules_no_match(self, m_match_selector):
  280. crd = self._crd_without_rules
  281. pod = self._pod2
  282. policy = crd['spec']['networkpolicy_spec']
  283. rule_list = policy.get('ingress', None)
  284. crd_rules = crd['spec'].get('ingressSgRules')
  285. for rule_block in rule_list:
  286. for rule in rule_block.get('from', []):
  287. namespace_selector = rule.get('namespaceSelector')
  288. pod_selector = rule.get('podSelector')
  289. matched = network_policy_security_groups._create_sg_rules(
  290. crd, pod, namespace_selector, pod_selector, rule_block,
  291. crd_rules, 'ingress', False)
  292. self.assertEqual(matched, False)
  293. @mock.patch('kuryr_kubernetes.controller.drivers.utils.'
  294. 'patch_kuryr_crd')
  295. @mock.patch.object(network_policy_security_groups,
  296. '_get_kuryrnetpolicy_crds')
  297. @mock.patch('kuryr_kubernetes.controller.drivers.utils.'
  298. 'delete_security_group_rule')
  299. @mock.patch('kuryr_kubernetes.controller.drivers.utils.get_pod_ip')
  300. def test_delete_sg_rules(self, m_get_pod_ip, m_delete_sg_rule,
  301. m_get_knp_crds, m_patch_kuryr_crd):
  302. crd = self._crd_with_rule
  303. i_rule = crd['spec'].get('ingressSgRules')[0]
  304. sgr_id = i_rule['security_group_rule'].get('id')
  305. m_get_pod_ip.return_value = self._pod_ip
  306. m_get_knp_crds.return_value = {
  307. "apiVersion": "v1",
  308. "items": [crd],
  309. "kind": "List",
  310. "metadata": {
  311. "resourceVersion": "",
  312. "selfLink": mock.sentinel.selfLink}}
  313. i_rules = e_rules = []
  314. pod = self._pod_dev_namespace
  315. self._driver.delete_sg_rules(pod)
  316. m_get_knp_crds.assert_called_once()
  317. m_get_pod_ip.assert_called_once_with(pod)
  318. m_delete_sg_rule.assert_called_once_with(sgr_id)
  319. m_patch_kuryr_crd.assert_called_with(
  320. crd, i_rules, e_rules, crd['spec'].get('podSelector'))
  321. @mock.patch('kuryr_kubernetes.config.CONF')
  322. @mock.patch.object(network_policy_security_groups,
  323. '_get_kuryrnetpolicy_crds')
  324. def test_get_sgs_for_pod_without_label(self, m_get_crds, m_cfg):
  325. m_get_crds.return_value = self._crds
  326. sg_list = [str(mock.sentinel.sg_id)]
  327. m_cfg.neutron_defaults.pod_security_groups = sg_list
  328. sgs = self._driver.get_security_groups(self._pod_without_label,
  329. self._project_id)
  330. m_get_crds.assert_called_once_with(namespace=self._namespace)
  331. self.assertEqual(sg_list, sgs)
  332. @mock.patch.object(network_policy_security_groups,
  333. '_match_expressions')
  334. @mock.patch.object(network_policy_security_groups,
  335. '_match_labels')
  336. @mock.patch.object(network_policy_security_groups,
  337. '_get_kuryrnetpolicy_crds')
  338. def test_get_sgs_for_pod_with_label(self, m_get_crds, m_match_labels,
  339. m_match_expressions):
  340. m_get_crds.return_value = self._crds
  341. m_match_expressions.return_value = True
  342. m_match_labels.return_value = True
  343. pod_labels = self._pod['metadata']['labels']
  344. resp = self._driver.get_security_groups(self._pod, self._project_id)
  345. m_get_crds.assert_called_once_with(namespace=self._namespace)
  346. m_match_expressions.assert_called_once_with(
  347. self._crd['spec']['podSelector']['matchExpressions'], pod_labels)
  348. m_match_labels.assert_called_once_with(
  349. self._crd['spec']['podSelector']['matchLabels'], pod_labels)
  350. self.assertEqual(resp, [str(self._sg_id)])
  351. @mock.patch('kuryr_kubernetes.config.CONF')
  352. @mock.patch.object(network_policy_security_groups,
  353. '_match_expressions')
  354. @mock.patch.object(network_policy_security_groups,
  355. '_match_labels')
  356. @mock.patch.object(network_policy_security_groups,
  357. '_get_kuryrnetpolicy_crds')
  358. def test_get_sgs_for_pod_with_label_no_match(self, m_get_crds,
  359. m_match_labels,
  360. m_match_expressions, m_cfg):
  361. m_get_crds.return_value = self._crds
  362. m_match_expressions.return_value = False
  363. m_match_labels.return_value = True
  364. sg_list = [mock.sentinel.sg_id]
  365. m_cfg.neutron_defaults.pod_security_groups = sg_list
  366. pod_labels = self._pod2['metadata']['labels']
  367. sgs = self._driver.get_security_groups(self._pod2, self._project_id)
  368. m_get_crds.assert_called_once_with(namespace=self._namespace)
  369. m_match_expressions.assert_called_once_with(
  370. self._crd['spec']['podSelector']['matchExpressions'], pod_labels)
  371. m_match_labels.assert_called_once_with(
  372. self._crd['spec']['podSelector']['matchLabels'], pod_labels)
  373. self.assertEqual(sg_list, sgs)
  374. @mock.patch.object(network_policy_security_groups,
  375. '_get_kuryrnetpolicy_crds')
  376. def test_get_sgs_no_crds(self, m_get_crds):
  377. m_get_crds.return_value = self._empty_crds
  378. cfg.CONF.set_override('pod_security_groups', [],
  379. group='neutron_defaults')
  380. self.assertRaises(cfg.RequiredOptError,
  381. self._driver.get_security_groups, self._pod,
  382. self._project_id)
  383. m_get_crds.assert_called_with(namespace=self._namespace)
  384. @mock.patch.object(network_policy_security_groups,
  385. '_match_expressions')
  386. @mock.patch.object(network_policy_security_groups,
  387. '_match_labels')
  388. @mock.patch.object(network_policy_security_groups,
  389. '_get_kuryrnetpolicy_crds')
  390. def test_get_sgs_multiple_crds(self, m_get_crds, m_match_labels,
  391. m_match_expressions):
  392. m_match_expressions.return_value = True
  393. m_match_labels.return_value = True
  394. m_get_crds.return_value = self._multiple_crds
  395. resp = self._driver.get_security_groups(self._pod, self._project_id)
  396. m_get_crds.assert_called_once_with(namespace=self._namespace)
  397. self.assertEqual([str(self._sg_id), str(self._sg_id2)], resp)