test_kubernetes.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927
  1. # -*- coding: utf-8 -*-
  2. '''
  3. :codeauthor: :email:`Jeff Schroeder <jeffschroeder@computer.org>`
  4. '''
  5. # Import Python libs
  6. from __future__ import absolute_import, unicode_literals, print_function
  7. import base64
  8. from contextlib import contextmanager
  9. # Import Salt Testing Libs
  10. from tests.support.mixins import LoaderModuleMockMixin
  11. from tests.support.unit import skipIf, TestCase
  12. from tests.support.mock import (
  13. MagicMock,
  14. patch)
  15. # Import Salt Libs
  16. import salt.utils.stringutils
  17. from salt.states import kubernetes
  18. from salt.ext import six
  19. @skipIf(kubernetes is False, "Probably Kubernetes client lib is not installed. \
  20. Skipping test_kubernetes.py")
  21. class KubernetesTestCase(TestCase, LoaderModuleMockMixin):
  22. '''
  23. Test cases for salt.states.kubernetes
  24. '''
  25. def setup_loader_modules(self):
  26. return {kubernetes: {'__env__': 'base'}}
  27. @contextmanager
  28. def mock_func(self, func_name, return_value, test=False):
  29. '''
  30. Mock any of the kubernetes state function return values and set
  31. the test options.
  32. '''
  33. name = 'kubernetes.{0}'.format(func_name)
  34. mocked = {name: MagicMock(return_value=return_value)}
  35. with patch.dict(kubernetes.__salt__, mocked) as patched:
  36. with patch.dict(kubernetes.__opts__, {'test': test}):
  37. yield patched
  38. def make_configmap(self, name, namespace='default', data=None):
  39. return self.make_ret_dict(
  40. kind='ConfigMap',
  41. name=name,
  42. namespace=namespace,
  43. data=data,
  44. )
  45. def make_secret(self, name, namespace='default', data=None):
  46. secret_data = self.make_ret_dict(
  47. kind='Secret',
  48. name=name,
  49. namespace=namespace,
  50. data=data,
  51. )
  52. # Base64 all of the values just like kubectl does
  53. for key, value in six.iteritems(secret_data['data']):
  54. secret_data['data'][key] = base64.b64encode(
  55. salt.utils.stringutils.to_bytes(value)
  56. )
  57. return secret_data
  58. def make_node_labels(self, name='minikube'):
  59. return {
  60. 'kubernetes.io/hostname': name,
  61. 'beta.kubernetes.io/os': 'linux',
  62. 'beta.kubernetes.io/arch': 'amd64',
  63. 'failure-domain.beta.kubernetes.io/region': 'us-west-1',
  64. }
  65. def make_node(self, name='minikube'):
  66. node_data = self.make_ret_dict(kind='Node', name='minikube')
  67. node_data.update({
  68. 'api_version': 'v1',
  69. 'kind': 'Node',
  70. 'metadata': {
  71. 'annotations': {
  72. 'node.alpha.kubernetes.io/ttl': '0',
  73. },
  74. 'labels': self.make_node_labels(name=name),
  75. 'name': name,
  76. 'namespace': None,
  77. 'self_link': '/api/v1/nodes/{name}'.format(name=name),
  78. 'uid': '7811b8ae-c1a1-11e7-a55a-0800279fb61e',
  79. },
  80. 'spec': {
  81. 'external_id': name,
  82. },
  83. 'status': {},
  84. })
  85. return node_data
  86. def make_namespace(self, name='default'):
  87. namespace_data = self.make_ret_dict(kind='Namespace', name=name)
  88. del namespace_data['data']
  89. namespace_data.update({
  90. 'status': {
  91. 'phase': 'Active',
  92. },
  93. 'spec': {
  94. 'finalizers': ['kubernetes'],
  95. },
  96. 'metadata': {
  97. 'name': name,
  98. 'namespace': None,
  99. 'labels': None,
  100. 'self_link': '/api/v1/namespaces/{namespace}'.format(
  101. namespace=name,
  102. ),
  103. 'annotations': None,
  104. 'uid': '752fceeb-c1a1-11e7-a55a-0800279fb61e',
  105. },
  106. })
  107. return namespace_data
  108. def make_ret_dict(self, kind, name, namespace=None, data=None):
  109. '''
  110. Make a minimal example configmap or secret for using in mocks
  111. '''
  112. assert kind in ('Secret', 'ConfigMap', 'Namespace', 'Node')
  113. if data is None:
  114. data = {}
  115. self_link = '/api/v1/namespaces/{namespace}/{kind}s/{name}'.format(
  116. namespace=namespace,
  117. kind=kind.lower(),
  118. name=name,
  119. )
  120. return_data = {
  121. 'kind': kind,
  122. 'data': data,
  123. 'api_version': 'v1',
  124. 'metadata': {
  125. 'name': name,
  126. 'labels': None,
  127. 'namespace': namespace,
  128. 'self_link': self_link,
  129. 'annotations': {
  130. 'kubernetes.io/change-cause': 'salt-call state.apply',
  131. },
  132. },
  133. }
  134. return return_data
  135. def test_configmap_present__fail(self):
  136. error = kubernetes.configmap_present(
  137. name='testme',
  138. data={1: 1},
  139. source='salt://beyond/oblivion.jinja',
  140. )
  141. self.assertDictEqual(
  142. {
  143. 'changes': {},
  144. 'result': False,
  145. 'name': 'testme',
  146. 'comment': "'source' cannot be used in combination with 'data'",
  147. },
  148. error,
  149. )
  150. def test_configmap_present__create_test_true(self):
  151. # Create a new configmap with test=True
  152. with self.mock_func('show_configmap', return_value=None, test=True):
  153. ret = kubernetes.configmap_present(
  154. name='example',
  155. data={'example.conf': '# empty config file'},
  156. )
  157. self.assertDictEqual(
  158. {
  159. 'comment': 'The configmap is going to be created',
  160. 'changes': {},
  161. 'name': 'example',
  162. 'result': None,
  163. },
  164. ret,
  165. )
  166. def test_configmap_present__create(self):
  167. # Create a new configmap
  168. with self.mock_func('show_configmap', return_value=None):
  169. cm = self.make_configmap(
  170. name='test',
  171. namespace='default',
  172. data={'foo': 'bar'},
  173. )
  174. with self.mock_func('create_configmap', return_value=cm):
  175. actual = kubernetes.configmap_present(
  176. name='test',
  177. data={'foo': 'bar'},
  178. )
  179. self.assertDictEqual(
  180. {
  181. 'comment': '',
  182. 'changes': {'data': {'foo': 'bar'}},
  183. 'name': 'test',
  184. 'result': True,
  185. },
  186. actual,
  187. )
  188. def test_configmap_present__create_no_data(self):
  189. # Create a new configmap with no 'data' attribute
  190. with self.mock_func('show_configmap', return_value=None):
  191. cm = self.make_configmap(
  192. name='test',
  193. namespace='default',
  194. )
  195. with self.mock_func('create_configmap', return_value=cm):
  196. actual = kubernetes.configmap_present(name='test')
  197. self.assertDictEqual(
  198. {
  199. 'comment': '',
  200. 'changes': {'data': {}},
  201. 'name': 'test',
  202. 'result': True,
  203. },
  204. actual,
  205. )
  206. def test_configmap_present__replace_test_true(self):
  207. cm = self.make_configmap(
  208. name='settings',
  209. namespace='saltstack',
  210. data={'foobar.conf': '# Example configuration'},
  211. )
  212. with self.mock_func('show_configmap', return_value=cm, test=True):
  213. ret = kubernetes.configmap_present(
  214. name='settings',
  215. namespace='saltstack',
  216. data={'foobar.conf': '# Example configuration'},
  217. )
  218. self.assertDictEqual(
  219. {
  220. 'comment': 'The configmap is going to be replaced',
  221. 'changes': {},
  222. 'name': 'settings',
  223. 'result': None,
  224. },
  225. ret,
  226. )
  227. def test_configmap_present__replace(self):
  228. cm = self.make_configmap(name='settings', data={'action': 'make=war'})
  229. # Replace an existing configmap
  230. with self.mock_func('show_configmap', return_value=cm):
  231. new_cm = cm.copy()
  232. new_cm.update({
  233. 'data': {'action': 'make=peace'},
  234. })
  235. with self.mock_func('replace_configmap', return_value=new_cm):
  236. actual = kubernetes.configmap_present(
  237. name='settings',
  238. data={'action': 'make=peace'},
  239. )
  240. self.assertDictEqual(
  241. {
  242. 'comment': 'The configmap is already present. Forcing recreation',
  243. 'changes': {
  244. 'data': {
  245. 'action': 'make=peace',
  246. },
  247. },
  248. 'name': 'settings',
  249. 'result': True,
  250. },
  251. actual,
  252. )
  253. def test_configmap_absent__noop_test_true(self):
  254. # Nothing to delete with test=True
  255. with self.mock_func('show_configmap', return_value=None, test=True):
  256. actual = kubernetes.configmap_absent(name='NOT_FOUND')
  257. self.assertDictEqual(
  258. {
  259. 'comment': 'The configmap does not exist',
  260. 'changes': {},
  261. 'name': 'NOT_FOUND',
  262. 'result': None,
  263. },
  264. actual,
  265. )
  266. def test_configmap_absent__test_true(self):
  267. # Configmap exists with test=True
  268. cm = self.make_configmap(name='deleteme', namespace='default')
  269. with self.mock_func('show_configmap', return_value=cm, test=True):
  270. actual = kubernetes.configmap_absent(name='deleteme')
  271. self.assertDictEqual(
  272. {
  273. 'comment': 'The configmap is going to be deleted',
  274. 'changes': {},
  275. 'name': 'deleteme',
  276. 'result': None,
  277. },
  278. actual,
  279. )
  280. def test_configmap_absent__noop(self):
  281. # Nothing to delete
  282. with self.mock_func('show_configmap', return_value=None):
  283. actual = kubernetes.configmap_absent(name='NOT_FOUND')
  284. self.assertDictEqual(
  285. {
  286. 'comment': 'The configmap does not exist',
  287. 'changes': {},
  288. 'name': 'NOT_FOUND',
  289. 'result': True,
  290. },
  291. actual,
  292. )
  293. def test_configmap_absent(self):
  294. # Configmap exists, delete it!
  295. cm = self.make_configmap(name='deleteme', namespace='default')
  296. with self.mock_func('show_configmap', return_value=cm):
  297. # The return from this module isn't used in the state
  298. with self.mock_func('delete_configmap', return_value={}):
  299. actual = kubernetes.configmap_absent(name='deleteme')
  300. self.assertDictEqual(
  301. {
  302. 'comment': 'ConfigMap deleted',
  303. 'changes': {
  304. 'kubernetes.configmap': {
  305. 'new': 'absent',
  306. 'old': 'present',
  307. },
  308. },
  309. 'name': 'deleteme',
  310. 'result': True,
  311. },
  312. actual,
  313. )
  314. def test_secret_present__fail(self):
  315. actual = kubernetes.secret_present(
  316. name='sekret',
  317. data={'password': 'monk3y'},
  318. source='salt://nope.jinja',
  319. )
  320. self.assertDictEqual(
  321. {
  322. 'changes': {},
  323. 'result': False,
  324. 'name': 'sekret',
  325. 'comment': "'source' cannot be used in combination with 'data'",
  326. },
  327. actual,
  328. )
  329. def test_secret_present__exists_test_true(self):
  330. secret = self.make_secret(name='sekret')
  331. new_secret = secret.copy()
  332. new_secret.update({
  333. 'data': {'password': 'uncle'},
  334. })
  335. # Secret exists already and needs replacing with test=True
  336. with self.mock_func('show_secret', return_value=secret):
  337. with self.mock_func('replace_secret', return_value=new_secret, test=True):
  338. actual = kubernetes.secret_present(
  339. name='sekret',
  340. data={'password': 'uncle'},
  341. )
  342. self.assertDictEqual(
  343. {
  344. 'changes': {},
  345. 'result': None,
  346. 'name': 'sekret',
  347. 'comment': 'The secret is going to be replaced',
  348. },
  349. actual,
  350. )
  351. def test_secret_present__exists(self):
  352. # Secret exists and gets replaced
  353. secret = self.make_secret(name='sekret', data={'password': 'booyah'})
  354. with self.mock_func('show_secret', return_value=secret):
  355. with self.mock_func('replace_secret', return_value=secret):
  356. actual = kubernetes.secret_present(
  357. name='sekret',
  358. data={'password': 'booyah'},
  359. )
  360. self.assertDictEqual(
  361. {
  362. 'changes': {'data': ['password']},
  363. 'result': True,
  364. 'name': 'sekret',
  365. 'comment': "The secret is already present. Forcing recreation",
  366. },
  367. actual,
  368. )
  369. def test_secret_present__create(self):
  370. # Secret exists and gets replaced
  371. secret = self.make_secret(name='sekret', data={'password': 'booyah'})
  372. with self.mock_func('show_secret', return_value=None):
  373. with self.mock_func('create_secret', return_value=secret):
  374. actual = kubernetes.secret_present(
  375. name='sekret',
  376. data={'password': 'booyah'},
  377. )
  378. self.assertDictEqual(
  379. {
  380. 'changes': {'data': ['password']},
  381. 'result': True,
  382. 'name': 'sekret',
  383. 'comment': '',
  384. },
  385. actual,
  386. )
  387. def test_secret_present__create_no_data(self):
  388. # Secret exists and gets replaced
  389. secret = self.make_secret(name='sekret')
  390. with self.mock_func('show_secret', return_value=None):
  391. with self.mock_func('create_secret', return_value=secret):
  392. actual = kubernetes.secret_present(name='sekret')
  393. self.assertDictEqual(
  394. {
  395. 'changes': {'data': []},
  396. 'result': True,
  397. 'name': 'sekret',
  398. 'comment': '',
  399. },
  400. actual,
  401. )
  402. def test_secret_present__create_test_true(self):
  403. # Secret exists and gets replaced with test=True
  404. secret = self.make_secret(name='sekret')
  405. with self.mock_func('show_secret', return_value=None):
  406. with self.mock_func('create_secret', return_value=secret, test=True):
  407. actual = kubernetes.secret_present(name='sekret')
  408. self.assertDictEqual(
  409. {
  410. 'changes': {},
  411. 'result': None,
  412. 'name': 'sekret',
  413. 'comment': 'The secret is going to be created',
  414. },
  415. actual,
  416. )
  417. def test_secret_absent__noop_test_true(self):
  418. with self.mock_func('show_secret', return_value=None, test=True):
  419. actual = kubernetes.secret_absent(name='sekret')
  420. self.assertDictEqual(
  421. {
  422. 'changes': {},
  423. 'result': None,
  424. 'name': 'sekret',
  425. 'comment': 'The secret does not exist',
  426. },
  427. actual,
  428. )
  429. def test_secret_absent__noop(self):
  430. with self.mock_func('show_secret', return_value=None):
  431. actual = kubernetes.secret_absent(name='passwords')
  432. self.assertDictEqual(
  433. {
  434. 'changes': {},
  435. 'result': True,
  436. 'name': 'passwords',
  437. 'comment': 'The secret does not exist',
  438. },
  439. actual,
  440. )
  441. def test_secret_absent__delete_test_true(self):
  442. secret = self.make_secret(name='credentials', data={'redis': 'letmein'})
  443. with self.mock_func('show_secret', return_value=secret):
  444. with self.mock_func('delete_secret', return_value=secret, test=True):
  445. actual = kubernetes.secret_absent(name='credentials')
  446. self.assertDictEqual(
  447. {
  448. 'changes': {},
  449. 'result': None,
  450. 'name': 'credentials',
  451. 'comment': 'The secret is going to be deleted',
  452. },
  453. actual,
  454. )
  455. def test_secret_absent__delete(self):
  456. secret = self.make_secret(name='foobar', data={'redis': 'letmein'})
  457. deleted = {
  458. 'status': None,
  459. 'kind': 'Secret',
  460. 'code': None,
  461. 'reason': None,
  462. 'details': None,
  463. 'message': None,
  464. 'api_version': 'v1',
  465. 'metadata': {
  466. 'self_link': '/api/v1/namespaces/default/secrets/foobar',
  467. 'resource_version': '30292',
  468. },
  469. }
  470. with self.mock_func('show_secret', return_value=secret):
  471. with self.mock_func('delete_secret', return_value=deleted):
  472. actual = kubernetes.secret_absent(name='foobar')
  473. self.assertDictEqual(
  474. {
  475. 'changes': {
  476. 'kubernetes.secret': {
  477. 'new': 'absent',
  478. 'old': 'present',
  479. },
  480. },
  481. 'result': True,
  482. 'name': 'foobar',
  483. 'comment': 'Secret deleted',
  484. },
  485. actual,
  486. )
  487. def test_node_label_present__add_test_true(self):
  488. labels = self.make_node_labels()
  489. with self.mock_func('node_labels', return_value=labels, test=True):
  490. actual = kubernetes.node_label_present(
  491. name='com.zoo-animal',
  492. node='minikube',
  493. value='monkey',
  494. )
  495. self.assertDictEqual(
  496. {
  497. 'changes': {},
  498. 'result': None,
  499. 'name': 'com.zoo-animal',
  500. 'comment': 'The label is going to be set',
  501. },
  502. actual,
  503. )
  504. def test_node_label_present__add(self):
  505. node_data = self.make_node()
  506. # Remove some of the defaults to make it simpler
  507. node_data['metadata']['labels'] = {
  508. 'beta.kubernetes.io/os': 'linux',
  509. }
  510. labels = node_data['metadata']['labels']
  511. with self.mock_func('node_labels', return_value=labels):
  512. with self.mock_func('node_add_label', return_value=node_data):
  513. actual = kubernetes.node_label_present(
  514. name='failure-domain.beta.kubernetes.io/zone',
  515. node='minikube',
  516. value='us-central1-a',
  517. )
  518. self.assertDictEqual(
  519. {
  520. 'comment': '',
  521. 'changes': {
  522. 'minikube.failure-domain.beta.kubernetes.io/zone': {
  523. 'new': {
  524. 'failure-domain.beta.kubernetes.io/zone': 'us-central1-a',
  525. 'beta.kubernetes.io/os': 'linux'
  526. },
  527. 'old': {
  528. 'beta.kubernetes.io/os': 'linux',
  529. },
  530. },
  531. },
  532. 'name': 'failure-domain.beta.kubernetes.io/zone',
  533. 'result': True,
  534. },
  535. actual,
  536. )
  537. def test_node_label_present__already_set(self):
  538. node_data = self.make_node()
  539. labels = node_data['metadata']['labels']
  540. with self.mock_func('node_labels', return_value=labels):
  541. with self.mock_func('node_add_label', return_value=node_data):
  542. actual = kubernetes.node_label_present(
  543. name='failure-domain.beta.kubernetes.io/region',
  544. node='minikube',
  545. value='us-west-1',
  546. )
  547. self.assertDictEqual(
  548. {
  549. 'changes': {},
  550. 'result': True,
  551. 'name': 'failure-domain.beta.kubernetes.io/region',
  552. 'comment': 'The label is already set and has the specified value',
  553. },
  554. actual,
  555. )
  556. def test_node_label_present__update_test_true(self):
  557. node_data = self.make_node()
  558. labels = node_data['metadata']['labels']
  559. with self.mock_func('node_labels', return_value=labels):
  560. with self.mock_func('node_add_label', return_value=node_data, test=True):
  561. actual = kubernetes.node_label_present(
  562. name='failure-domain.beta.kubernetes.io/region',
  563. node='minikube',
  564. value='us-east-1',
  565. )
  566. self.assertDictEqual(
  567. {
  568. 'changes': {},
  569. 'result': None,
  570. 'name': 'failure-domain.beta.kubernetes.io/region',
  571. 'comment': 'The label is going to be updated',
  572. },
  573. actual,
  574. )
  575. def test_node_label_present__update(self):
  576. node_data = self.make_node()
  577. # Remove some of the defaults to make it simpler
  578. node_data['metadata']['labels'] = {
  579. 'failure-domain.beta.kubernetes.io/region': 'us-west-1',
  580. }
  581. labels = node_data['metadata']['labels']
  582. with self.mock_func('node_labels', return_value=labels):
  583. with self.mock_func('node_add_label', return_value=node_data):
  584. actual = kubernetes.node_label_present(
  585. name='failure-domain.beta.kubernetes.io/region',
  586. node='minikube',
  587. value='us-east-1',
  588. )
  589. self.assertDictEqual(
  590. {
  591. 'changes': {
  592. 'minikube.failure-domain.beta.kubernetes.io/region': {
  593. 'new': {'failure-domain.beta.kubernetes.io/region': 'us-east-1'},
  594. 'old': {'failure-domain.beta.kubernetes.io/region': 'us-west-1'},
  595. }
  596. },
  597. 'result': True,
  598. 'name': 'failure-domain.beta.kubernetes.io/region',
  599. 'comment': 'The label is already set, changing the value',
  600. },
  601. actual,
  602. )
  603. def test_node_label_absent__noop_test_true(self):
  604. labels = self.make_node_labels()
  605. with self.mock_func('node_labels', return_value=labels, test=True):
  606. actual = kubernetes.node_label_absent(
  607. name='non-existent-label',
  608. node='minikube',
  609. )
  610. self.assertDictEqual(
  611. {
  612. 'changes': {},
  613. 'result': None,
  614. 'name': 'non-existent-label',
  615. 'comment': 'The label does not exist',
  616. },
  617. actual
  618. )
  619. def test_node_label_absent__noop(self):
  620. labels = self.make_node_labels()
  621. with self.mock_func('node_labels', return_value=labels):
  622. actual = kubernetes.node_label_absent(
  623. name='non-existent-label',
  624. node='minikube',
  625. )
  626. self.assertDictEqual(
  627. {
  628. 'changes': {},
  629. 'result': True,
  630. 'name': 'non-existent-label',
  631. 'comment': 'The label does not exist',
  632. },
  633. actual
  634. )
  635. def test_node_label_absent__delete_test_true(self):
  636. labels = self.make_node_labels()
  637. with self.mock_func('node_labels', return_value=labels, test=True):
  638. actual = kubernetes.node_label_absent(
  639. name='failure-domain.beta.kubernetes.io/region',
  640. node='minikube',
  641. )
  642. self.assertDictEqual(
  643. {
  644. 'changes': {},
  645. 'result': None,
  646. 'name': 'failure-domain.beta.kubernetes.io/region',
  647. 'comment': 'The label is going to be deleted',
  648. },
  649. actual
  650. )
  651. def test_node_label_absent__delete(self):
  652. node_data = self.make_node()
  653. labels = node_data['metadata']['labels'].copy()
  654. node_data['metadata']['labels'].pop('failure-domain.beta.kubernetes.io/region')
  655. with self.mock_func('node_labels', return_value=labels):
  656. with self.mock_func('node_remove_label', return_value=node_data):
  657. actual = kubernetes.node_label_absent(
  658. name='failure-domain.beta.kubernetes.io/region',
  659. node='minikube',
  660. )
  661. self.assertDictEqual(
  662. {
  663. 'result': True,
  664. 'changes': {
  665. 'kubernetes.node_label': {
  666. 'new': 'absent',
  667. 'old': 'present',
  668. }
  669. },
  670. 'comment': 'Label removed from node',
  671. 'name': 'failure-domain.beta.kubernetes.io/region',
  672. },
  673. actual
  674. )
  675. def test_namespace_present__create_test_true(self):
  676. with self.mock_func('show_namespace', return_value=None, test=True):
  677. actual = kubernetes.namespace_present(name='saltstack')
  678. self.assertDictEqual(
  679. {
  680. 'changes': {},
  681. 'result': None,
  682. 'name': 'saltstack',
  683. 'comment': 'The namespace is going to be created',
  684. },
  685. actual
  686. )
  687. def test_namespace_present__create(self):
  688. namespace_data = self.make_namespace(name='saltstack')
  689. with self.mock_func('show_namespace', return_value=None):
  690. with self.mock_func('create_namespace', return_value=namespace_data):
  691. actual = kubernetes.namespace_present(name='saltstack')
  692. self.assertDictEqual(
  693. {
  694. 'changes': {
  695. 'namespace': {
  696. 'new': namespace_data,
  697. 'old': {},
  698. },
  699. },
  700. 'result': True,
  701. 'name': 'saltstack',
  702. 'comment': '',
  703. },
  704. actual
  705. )
  706. def test_namespace_present__noop_test_true(self):
  707. namespace_data = self.make_namespace(name='saltstack')
  708. with self.mock_func('show_namespace', return_value=namespace_data, test=True):
  709. actual = kubernetes.namespace_present(name='saltstack')
  710. self.assertDictEqual(
  711. {
  712. 'changes': {},
  713. 'result': None,
  714. 'name': 'saltstack',
  715. 'comment': 'The namespace already exists',
  716. },
  717. actual
  718. )
  719. def test_namespace_present__noop(self):
  720. namespace_data = self.make_namespace(name='saltstack')
  721. with self.mock_func('show_namespace', return_value=namespace_data):
  722. actual = kubernetes.namespace_present(name='saltstack')
  723. self.assertDictEqual(
  724. {
  725. 'changes': {},
  726. 'result': True,
  727. 'name': 'saltstack',
  728. 'comment': 'The namespace already exists',
  729. },
  730. actual
  731. )
  732. def test_namespace_absent__noop_test_true(self):
  733. with self.mock_func('show_namespace', return_value=None, test=True):
  734. actual = kubernetes.namespace_absent(name='salt')
  735. self.assertDictEqual(
  736. {
  737. 'changes': {},
  738. 'result': None,
  739. 'name': 'salt',
  740. 'comment': 'The namespace does not exist',
  741. },
  742. actual
  743. )
  744. def test_namespace_absent__noop(self):
  745. with self.mock_func('show_namespace', return_value=None):
  746. actual = kubernetes.namespace_absent(name='salt')
  747. self.assertDictEqual(
  748. {
  749. 'changes': {},
  750. 'result': True,
  751. 'name': 'salt',
  752. 'comment': 'The namespace does not exist',
  753. },
  754. actual
  755. )
  756. def test_namespace_absent__delete_test_true(self):
  757. namespace_data = self.make_namespace(name='salt')
  758. with self.mock_func('show_namespace', return_value=namespace_data, test=True):
  759. actual = kubernetes.namespace_absent(name='salt')
  760. self.assertDictEqual(
  761. {
  762. 'changes': {},
  763. 'result': None,
  764. 'name': 'salt',
  765. 'comment': 'The namespace is going to be deleted',
  766. },
  767. actual
  768. )
  769. def test_namespace_absent__delete_code_200(self):
  770. namespace_data = self.make_namespace(name='salt')
  771. deleted = namespace_data.copy()
  772. deleted['code'] = 200
  773. deleted.update({
  774. 'code': 200,
  775. 'message': None,
  776. })
  777. with self.mock_func('show_namespace', return_value=namespace_data):
  778. with self.mock_func('delete_namespace', return_value=deleted):
  779. actual = kubernetes.namespace_absent(name='salt')
  780. self.assertDictEqual(
  781. {
  782. 'changes': {
  783. 'kubernetes.namespace': {
  784. 'new': 'absent',
  785. 'old': 'present',
  786. }
  787. },
  788. 'result': True,
  789. 'name': 'salt',
  790. 'comment': 'Terminating',
  791. },
  792. actual
  793. )
  794. def test_namespace_absent__delete_status_terminating(self):
  795. namespace_data = self.make_namespace(name='salt')
  796. deleted = namespace_data.copy()
  797. deleted.update({
  798. 'code': None,
  799. 'status': 'Terminating namespace',
  800. 'message': 'Terminating this shizzzle yo',
  801. })
  802. with self.mock_func('show_namespace', return_value=namespace_data):
  803. with self.mock_func('delete_namespace', return_value=deleted):
  804. actual = kubernetes.namespace_absent(name='salt')
  805. self.assertDictEqual(
  806. {
  807. 'changes': {
  808. 'kubernetes.namespace': {
  809. 'new': 'absent',
  810. 'old': 'present',
  811. }
  812. },
  813. 'result': True,
  814. 'name': 'salt',
  815. 'comment': 'Terminating this shizzzle yo',
  816. },
  817. actual
  818. )
  819. def test_namespace_absent__delete_status_phase_terminating(self):
  820. # This is what kubernetes 1.8.0 looks like when deleting namespaces
  821. namespace_data = self.make_namespace(name='salt')
  822. deleted = namespace_data.copy()
  823. deleted.update({
  824. 'code': None,
  825. 'message': None,
  826. 'status': {'phase': 'Terminating'},
  827. })
  828. with self.mock_func('show_namespace', return_value=namespace_data):
  829. with self.mock_func('delete_namespace', return_value=deleted):
  830. actual = kubernetes.namespace_absent(name='salt')
  831. self.assertDictEqual(
  832. {
  833. 'changes': {
  834. 'kubernetes.namespace': {
  835. 'new': 'absent',
  836. 'old': 'present',
  837. }
  838. },
  839. 'result': True,
  840. 'name': 'salt',
  841. 'comment': 'Terminating',
  842. },
  843. actual
  844. )
  845. def test_namespace_absent__delete_error(self):
  846. namespace_data = self.make_namespace(name='salt')
  847. deleted = namespace_data.copy()
  848. deleted.update({
  849. 'code': 418,
  850. 'message': 'I\' a teapot!',
  851. 'status': None,
  852. })
  853. with self.mock_func('show_namespace', return_value=namespace_data):
  854. with self.mock_func('delete_namespace', return_value=deleted):
  855. actual = kubernetes.namespace_absent(name='salt')
  856. self.assertDictEqual(
  857. {
  858. 'changes': {},
  859. 'result': False,
  860. 'name': 'salt',
  861. 'comment': 'Something went wrong, response: {0}'.format(
  862. deleted,
  863. ),
  864. },
  865. actual
  866. )