test_grains.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  1. # -*- coding: utf-8 -*-
  2. # Import python libs
  3. from __future__ import absolute_import, print_function, unicode_literals
  4. import copy
  5. import os
  6. import salt.modules.grains as grainsmod
  7. import salt.utils.dictupdate as dictupdate
  8. # Import Salt libs
  9. from salt.exceptions import SaltException
  10. # Import 3rd-party libs
  11. from salt.utils.odict import OrderedDict
  12. from tests.support.mixins import LoaderModuleMockMixin
  13. from tests.support.mock import MagicMock, patch
  14. # Import Salt Testing libs
  15. from tests.support.runtests import RUNTIME_VARS
  16. from tests.support.unit import TestCase
  17. class GrainsModuleTestCase(TestCase, LoaderModuleMockMixin):
  18. def setup_loader_modules(self):
  19. conf_file = os.path.join(RUNTIME_VARS.TMP, "__salt_test_grains")
  20. cachedir = os.path.join(RUNTIME_VARS.TMP, "__salt_test_grains_cache_dir")
  21. if not os.path.isdir(cachedir):
  22. os.makedirs(cachedir)
  23. return {
  24. grainsmod: {
  25. "__opts__": {"conf_file": conf_file, "cachedir": cachedir},
  26. "__salt__": {"saltutil.refresh_grains": MagicMock()},
  27. }
  28. }
  29. def test_filter_by(self):
  30. with patch.dict(
  31. grainsmod.__grains__,
  32. {"os_family": "MockedOS", "1": "1", "2": "2", "roles": ["A", "B"]},
  33. ):
  34. dict1 = {"A": "B", "C": {"D": {"E": "F", "G": "H"}}}
  35. dict2 = {
  36. "default": {"A": "B", "C": {"D": "E"}},
  37. "1": {"A": "X"},
  38. "2": {"C": {"D": "H"}},
  39. "MockedOS": {"A": "Z"},
  40. }
  41. mdict1 = {"D": {"E": "I"}, "J": "K"}
  42. mdict2 = {"A": "Z"}
  43. mdict3 = {"C": {"D": "J"}}
  44. # test None result with non existent grain and no default
  45. res = grainsmod.filter_by(dict1, grain="xxx")
  46. self.assertIs(res, None)
  47. # test None result with os_family grain and no matching result
  48. res = grainsmod.filter_by(dict1)
  49. self.assertIs(res, None)
  50. # test with non existent grain, and a given default key
  51. res = grainsmod.filter_by(dict1, grain="xxx", default="C")
  52. self.assertEqual(res, {"D": {"E": "F", "G": "H"}})
  53. # add a merge dictionary, F disappears
  54. res = grainsmod.filter_by(dict1, grain="xxx", merge=mdict1, default="C")
  55. self.assertEqual(res, {"D": {"E": "I", "G": "H"}, "J": "K"})
  56. # dict1 was altered, reestablish
  57. dict1 = {"A": "B", "C": {"D": {"E": "F", "G": "H"}}}
  58. # default is not present in dict1, check we only have merge in result
  59. res = grainsmod.filter_by(dict1, grain="xxx", merge=mdict1, default="Z")
  60. self.assertEqual(res, mdict1)
  61. # default is not present in dict1, and no merge, should get None
  62. res = grainsmod.filter_by(dict1, grain="xxx", default="Z")
  63. self.assertIs(res, None)
  64. # test giving a list as merge argument raise exception
  65. self.assertRaises(
  66. SaltException, grainsmod.filter_by, dict1, "xxx", ["foo"], "C"
  67. )
  68. # Now, re-test with an existing grain (os_family), but with no match.
  69. res = grainsmod.filter_by(dict1)
  70. self.assertIs(res, None)
  71. res = grainsmod.filter_by(dict1, default="C")
  72. self.assertEqual(res, {"D": {"E": "F", "G": "H"}})
  73. res = grainsmod.filter_by(dict1, merge=mdict1, default="C")
  74. self.assertEqual(res, {"D": {"E": "I", "G": "H"}, "J": "K"})
  75. # dict1 was altered, reestablish
  76. dict1 = {"A": "B", "C": {"D": {"E": "F", "G": "H"}}}
  77. res = grainsmod.filter_by(dict1, merge=mdict1, default="Z")
  78. self.assertEqual(res, mdict1)
  79. res = grainsmod.filter_by(dict1, default="Z")
  80. self.assertIs(res, None)
  81. # this one is in fact a traceback in updatedict, merging a string with a dictionary
  82. self.assertRaises(
  83. TypeError, grainsmod.filter_by, dict1, merge=mdict1, default="A"
  84. )
  85. # Now, re-test with a matching grain.
  86. dict1 = {"A": "B", "MockedOS": {"D": {"E": "F", "G": "H"}}}
  87. res = grainsmod.filter_by(dict1)
  88. self.assertEqual(res, {"D": {"E": "F", "G": "H"}})
  89. res = grainsmod.filter_by(dict1, default="A")
  90. self.assertEqual(res, {"D": {"E": "F", "G": "H"}})
  91. res = grainsmod.filter_by(dict1, merge=mdict1, default="A")
  92. self.assertEqual(res, {"D": {"E": "I", "G": "H"}, "J": "K"})
  93. # dict1 was altered, reestablish
  94. dict1 = {"A": "B", "MockedOS": {"D": {"E": "F", "G": "H"}}}
  95. res = grainsmod.filter_by(dict1, merge=mdict1, default="Z")
  96. self.assertEqual(res, {"D": {"E": "I", "G": "H"}, "J": "K"})
  97. # dict1 was altered, reestablish
  98. dict1 = {"A": "B", "MockedOS": {"D": {"E": "F", "G": "H"}}}
  99. res = grainsmod.filter_by(dict1, default="Z")
  100. self.assertEqual(res, {"D": {"E": "F", "G": "H"}})
  101. # Test when grain value is a list
  102. dict1 = {"A": "B", "C": {"D": {"E": "F", "G": "H"}}}
  103. res = grainsmod.filter_by(dict1, grain="roles", default="C")
  104. self.assertEqual(res, "B")
  105. # Test default when grain value is a list
  106. dict1 = {"Z": "B", "C": {"D": {"E": "F", "G": "H"}}}
  107. res = grainsmod.filter_by(dict1, grain="roles", default="C")
  108. self.assertEqual(res, {"D": {"E": "F", "G": "H"}})
  109. # Test with wildcard pattern in the lookup_dict keys
  110. dict1 = {"*OS": "B", "C": {"D": {"E": "F", "G": "H"}}}
  111. res = grainsmod.filter_by(dict1)
  112. self.assertEqual(res, "B")
  113. # Test with non-strings in lookup_dict keys
  114. # Issue #38094
  115. dict1 = {1: 2, 3: {4: 5}, "*OS": "B"}
  116. res = grainsmod.filter_by(dict1)
  117. self.assertEqual(res, "B")
  118. # Test with sequence pattern with roles
  119. dict1 = {"Z": "B", "[BC]": {"D": {"E": "F", "G": "H"}}}
  120. res = grainsmod.filter_by(dict1, grain="roles", default="Z")
  121. self.assertEqual(res, {"D": {"E": "F", "G": "H"}})
  122. # Base tests
  123. # NOTE: these may fail to detect errors if dictupdate.update() is broken
  124. # but then the unit test for dictupdate.update() should fail and expose
  125. # that. The purpose of these tests is it validate the logic of how
  126. # in filter_by() processes its arguments.
  127. # Test with just the base
  128. res = grainsmod.filter_by(dict2, grain="xxx", default="xxx", base="default")
  129. self.assertEqual(res, dict2["default"])
  130. # Test the base with the OS grain look-up
  131. res = grainsmod.filter_by(dict2, default="xxx", base="default")
  132. self.assertEqual(
  133. res,
  134. dictupdate.update(copy.deepcopy(dict2["default"]), dict2["MockedOS"]),
  135. )
  136. # Test the base with default
  137. res = grainsmod.filter_by(dict2, grain="xxx", base="default")
  138. self.assertEqual(res, dict2["default"])
  139. res = grainsmod.filter_by(dict2, grain="1", base="default")
  140. self.assertEqual(
  141. res, dictupdate.update(copy.deepcopy(dict2["default"]), dict2["1"])
  142. )
  143. res = grainsmod.filter_by(dict2, base="default", merge=mdict2)
  144. self.assertEqual(
  145. res,
  146. dictupdate.update(
  147. dictupdate.update(
  148. copy.deepcopy(dict2["default"]), dict2["MockedOS"]
  149. ),
  150. mdict2,
  151. ),
  152. )
  153. res = grainsmod.filter_by(dict2, base="default", merge=mdict3)
  154. self.assertEqual(
  155. res,
  156. dictupdate.update(
  157. dictupdate.update(
  158. copy.deepcopy(dict2["default"]), dict2["MockedOS"]
  159. ),
  160. mdict3,
  161. ),
  162. )
  163. def test_append_not_a_list(self):
  164. # Failing append to an existing string, without convert
  165. with patch.dict(grainsmod.__grains__, {"b": "bval"}):
  166. res = grainsmod.append("b", "d")
  167. self.assertEqual(res, "The key b is not a valid list")
  168. self.assertEqual(grainsmod.__grains__, {"b": "bval"})
  169. # Failing append to an existing dict
  170. with patch.dict(grainsmod.__grains__, {"b": {"b1": "bval1"}}):
  171. res = grainsmod.append("b", "d")
  172. self.assertEqual(res, "The key b is not a valid list")
  173. self.assertEqual(grainsmod.__grains__, {"b": {"b1": "bval1"}})
  174. def test_append_already_in_list(self):
  175. # Append an existing value
  176. with patch.dict(grainsmod.__grains__, {"a_list": ["a", "b", "c"], "b": "bval"}):
  177. res = grainsmod.append("a_list", "b")
  178. self.assertEqual(res, "The val b was already in the list a_list")
  179. self.assertEqual(
  180. grainsmod.__grains__, {"a_list": ["a", "b", "c"], "b": "bval"}
  181. )
  182. def test_append_ok(self):
  183. # Append to an existing list
  184. with patch.dict(grainsmod.__grains__, {"a_list": ["a", "b", "c"], "b": "bval"}):
  185. res = grainsmod.append("a_list", "d")
  186. self.assertEqual(res, {"a_list": ["a", "b", "c", "d"]})
  187. self.assertEqual(
  188. grainsmod.__grains__, {"a_list": ["a", "b", "c", "d"], "b": "bval"}
  189. )
  190. # Append to an non existing list
  191. with patch.dict(grainsmod.__grains__, {"b": "bval"}):
  192. res = grainsmod.append("a_list", "d")
  193. self.assertEqual(res, {"a_list": ["d"]})
  194. self.assertEqual(grainsmod.__grains__, {"a_list": ["d"], "b": "bval"})
  195. # Append to an existing string, with convert
  196. with patch.dict(grainsmod.__grains__, {"b": "bval"}):
  197. res = grainsmod.append("b", "d", convert=True)
  198. self.assertEqual(res, {"b": ["bval", "d"]})
  199. self.assertEqual(grainsmod.__grains__, {"b": ["bval", "d"]})
  200. # Append to an existing dict, with convert
  201. with patch.dict(grainsmod.__grains__, {"b": {"b1": "bval1"}}):
  202. res = grainsmod.append("b", "d", convert=True)
  203. self.assertEqual(res, {"b": [{"b1": "bval1"}, "d"]})
  204. self.assertEqual(grainsmod.__grains__, {"b": [{"b1": "bval1"}, "d"]})
  205. def test_append_nested_not_a_list(self):
  206. # Failing append to an existing string, without convert
  207. with patch.dict(grainsmod.__grains__, {"a": {"b": "bval"}}):
  208. res = grainsmod.append("a:b", "d")
  209. self.assertEqual(res, "The key a:b is not a valid list")
  210. self.assertEqual(grainsmod.__grains__, {"a": {"b": "bval"}})
  211. # Failing append to an existing dict
  212. with patch.dict(grainsmod.__grains__, {"a": {"b": {"b1": "bval1"}}}):
  213. res = grainsmod.append("a:b", "d")
  214. self.assertEqual(res, "The key a:b is not a valid list")
  215. self.assertEqual(grainsmod.__grains__, {"a": {"b": {"b1": "bval1"}}})
  216. def test_append_nested_already_in_list(self):
  217. # Append an existing value
  218. with patch.dict(
  219. grainsmod.__grains__, {"a": {"a_list": ["a", "b", "c"], "b": "bval"}}
  220. ):
  221. res = grainsmod.append("a:a_list", "b")
  222. self.assertEqual(res, "The val b was already in the list a:a_list")
  223. self.assertEqual(
  224. grainsmod.__grains__, {"a": {"a_list": ["a", "b", "c"], "b": "bval"}}
  225. )
  226. def test_append_nested_ok(self):
  227. # Append to an existing list
  228. with patch.dict(
  229. grainsmod.__grains__, {"a": {"a_list": ["a", "b", "c"], "b": "bval"}}
  230. ):
  231. res = grainsmod.append("a:a_list", "d")
  232. self.assertEqual(res, {"a": {"a_list": ["a", "b", "c", "d"], "b": "bval"}})
  233. self.assertEqual(
  234. grainsmod.__grains__,
  235. {"a": {"a_list": ["a", "b", "c", "d"], "b": "bval"}},
  236. )
  237. # Append to an non existing list
  238. with patch.dict(grainsmod.__grains__, {"a": {"b": "bval"}}):
  239. res = grainsmod.append("a:a_list", "d")
  240. self.assertEqual(res, {"a": {"a_list": ["d"], "b": "bval"}})
  241. self.assertEqual(
  242. grainsmod.__grains__, {"a": {"a_list": ["d"], "b": "bval"}}
  243. )
  244. # Append to an existing string, with convert
  245. with patch.dict(grainsmod.__grains__, {"a": {"b": "bval"}}):
  246. res = grainsmod.append("a:b", "d", convert=True)
  247. self.assertEqual(res, {"a": {"b": ["bval", "d"]}})
  248. self.assertEqual(grainsmod.__grains__, {"a": {"b": ["bval", "d"]}})
  249. # Append to an existing dict, with convert
  250. with patch.dict(grainsmod.__grains__, {"a": {"b": {"b1": "bval1"}}}):
  251. res = grainsmod.append("a:b", "d", convert=True)
  252. self.assertEqual(res, {"a": {"b": [{"b1": "bval1"}, "d"]}})
  253. self.assertEqual(grainsmod.__grains__, {"a": {"b": [{"b1": "bval1"}, "d"]}})
  254. def test_append_to_an_element_of_a_list(self):
  255. # Append to an element in a list
  256. # It currently fails silently
  257. with patch.dict(grainsmod.__grains__, {"a": ["b", "c"]}):
  258. res = grainsmod.append("a:b", "d")
  259. self.assertEqual(res, {"a": ["b", "c"]})
  260. self.assertEqual(grainsmod.__grains__, {"a": ["b", "c"]})
  261. def test_set_value_already_set(self):
  262. # Set a grain to the same simple value
  263. with patch.dict(grainsmod.__grains__, {"a": 12, "c": 8}):
  264. res = grainsmod.set("a", 12)
  265. self.assertTrue(res["result"])
  266. self.assertEqual(res["comment"], "Grain is already set")
  267. self.assertEqual(grainsmod.__grains__, {"a": 12, "c": 8})
  268. # Set a grain to the same complex value
  269. with patch.dict(grainsmod.__grains__, {"a": ["item", 12], "c": 8}):
  270. res = grainsmod.set("a", ["item", 12])
  271. self.assertTrue(res["result"])
  272. self.assertEqual(res["comment"], "Grain is already set")
  273. self.assertEqual(grainsmod.__grains__, {"a": ["item", 12], "c": 8})
  274. # Set a key to the same simple value in a nested grain
  275. with patch.dict(
  276. grainsmod.__grains__, {"a": "aval", "b": {"nested": "val"}, "c": 8}
  277. ):
  278. res = grainsmod.set("b,nested", "val", delimiter=",")
  279. self.assertTrue(res["result"])
  280. self.assertEqual(res["comment"], "Grain is already set")
  281. self.assertEqual(
  282. grainsmod.__grains__, {"a": "aval", "b": {"nested": "val"}, "c": 8}
  283. )
  284. def test_set_fail_replacing_existing_complex_key(self):
  285. # Fails to set a complex value without 'force'
  286. with patch.dict(grainsmod.__grains__, {"a": "aval", "c": 8}):
  287. res = grainsmod.set("a", ["item", 12])
  288. self.assertFalse(res["result"])
  289. self.assertEqual(
  290. res["comment"],
  291. "The key 'a' exists and the given value is a "
  292. "dict or a list. Use 'force=True' to overwrite.",
  293. )
  294. self.assertEqual(grainsmod.__grains__, {"a": "aval", "c": 8})
  295. # Fails to overwrite a complex value without 'force'
  296. with patch.dict(grainsmod.__grains__, {"a": ["item", 12], "c": 8}):
  297. res = grainsmod.set("a", ["item", 14])
  298. self.assertFalse(res["result"])
  299. self.assertEqual(
  300. res["comment"],
  301. "The key 'a' exists but is a dict or a list. "
  302. + "Use 'force=True' to overwrite.",
  303. )
  304. self.assertEqual(grainsmod.__grains__, {"a": ["item", 12], "c": 8})
  305. # Fails to overwrite a complex value without 'force' in a nested grain
  306. with patch.dict(
  307. grainsmod.__grains__, {"a": "aval", "b": ["l1", {"l2": ["val1"]}], "c": 8}
  308. ):
  309. res = grainsmod.set("b,l2", "val2", delimiter=",")
  310. self.assertFalse(res["result"])
  311. self.assertEqual(
  312. res["comment"],
  313. "The key 'b,l2' exists but is a dict or a "
  314. + "list. Use 'force=True' to overwrite.",
  315. )
  316. self.assertEqual(
  317. grainsmod.__grains__,
  318. {"a": "aval", "b": ["l1", {"l2": ["val1"]}], "c": 8},
  319. )
  320. def test_set_nested_fails_replace_simple_value(self):
  321. # Fails to replace a simple value with a new dictionary consisting
  322. # of the specified key and value
  323. with patch.dict(grainsmod.__grains__, {"a": "aval", "b": "l1", "c": 8}):
  324. res = grainsmod.set("b,l3", "val3", delimiter=",")
  325. self.assertFalse(res["result"])
  326. self.assertEqual(
  327. res["comment"],
  328. "The key 'b' value is 'l1', which is "
  329. + "different from the provided key 'l3'. "
  330. + "Use 'force=True' to overwrite.",
  331. )
  332. self.assertEqual(grainsmod.__grains__, {"a": "aval", "b": "l1", "c": 8})
  333. def test_set_simple_value(self):
  334. with patch.dict(grainsmod.__grains__, {"a": ["b", "c"], "c": 8}):
  335. res = grainsmod.set("b", "bval")
  336. self.assertTrue(res["result"])
  337. self.assertEqual(res["changes"], {"b": "bval"})
  338. self.assertEqual(
  339. grainsmod.__grains__, {"a": ["b", "c"], "b": "bval", "c": 8}
  340. )
  341. def test_set_replace_value(self):
  342. with patch.dict(grainsmod.__grains__, {"a": "aval", "c": 8}):
  343. res = grainsmod.set("a", 12)
  344. self.assertTrue(res["result"])
  345. self.assertEqual(res["changes"], {"a": 12})
  346. self.assertEqual(grainsmod.__grains__, {"a": 12, "c": 8})
  347. def test_set_None_ok(self):
  348. with patch.dict(grainsmod.__grains__, {"a": "aval", "c": 8}):
  349. res = grainsmod.set("b", None)
  350. self.assertTrue(res["result"])
  351. self.assertEqual(res["changes"], {"b": None})
  352. self.assertEqual(grainsmod.__grains__, {"a": "aval", "b": None, "c": 8})
  353. def test_set_None_ok_destructive(self):
  354. with patch.dict(grainsmod.__grains__, {"a": "aval", "c": 8}):
  355. res = grainsmod.set("b", None, destructive=True)
  356. self.assertTrue(res["result"])
  357. self.assertEqual(res["changes"], {"b": None})
  358. self.assertEqual(grainsmod.__grains__, {"a": "aval", "c": 8})
  359. def test_set_None_replace_ok(self):
  360. with patch.dict(grainsmod.__grains__, {"a": "aval", "c": 8}):
  361. res = grainsmod.set("a", None)
  362. self.assertTrue(res["result"])
  363. self.assertEqual(res["changes"], {"a": None})
  364. self.assertEqual(grainsmod.__grains__, {"a": None, "c": 8})
  365. def test_set_None_force_destructive(self):
  366. with patch.dict(grainsmod.__grains__, {"a": "aval", "c": 8}):
  367. res = grainsmod.set("a", None, force=True, destructive=True)
  368. self.assertTrue(res["result"])
  369. self.assertEqual(res["changes"], {"a": None})
  370. self.assertEqual(grainsmod.__grains__, {"c": 8})
  371. def test_set_replace_value_was_complex_force(self):
  372. with patch.dict(grainsmod.__grains__, {"a": ["item", 12], "c": 8}):
  373. res = grainsmod.set("a", "aval", force=True)
  374. self.assertTrue(res["result"])
  375. self.assertEqual(res["changes"], {"a": "aval"})
  376. self.assertEqual(grainsmod.__grains__, {"a": "aval", "c": 8})
  377. def test_set_complex_value_force(self):
  378. with patch.dict(grainsmod.__grains__, {"a": "aval", "c": 8}):
  379. res = grainsmod.set("a", ["item", 12], force=True)
  380. self.assertTrue(res["result"])
  381. self.assertEqual(res["changes"], {"a": ["item", 12]})
  382. self.assertEqual(grainsmod.__grains__, {"a": ["item", 12], "c": 8})
  383. def test_set_nested_create(self):
  384. with patch.dict(grainsmod.__grains__, {"a": "aval", "c": 8}):
  385. res = grainsmod.set("b,nested", "val", delimiter=",")
  386. self.assertTrue(res["result"])
  387. self.assertEqual(res["changes"], {"b": {"nested": "val"}})
  388. self.assertEqual(
  389. grainsmod.__grains__, {"a": "aval", "b": {"nested": "val"}, "c": 8}
  390. )
  391. def test_set_nested_update_dict(self):
  392. with patch.dict(
  393. grainsmod.__grains__, {"a": "aval", "b": {"nested": "val"}, "c": 8}
  394. ):
  395. res = grainsmod.set("b,nested", "val2", delimiter=",")
  396. self.assertTrue(res["result"])
  397. self.assertEqual(res["changes"], {"b": {"nested": "val2"}})
  398. self.assertEqual(
  399. grainsmod.__grains__, {"a": "aval", "b": {"nested": "val2"}, "c": 8}
  400. )
  401. def test_set_nested_update_dict_remove_key(self):
  402. with patch.dict(
  403. grainsmod.__grains__, {"a": "aval", "b": {"nested": "val"}, "c": 8}
  404. ):
  405. res = grainsmod.set("b,nested", None, delimiter=",", destructive=True)
  406. self.assertTrue(res["result"])
  407. self.assertEqual(res["changes"], {"b": {}})
  408. self.assertEqual(grainsmod.__grains__, {"a": "aval", "b": {}, "c": 8})
  409. def test_set_nested_update_dict_new_key(self):
  410. with patch.dict(
  411. grainsmod.__grains__, {"a": "aval", "b": {"nested": "val"}, "c": 8}
  412. ):
  413. res = grainsmod.set("b,b2", "val2", delimiter=",")
  414. self.assertTrue(res["result"])
  415. self.assertEqual(res["changes"], {"b": {"b2": "val2", "nested": "val"}})
  416. self.assertEqual(
  417. grainsmod.__grains__,
  418. {"a": "aval", "b": {"b2": "val2", "nested": "val"}, "c": 8},
  419. )
  420. def test_set_nested_list_replace_key(self):
  421. with patch.dict(
  422. grainsmod.__grains__, {"a": "aval", "b": ["l1", "l2", "l3"], "c": 8}
  423. ):
  424. res = grainsmod.set("b,l2", "val2", delimiter=",")
  425. self.assertTrue(res["result"])
  426. self.assertEqual(res["changes"], {"b": ["l1", {"l2": "val2"}, "l3"]})
  427. self.assertEqual(
  428. grainsmod.__grains__,
  429. {"a": "aval", "b": ["l1", {"l2": "val2"}, "l3"], "c": 8},
  430. )
  431. def test_set_nested_list_update_dict_key(self):
  432. with patch.dict(
  433. grainsmod.__grains__, {"a": "aval", "b": ["l1", {"l2": "val1"}], "c": 8}
  434. ):
  435. res = grainsmod.set("b,l2", "val2", delimiter=",")
  436. self.assertTrue(res["result"])
  437. self.assertEqual(res["changes"], {"b": ["l1", {"l2": "val2"}]})
  438. self.assertEqual(
  439. grainsmod.__grains__, {"a": "aval", "b": ["l1", {"l2": "val2"}], "c": 8}
  440. )
  441. def test_set_nested_list_update_dict_key_overwrite(self):
  442. with patch.dict(
  443. grainsmod.__grains__, {"a": "aval", "b": ["l1", {"l2": ["val1"]}], "c": 8}
  444. ):
  445. res = grainsmod.set("b,l2", "val2", delimiter=",", force=True)
  446. self.assertTrue(res["result"])
  447. self.assertEqual(res["changes"], {"b": ["l1", {"l2": "val2"}]})
  448. self.assertEqual(
  449. grainsmod.__grains__, {"a": "aval", "b": ["l1", {"l2": "val2"}], "c": 8}
  450. )
  451. def test_set_nested_list_append_dict_key(self):
  452. with patch.dict(
  453. grainsmod.__grains__, {"a": "aval", "b": ["l1", {"l2": "val2"}], "c": 8}
  454. ):
  455. res = grainsmod.set("b,l3", "val3", delimiter=",")
  456. self.assertTrue(res["result"])
  457. self.assertEqual(
  458. res["changes"], {"b": ["l1", {"l2": "val2"}, {"l3": "val3"}]}
  459. )
  460. self.assertEqual(
  461. grainsmod.__grains__,
  462. {"a": "aval", "b": ["l1", {"l2": "val2"}, {"l3": "val3"}], "c": 8},
  463. )
  464. def test_set_nested_existing_value_is_the_key(self):
  465. with patch.dict(grainsmod.__grains__, {"a": "aval", "b": "l3", "c": 8}):
  466. res = grainsmod.set("b,l3", "val3", delimiter=",")
  467. self.assertTrue(res["result"])
  468. self.assertEqual(res["changes"], {"b": {"l3": "val3"}})
  469. self.assertEqual(
  470. grainsmod.__grains__, {"a": "aval", "b": {"l3": "val3"}, "c": 8}
  471. )
  472. def test_set_nested_existing_value_overwrite(self):
  473. with patch.dict(grainsmod.__grains__, {"a": "aval", "b": "l1", "c": 8}):
  474. res = grainsmod.set("b,l3", "val3", delimiter=",", force=True)
  475. self.assertTrue(res["result"])
  476. self.assertEqual(res["changes"], {"b": {"l3": "val3"}})
  477. self.assertEqual(
  478. grainsmod.__grains__, {"a": "aval", "b": {"l3": "val3"}, "c": 8}
  479. )
  480. def test_set_deeply_nested_update(self):
  481. with patch.dict(
  482. grainsmod.__grains__,
  483. {"a": "aval", "b": {"l1": ["l21", "l22", {"l23": "l23val"}]}, "c": 8},
  484. ):
  485. res = grainsmod.set("b,l1,l23", "val", delimiter=",")
  486. self.assertTrue(res["result"])
  487. self.assertEqual(
  488. res["changes"], {"b": {"l1": ["l21", "l22", {"l23": "val"}]}}
  489. )
  490. self.assertEqual(
  491. grainsmod.__grains__,
  492. {"a": "aval", "b": {"l1": ["l21", "l22", {"l23": "val"}]}, "c": 8},
  493. )
  494. def test_set_deeply_nested_create(self):
  495. with patch.dict(
  496. grainsmod.__grains__,
  497. {"a": "aval", "b": {"l1": ["l21", "l22", {"l23": "l23val"}]}, "c": 8},
  498. ):
  499. res = grainsmod.set("b,l1,l24,l241", "val", delimiter=",")
  500. self.assertTrue(res["result"])
  501. self.assertEqual(
  502. res["changes"],
  503. {
  504. "b": {
  505. "l1": [
  506. "l21",
  507. "l22",
  508. {"l23": "l23val"},
  509. {"l24": {"l241": "val"}},
  510. ]
  511. }
  512. },
  513. )
  514. self.assertEqual(
  515. grainsmod.__grains__,
  516. {
  517. "a": "aval",
  518. "b": {
  519. "l1": [
  520. "l21",
  521. "l22",
  522. {"l23": "l23val"},
  523. {"l24": {"l241": "val"}},
  524. ]
  525. },
  526. "c": 8,
  527. },
  528. )
  529. def test_get_ordered(self):
  530. with patch.dict(
  531. grainsmod.__grains__,
  532. OrderedDict(
  533. [
  534. ("a", "aval"),
  535. (
  536. "b",
  537. OrderedDict(
  538. [
  539. ("z", "zval"),
  540. (
  541. "l1",
  542. ["l21", "l22", OrderedDict([("l23", "l23val")])],
  543. ),
  544. ]
  545. ),
  546. ),
  547. ("c", 8),
  548. ]
  549. ),
  550. ):
  551. res = grainsmod.get("b")
  552. self.assertEqual(type(res), OrderedDict)
  553. # Check that order really matters
  554. self.assertTrue(
  555. res
  556. == OrderedDict(
  557. [
  558. ("z", "zval"),
  559. ("l1", ["l21", "l22", OrderedDict([("l23", "l23val")])]),
  560. ]
  561. )
  562. )
  563. self.assertFalse(
  564. res
  565. == OrderedDict(
  566. [
  567. ("l1", ["l21", "l22", OrderedDict([("l23", "l23val")])]),
  568. ("z", "zval"),
  569. ]
  570. )
  571. )
  572. def test_get_unordered(self):
  573. with patch.dict(
  574. grainsmod.__grains__,
  575. OrderedDict(
  576. [
  577. ("a", "aval"),
  578. (
  579. "b",
  580. OrderedDict(
  581. [
  582. ("z", "zval"),
  583. (
  584. "l1",
  585. ["l21", "l22", OrderedDict([("l23", "l23val")])],
  586. ),
  587. ]
  588. ),
  589. ),
  590. ("c", 8),
  591. ]
  592. ),
  593. ):
  594. res = grainsmod.get("b", ordered=False)
  595. self.assertEqual(type(res), dict)
  596. # Check that order doesn't matter
  597. self.assertTrue(
  598. res
  599. == OrderedDict(
  600. [
  601. ("l1", ["l21", "l22", OrderedDict([("l23", "l23val")])]),
  602. ("z", "zval"),
  603. ]
  604. )
  605. )
  606. def test_equals(self):
  607. with patch.dict(
  608. grainsmod.__grains__,
  609. OrderedDict(
  610. [
  611. ("a", "aval"),
  612. (
  613. "b",
  614. OrderedDict(
  615. [
  616. ("z", "zval"),
  617. (
  618. "l1",
  619. ["l21", "l22", OrderedDict([("l23", "l23val")])],
  620. ),
  621. ]
  622. ),
  623. ),
  624. ("c", 8),
  625. ]
  626. ),
  627. ):
  628. res = grainsmod.equals("a", "aval")
  629. self.assertEqual(type(res), bool)
  630. self.assertTrue(res)
  631. res = grainsmod.equals("b:z", "zval")
  632. self.assertTrue(res)
  633. res = grainsmod.equals("b:z", "aval")
  634. self.assertFalse(res)