test_loader.py 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756
  1. """
  2. unit.loader
  3. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. Test Salt's loader
  5. """
  6. import collections
  7. import compileall
  8. import copy
  9. import imp
  10. import inspect
  11. import logging
  12. import os
  13. import shutil
  14. import sys
  15. import tempfile
  16. import textwrap
  17. import salt.config
  18. import salt.loader
  19. import salt.loader_context
  20. import salt.utils.files
  21. import salt.utils.stringutils
  22. from tests.support.case import ModuleCase
  23. from tests.support.helpers import slowTest
  24. from tests.support.mock import MagicMock, patch
  25. from tests.support.runtests import RUNTIME_VARS
  26. from tests.support.unit import TestCase
  27. log = logging.getLogger(__name__)
  28. def remove_bytecode(module_path):
  29. paths = [module_path + "c"]
  30. if hasattr(imp, "get_tag"):
  31. modname, ext = os.path.splitext(module_path.split(os.sep)[-1])
  32. paths.append(
  33. os.path.join(
  34. os.path.dirname(module_path),
  35. "__pycache__",
  36. "{}.{}.pyc".format(modname, imp.get_tag()),
  37. )
  38. )
  39. for path in paths:
  40. if os.path.exists(path):
  41. os.unlink(path)
  42. loader_template = """
  43. import os
  44. from salt.utils.decorators import depends
  45. @depends('os')
  46. def loaded():
  47. return True
  48. @depends('non_existantmodulename')
  49. def not_loaded():
  50. return True
  51. """
  52. class LazyLoaderTest(TestCase):
  53. """
  54. Test the loader
  55. """
  56. module_name = "lazyloadertest"
  57. @classmethod
  58. def setUpClass(cls):
  59. cls.opts = salt.config.minion_config(None)
  60. cls.opts["grains"] = salt.loader.grains(cls.opts)
  61. if not os.path.isdir(RUNTIME_VARS.TMP):
  62. os.makedirs(RUNTIME_VARS.TMP)
  63. cls.utils = salt.loader.utils(cls.opts)
  64. cls.proxy = salt.loader.proxy(cls.opts)
  65. cls.funcs = salt.loader.minion_mods(cls.opts, utils=cls.utils, proxy=cls.proxy)
  66. def setUp(self):
  67. # Setup the module
  68. self.module_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
  69. self.addCleanup(shutil.rmtree, self.module_dir, ignore_errors=True)
  70. self.module_file = os.path.join(
  71. self.module_dir, "{}.py".format(self.module_name)
  72. )
  73. with salt.utils.files.fopen(self.module_file, "w") as fh:
  74. fh.write(salt.utils.stringutils.to_str(loader_template))
  75. fh.flush()
  76. os.fsync(fh.fileno())
  77. # Invoke the loader
  78. self.loader = salt.loader.LazyLoader(
  79. [self.module_dir],
  80. copy.deepcopy(self.opts),
  81. pack={
  82. "__utils__": self.utils,
  83. "__salt__": self.funcs,
  84. "__proxy__": self.proxy,
  85. },
  86. tag="module",
  87. )
  88. def tearDown(self):
  89. del self.module_dir
  90. del self.module_file
  91. del self.loader
  92. @classmethod
  93. def tearDownClass(cls):
  94. del cls.opts
  95. del cls.funcs
  96. del cls.utils
  97. del cls.proxy
  98. @slowTest
  99. def test_depends(self):
  100. """
  101. Test that the depends decorator works properly
  102. """
  103. # Make sure depends correctly allowed a function to load. If this
  104. # results in a KeyError, the decorator is broken.
  105. self.assertTrue(
  106. isinstance(
  107. self.loader[self.module_name + ".loaded"], salt.loader.LoadedFunc,
  108. )
  109. )
  110. # Make sure depends correctly kept a function from loading
  111. self.assertTrue(self.module_name + ".not_loaded" not in self.loader)
  112. loader_template_module = """
  113. import my_utils
  114. def run():
  115. return my_utils.run()
  116. """
  117. loader_template_utils = """
  118. def run():
  119. return True
  120. """
  121. class LazyLoaderUtilsTest(TestCase):
  122. """
  123. Test the loader
  124. """
  125. module_name = "lazyloaderutilstest"
  126. utils_name = "my_utils"
  127. @classmethod
  128. def setUpClass(cls):
  129. cls.opts = salt.config.minion_config(None)
  130. cls.opts["grains"] = salt.loader.grains(cls.opts)
  131. if not os.path.isdir(RUNTIME_VARS.TMP):
  132. os.makedirs(RUNTIME_VARS.TMP)
  133. def setUp(self):
  134. # Setup the module
  135. self.module_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
  136. self.module_file = os.path.join(
  137. self.module_dir, "{}.py".format(self.module_name)
  138. )
  139. with salt.utils.files.fopen(self.module_file, "w") as fh:
  140. fh.write(salt.utils.stringutils.to_str(loader_template_module))
  141. fh.flush()
  142. os.fsync(fh.fileno())
  143. self.utils_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
  144. self.utils_file = os.path.join(self.utils_dir, "{}.py".format(self.utils_name))
  145. with salt.utils.files.fopen(self.utils_file, "w") as fh:
  146. fh.write(salt.utils.stringutils.to_str(loader_template_utils))
  147. fh.flush()
  148. os.fsync(fh.fileno())
  149. def tearDown(self):
  150. shutil.rmtree(self.module_dir)
  151. if os.path.isdir(self.module_dir):
  152. shutil.rmtree(self.module_dir)
  153. shutil.rmtree(self.utils_dir)
  154. if os.path.isdir(self.utils_dir):
  155. shutil.rmtree(self.utils_dir)
  156. del self.module_dir
  157. del self.module_file
  158. del self.utils_dir
  159. del self.utils_file
  160. if self.module_name in sys.modules:
  161. del sys.modules[self.module_name]
  162. if self.utils_name in sys.modules:
  163. del sys.modules[self.utils_name]
  164. @classmethod
  165. def tearDownClass(cls):
  166. del cls.opts
  167. def test_utils_found(self):
  168. """
  169. Test that the extra module directory is available for imports
  170. """
  171. loader = salt.loader.LazyLoader(
  172. [self.module_dir],
  173. copy.deepcopy(self.opts),
  174. tag="module",
  175. extra_module_dirs=[self.utils_dir],
  176. )
  177. self.assertTrue(
  178. isinstance(loader[self.module_name + ".run"], salt.loader.LoadedFunc)
  179. )
  180. self.assertTrue(loader[self.module_name + ".run"]())
  181. def test_utils_not_found(self):
  182. """
  183. Test that the extra module directory is not available for imports
  184. """
  185. loader = salt.loader.LazyLoader(
  186. [self.module_dir], copy.deepcopy(self.opts), tag="module"
  187. )
  188. self.assertTrue(self.module_name + ".run" not in loader)
  189. class LazyLoaderVirtualEnabledTest(TestCase):
  190. """
  191. Test the base loader of salt.
  192. """
  193. @classmethod
  194. def setUpClass(cls):
  195. cls.opts = salt.config.minion_config(None)
  196. cls.opts["disable_modules"] = ["pillar"]
  197. cls.opts["grains"] = salt.loader.grains(cls.opts)
  198. cls.utils = salt.loader.utils(copy.deepcopy(cls.opts))
  199. cls.proxy = salt.loader.proxy(cls.opts)
  200. cls.funcs = salt.loader.minion_mods(cls.opts, utils=cls.utils, proxy=cls.proxy)
  201. def setUp(self):
  202. self.loader = salt.loader.LazyLoader(
  203. salt.loader._module_dirs(copy.deepcopy(self.opts), "modules", "module"),
  204. copy.deepcopy(self.opts),
  205. pack={
  206. "__utils__": self.utils,
  207. "__salt__": self.funcs,
  208. "__proxy__": self.proxy,
  209. },
  210. tag="module",
  211. )
  212. def tearDown(self):
  213. del self.loader
  214. @classmethod
  215. def tearDownClass(cls):
  216. del cls.opts
  217. del cls.funcs
  218. del cls.utils
  219. del cls.proxy
  220. @slowTest
  221. def test_basic(self):
  222. """
  223. Ensure that it only loads stuff when needed
  224. """
  225. # make sure it starts empty
  226. self.assertEqual(self.loader._dict, {})
  227. # get something, and make sure its a func
  228. self.assertTrue(inspect.isfunction(self.loader["test.ping"].func))
  229. # make sure we only loaded "test" functions
  230. for key, val in self.loader._dict.items():
  231. self.assertEqual(key.split(".", 1)[0], "test")
  232. # make sure the depends thing worked (double check of the depends testing,
  233. # since the loader does the calling magically
  234. self.assertFalse("test.missing_func" in self.loader._dict)
  235. def test_badkey(self):
  236. with self.assertRaises(KeyError):
  237. self.loader[None] # pylint: disable=W0104
  238. with self.assertRaises(KeyError):
  239. self.loader[1] # pylint: disable=W0104
  240. @slowTest
  241. def test_disable(self):
  242. self.assertNotIn("pillar.items", self.loader)
  243. @slowTest
  244. def test_len_load(self):
  245. """
  246. Since LazyLoader is a MutableMapping, if someone asks for len() we have
  247. to load all
  248. """
  249. self.assertEqual(self.loader._dict, {})
  250. len(self.loader) # force a load all
  251. self.assertNotEqual(self.loader._dict, {})
  252. @slowTest
  253. def test_iter_load(self):
  254. """
  255. Since LazyLoader is a MutableMapping, if someone asks to iterate we have
  256. to load all
  257. """
  258. self.assertEqual(self.loader._dict, {})
  259. # force a load all
  260. for key, func in self.loader.items():
  261. break
  262. self.assertNotEqual(self.loader._dict, {})
  263. def test_context(self):
  264. """
  265. Make sure context is shared across modules
  266. """
  267. # make sure it starts empty
  268. self.assertEqual(self.loader._dict, {})
  269. # get something, and make sure its a func
  270. func = self.loader["test.ping"]
  271. with salt.loader_context.loader_context(self.loader):
  272. with patch.dict(func.__globals__["__context__"], {"foo": "bar"}):
  273. self.assertEqual(
  274. self.loader["test.echo"].__globals__["__context__"]["foo"], "bar"
  275. )
  276. self.assertEqual(
  277. self.loader["grains.get"].__globals__["__context__"]["foo"], "bar"
  278. )
  279. def test_globals(self):
  280. with salt.loader_context.loader_context(self.loader):
  281. func_globals = self.loader["test.ping"].__globals__
  282. self.assertEqual(
  283. func_globals["__grains__"].value(), self.opts.get("grains", {})
  284. )
  285. self.assertEqual(
  286. func_globals["__pillar__"].value(), self.opts.get("pillar", {})
  287. )
  288. # the opts passed into modules is at least a subset of the whole opts
  289. for key, val in func_globals["__opts__"].items():
  290. if (
  291. key in salt.config.DEFAULT_MASTER_OPTS
  292. and key not in salt.config.DEFAULT_MINION_OPTS
  293. ):
  294. # We loaded the minion opts, but somewhere in the code, the master options got pulled in
  295. # Let's just not check for equality since the option won't even exist in the loaded
  296. # minion options
  297. continue
  298. if (
  299. key not in salt.config.DEFAULT_MASTER_OPTS
  300. and key not in salt.config.DEFAULT_MINION_OPTS
  301. ):
  302. # This isn't even a default configuration setting, lets carry on
  303. continue
  304. self.assertEqual(self.opts[key], val)
  305. def test_pack(self):
  306. with salt.loader_context.loader_context(self.loader):
  307. self.loader.pack["__foo__"] = "bar"
  308. func_globals = self.loader["test.ping"].__globals__
  309. self.assertEqual(func_globals["__foo__"].value(), "bar")
  310. @slowTest
  311. def test_virtual(self):
  312. self.assertNotIn("test_virtual.ping", self.loader)
  313. class LazyLoaderVirtualDisabledTest(TestCase):
  314. """
  315. Test the loader of salt without __virtual__
  316. """
  317. @classmethod
  318. def setUpClass(cls):
  319. cls.opts = salt.config.minion_config(None)
  320. cls.opts["grains"] = salt.loader.grains(cls.opts)
  321. cls.utils = salt.loader.utils(copy.deepcopy(cls.opts))
  322. cls.proxy = salt.loader.proxy(cls.opts)
  323. cls.funcs = salt.loader.minion_mods(cls.opts, utils=cls.utils, proxy=cls.proxy)
  324. def setUp(self):
  325. self.loader = salt.loader.LazyLoader(
  326. salt.loader._module_dirs(copy.deepcopy(self.opts), "modules", "module"),
  327. copy.deepcopy(self.opts),
  328. tag="module",
  329. pack={
  330. "__utils__": self.utils,
  331. "__salt__": self.funcs,
  332. "__proxy__": self.proxy,
  333. },
  334. virtual_enable=False,
  335. )
  336. def tearDown(self):
  337. del self.loader
  338. @classmethod
  339. def tearDownClass(cls):
  340. del cls.opts
  341. del cls.utils
  342. del cls.funcs
  343. del cls.proxy
  344. @slowTest
  345. def test_virtual(self):
  346. self.assertTrue(
  347. isinstance(self.loader["test_virtual.ping"], salt.loader.LoadedFunc,)
  348. )
  349. class LazyLoaderWhitelistTest(TestCase):
  350. """
  351. Test the loader of salt with a whitelist
  352. """
  353. @classmethod
  354. def setUpClass(cls):
  355. cls.opts = salt.config.minion_config(None)
  356. cls.opts["grains"] = salt.loader.grains(cls.opts)
  357. cls.utils = salt.loader.utils(copy.deepcopy(cls.opts))
  358. cls.proxy = salt.loader.proxy(cls.opts)
  359. cls.funcs = salt.loader.minion_mods(cls.opts, utils=cls.utils, proxy=cls.proxy)
  360. def setUp(self):
  361. self.loader = salt.loader.LazyLoader(
  362. salt.loader._module_dirs(copy.deepcopy(self.opts), "modules", "module"),
  363. copy.deepcopy(self.opts),
  364. tag="module",
  365. pack={
  366. "__utils__": self.utils,
  367. "__salt__": self.funcs,
  368. "__proxy__": self.proxy,
  369. },
  370. whitelist=["test", "pillar"],
  371. )
  372. def tearDown(self):
  373. del self.loader
  374. @classmethod
  375. def tearDownClass(cls):
  376. del cls.opts
  377. del cls.funcs
  378. del cls.utils
  379. del cls.proxy
  380. @slowTest
  381. def test_whitelist(self):
  382. self.assertTrue(inspect.isfunction(self.loader["test.ping"].func))
  383. self.assertTrue(inspect.isfunction(self.loader["pillar.get"].func))
  384. self.assertNotIn("grains.get", self.loader)
  385. class LazyLoaderGrainsBlacklistTest(TestCase):
  386. """
  387. Test the loader of grains with a blacklist
  388. """
  389. def setUp(self):
  390. self.opts = salt.config.minion_config(None)
  391. def tearDown(self):
  392. del self.opts
  393. @slowTest
  394. def test_whitelist(self):
  395. opts = copy.deepcopy(self.opts)
  396. opts["grains_blacklist"] = ["master", "os*", "ipv[46]"]
  397. grains = salt.loader.grains(opts)
  398. self.assertNotIn("master", grains)
  399. self.assertNotIn("os", {g[:2] for g in list(grains)})
  400. self.assertNotIn("ipv4", grains)
  401. self.assertNotIn("ipv6", grains)
  402. class LazyLoaderSingleItem(TestCase):
  403. """
  404. Test loading a single item via the _load() function
  405. """
  406. @classmethod
  407. def setUpClass(cls):
  408. cls.opts = salt.config.minion_config(None)
  409. cls.opts["grains"] = salt.loader.grains(cls.opts)
  410. cls.utils = salt.loader.utils(copy.deepcopy(cls.opts))
  411. cls.proxy = salt.loader.proxy(cls.opts)
  412. cls.funcs = salt.loader.minion_mods(cls.opts, utils=cls.utils, proxy=cls.proxy)
  413. @classmethod
  414. def tearDownClass(cls):
  415. del cls.opts
  416. del cls.funcs
  417. del cls.utils
  418. del cls.proxy
  419. def setUp(self):
  420. self.loader = salt.loader.LazyLoader(
  421. salt.loader._module_dirs(copy.deepcopy(self.opts), "modules", "module"),
  422. copy.deepcopy(self.opts),
  423. pack={
  424. "__utils__": self.utils,
  425. "__salt__": self.funcs,
  426. "__proxy__": self.proxy,
  427. },
  428. tag="module",
  429. )
  430. def tearDown(self):
  431. del self.loader
  432. def test_single_item_no_dot(self):
  433. """
  434. Checks that a KeyError is raised when the function key does not contain a '.'
  435. """
  436. key = "testing_no_dot"
  437. expected = "The key '{}' should contain a '.'".format(key)
  438. with self.assertRaises(KeyError) as err:
  439. inspect.isfunction(self.loader["testing_no_dot"])
  440. result = err.exception.args[0]
  441. assert result == expected, result
  442. module_template = """
  443. __load__ = ['test', 'test_alias']
  444. __func_alias__ = dict(test_alias='working_alias')
  445. from salt.utils.decorators import depends
  446. def test():
  447. return {count}
  448. def test_alias():
  449. return True
  450. def test2():
  451. return True
  452. @depends('non_existantmodulename')
  453. def test3():
  454. return True
  455. @depends('non_existantmodulename', fallback_function=test)
  456. def test4():
  457. return True
  458. """
  459. class LazyLoaderReloadingTest(TestCase):
  460. """
  461. Test the loader of salt with changing modules
  462. """
  463. module_name = "loadertest"
  464. module_key = "loadertest.test"
  465. @classmethod
  466. def setUpClass(cls):
  467. cls.opts = salt.config.minion_config(None)
  468. cls.opts["grains"] = salt.loader.grains(cls.opts)
  469. if not os.path.isdir(RUNTIME_VARS.TMP):
  470. os.makedirs(RUNTIME_VARS.TMP)
  471. def setUp(self):
  472. self.tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
  473. self.addCleanup(shutil.rmtree, self.tmp_dir, ignore_errors=True)
  474. self.count = 0
  475. opts = copy.deepcopy(self.opts)
  476. dirs = salt.loader._module_dirs(opts, "modules", "module")
  477. dirs.append(self.tmp_dir)
  478. self.utils = salt.loader.utils(opts)
  479. self.proxy = salt.loader.proxy(opts)
  480. self.minion_mods = salt.loader.minion_mods(opts)
  481. self.loader = salt.loader.LazyLoader(
  482. dirs,
  483. opts,
  484. tag="module",
  485. pack={
  486. "__utils__": self.utils,
  487. "__proxy__": self.proxy,
  488. "__salt__": self.minion_mods,
  489. },
  490. )
  491. def tearDown(self):
  492. for attrname in ("tmp_dir", "utils", "proxy", "loader", "minion_mods", "utils"):
  493. try:
  494. delattr(self, attrname)
  495. except AttributeError:
  496. continue
  497. @classmethod
  498. def tearDownClass(cls):
  499. del cls.opts
  500. def update_module(self):
  501. self.count += 1
  502. with salt.utils.files.fopen(self.module_path, "wb") as fh:
  503. fh.write(
  504. salt.utils.stringutils.to_bytes(
  505. module_template.format(count=self.count)
  506. )
  507. )
  508. fh.flush()
  509. os.fsync(fh.fileno()) # flush to disk
  510. # pyc files don't like it when we change the original quickly
  511. # since the header bytes only contain the timestamp (granularity of seconds)
  512. # TODO: don't write them? Is *much* slower on re-load (~3x)
  513. # https://docs.python.org/2/library/sys.html#sys.dont_write_bytecode
  514. remove_bytecode(self.module_path)
  515. def rm_module(self):
  516. os.unlink(self.module_path)
  517. remove_bytecode(self.module_path)
  518. @property
  519. def module_path(self):
  520. return os.path.join(self.tmp_dir, "{}.py".format(self.module_name))
  521. @slowTest
  522. def test_alias(self):
  523. """
  524. Make sure that you can access alias-d modules
  525. """
  526. # ensure it doesn't exist
  527. self.assertNotIn(self.module_key, self.loader)
  528. self.update_module()
  529. self.assertNotIn("{}.test_alias".format(self.module_name), self.loader)
  530. self.assertTrue(
  531. isinstance(
  532. self.loader["{}.working_alias".format(self.module_name)],
  533. salt.loader.LoadedFunc,
  534. )
  535. )
  536. self.assertTrue(
  537. inspect.isfunction(
  538. self.loader["{}.working_alias".format(self.module_name)].func
  539. )
  540. )
  541. @slowTest
  542. def test_clear(self):
  543. self.assertTrue(isinstance(self.loader["test.ping"], salt.loader.LoadedFunc))
  544. self.assertTrue(inspect.isfunction(self.loader["test.ping"].func))
  545. self.update_module() # write out out custom module
  546. self.loader.clear() # clear the loader dict
  547. # force a load of our module
  548. self.assertTrue(
  549. isinstance(self.loader[self.module_key], salt.loader.LoadedFunc)
  550. )
  551. self.assertTrue(inspect.isfunction(self.loader[self.module_key].func))
  552. # make sure we only loaded our custom module
  553. # which means that we did correctly refresh the file mapping
  554. for k, v in self.loader._dict.items():
  555. self.assertTrue(k.startswith(self.module_name))
  556. @slowTest
  557. def test_load(self):
  558. # ensure it doesn't exist
  559. self.assertNotIn(self.module_key, self.loader)
  560. self.update_module()
  561. self.assertTrue(
  562. isinstance(self.loader[self.module_key], salt.loader.LoadedFunc)
  563. )
  564. self.assertTrue(inspect.isfunction(self.loader[self.module_key].func))
  565. @slowTest
  566. def test__load__(self):
  567. """
  568. If a module specifies __load__ we should only load/expose those modules
  569. """
  570. self.update_module()
  571. # ensure it doesn't exist
  572. self.assertNotIn(self.module_key + "2", self.loader)
  573. @slowTest
  574. def test__load__and_depends(self):
  575. """
  576. If a module specifies __load__ we should only load/expose those modules
  577. """
  578. self.update_module()
  579. # ensure it doesn't exist
  580. self.assertNotIn(self.module_key + "3", self.loader)
  581. self.assertNotIn(self.module_key + "4", self.loader)
  582. @slowTest
  583. def test_reload(self):
  584. # ensure it doesn't exist
  585. self.assertNotIn(self.module_key, self.loader)
  586. # make sure it updates correctly
  587. for x in range(1, 3):
  588. self.update_module()
  589. self.loader.clear()
  590. self.assertEqual(self.loader[self.module_key](), self.count)
  591. self.rm_module()
  592. # make sure that even if we remove the module, its still loaded until a clear
  593. self.assertEqual(self.loader[self.module_key](), self.count)
  594. self.loader.clear()
  595. self.assertNotIn(self.module_key, self.loader)
  596. def test_wrong_bytecode(self):
  597. """
  598. Checks to make sure we don't even try to load .pyc files that are for a different Python
  599. This should pass (the load should fail) all the time because we don't run Salt on Py 3.4 anymore
  600. """
  601. test_module_name = "test_module.cpython-34"
  602. filemap_save = copy.deepcopy(self.loader.file_mapping)
  603. self.loader.file_mapping = {
  604. test_module_name: (
  605. "/temp/path/does/not/matter/here/__pycache__/"
  606. + test_module_name
  607. + ".pyc",
  608. ".pyc",
  609. 0,
  610. )
  611. }
  612. self.assertFalse(self.loader._load_module(test_module_name))
  613. self.loader.file_mapping = copy.deepcopy(filemap_save)
  614. virtual_aliases = ("loadertest2", "loadertest3")
  615. virtual_alias_module_template = """
  616. __virtual_aliases__ = {}
  617. def test():
  618. return True
  619. """.format(
  620. virtual_aliases
  621. )
  622. class LazyLoaderVirtualAliasTest(TestCase):
  623. """
  624. Test the loader of salt with changing modules
  625. """
  626. module_name = "loadertest"
  627. @classmethod
  628. def setUpClass(cls):
  629. cls.opts = salt.config.minion_config(None)
  630. cls.opts["grains"] = salt.loader.grains(cls.opts)
  631. if not os.path.isdir(RUNTIME_VARS.TMP):
  632. os.makedirs(RUNTIME_VARS.TMP)
  633. def setUp(self):
  634. self.tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
  635. opts = copy.deepcopy(self.opts)
  636. dirs = salt.loader._module_dirs(opts, "modules", "module")
  637. dirs.append(self.tmp_dir)
  638. self.utils = salt.loader.utils(opts)
  639. self.proxy = salt.loader.proxy(opts)
  640. self.minion_mods = salt.loader.minion_mods(opts)
  641. self.loader = salt.loader.LazyLoader(
  642. dirs,
  643. opts,
  644. tag="module",
  645. pack={
  646. "__utils__": self.utils,
  647. "__proxy__": self.proxy,
  648. "__salt__": self.minion_mods,
  649. },
  650. )
  651. def tearDown(self):
  652. del self.tmp_dir
  653. del self.utils
  654. del self.proxy
  655. del self.minion_mods
  656. del self.loader
  657. @classmethod
  658. def tearDownClass(cls):
  659. del cls.opts
  660. def update_module(self):
  661. with salt.utils.files.fopen(self.module_path, "wb") as fh:
  662. fh.write(salt.utils.stringutils.to_bytes(virtual_alias_module_template))
  663. fh.flush()
  664. os.fsync(fh.fileno()) # flush to disk
  665. # pyc files don't like it when we change the original quickly
  666. # since the header bytes only contain the timestamp (granularity of seconds)
  667. # TODO: don't write them? Is *much* slower on re-load (~3x)
  668. # https://docs.python.org/2/library/sys.html#sys.dont_write_bytecode
  669. remove_bytecode(self.module_path)
  670. @property
  671. def module_path(self):
  672. return os.path.join(self.tmp_dir, "{}.py".format(self.module_name))
  673. @slowTest
  674. def test_virtual_alias(self):
  675. """
  676. Test the __virtual_alias__ feature
  677. """
  678. self.update_module()
  679. mod_names = [self.module_name] + list(virtual_aliases)
  680. for mod_name in mod_names:
  681. func_name = ".".join((mod_name, "test"))
  682. log.debug("Running %s (dict attribute)", func_name)
  683. self.assertTrue(self.loader[func_name]())
  684. log.debug("Running %s (loader attribute)", func_name)
  685. self.assertTrue(getattr(self.loader, mod_name).test())
  686. submodule_template = """
  687. from __future__ import absolute_import
  688. import {0}.lib
  689. def test():
  690. return ({count}, {0}.lib.test())
  691. """
  692. submodule_lib_template = """
  693. def test():
  694. return {count}
  695. """
  696. class LazyLoaderSubmodReloadingTest(TestCase):
  697. """
  698. Test the loader of salt with changing modules
  699. """
  700. module_name = "loadertestsubmod"
  701. module_key = "loadertestsubmod.test"
  702. @classmethod
  703. def setUpClass(cls):
  704. cls.opts = salt.config.minion_config(None)
  705. cls.opts["grains"] = salt.loader.grains(cls.opts)
  706. if not os.path.isdir(RUNTIME_VARS.TMP):
  707. os.makedirs(RUNTIME_VARS.TMP)
  708. def setUp(self):
  709. self.tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
  710. self.addCleanup(shutil.rmtree, self.tmp_dir, ignore_errors=True)
  711. os.makedirs(self.module_dir)
  712. self.count = 0
  713. self.lib_count = 0
  714. opts = copy.deepcopy(self.opts)
  715. dirs = salt.loader._module_dirs(opts, "modules", "module")
  716. dirs.append(self.tmp_dir)
  717. self.utils = salt.loader.utils(opts)
  718. self.proxy = salt.loader.proxy(opts)
  719. self.minion_mods = salt.loader.minion_mods(opts)
  720. self.loader = salt.loader.LazyLoader(
  721. dirs,
  722. opts,
  723. tag="module",
  724. pack={
  725. "__utils__": self.utils,
  726. "__proxy__": self.proxy,
  727. "__salt__": self.minion_mods,
  728. },
  729. )
  730. def tearDown(self):
  731. del self.tmp_dir
  732. del self.utils
  733. del self.proxy
  734. del self.minion_mods
  735. del self.loader
  736. @classmethod
  737. def tearDownClass(cls):
  738. del cls.opts
  739. def update_module(self):
  740. self.count += 1
  741. with salt.utils.files.fopen(self.module_path, "wb") as fh:
  742. fh.write(
  743. salt.utils.stringutils.to_bytes(
  744. submodule_template.format(self.module_name, count=self.count)
  745. )
  746. )
  747. fh.flush()
  748. os.fsync(fh.fileno()) # flush to disk
  749. # pyc files don't like it when we change the original quickly
  750. # since the header bytes only contain the timestamp (granularity of seconds)
  751. # TODO: don't write them? Is *much* slower on re-load (~3x)
  752. # https://docs.python.org/2/library/sys.html#sys.dont_write_bytecode
  753. remove_bytecode(self.module_path)
  754. def rm_module(self):
  755. os.unlink(self.module_path)
  756. remove_bytecode(self.module_path)
  757. def update_lib(self):
  758. self.lib_count += 1
  759. for modname in list(sys.modules):
  760. if modname.startswith(self.module_name):
  761. del sys.modules[modname]
  762. with salt.utils.files.fopen(self.lib_path, "wb") as fh:
  763. fh.write(
  764. salt.utils.stringutils.to_bytes(
  765. submodule_lib_template.format(count=self.lib_count)
  766. )
  767. )
  768. fh.flush()
  769. os.fsync(fh.fileno()) # flush to disk
  770. # pyc files don't like it when we change the original quickly
  771. # since the header bytes only contain the timestamp (granularity of seconds)
  772. # TODO: don't write them? Is *much* slower on re-load (~3x)
  773. # https://docs.python.org/2/library/sys.html#sys.dont_write_bytecode
  774. remove_bytecode(self.lib_path)
  775. def rm_lib(self):
  776. for modname in list(sys.modules):
  777. if modname.startswith(self.module_name):
  778. del sys.modules[modname]
  779. os.unlink(self.lib_path)
  780. remove_bytecode(self.lib_path)
  781. @property
  782. def module_dir(self):
  783. return os.path.join(self.tmp_dir, self.module_name)
  784. @property
  785. def module_path(self):
  786. return os.path.join(self.module_dir, "__init__.py")
  787. @property
  788. def lib_path(self):
  789. return os.path.join(self.module_dir, "lib.py")
  790. @slowTest
  791. def test_basic(self):
  792. # ensure it doesn't exist
  793. self.assertNotIn(self.module_key, self.loader)
  794. self.update_module()
  795. self.update_lib()
  796. self.loader.clear()
  797. self.assertIn(self.module_key, self.loader)
  798. @slowTest
  799. def test_reload(self):
  800. # ensure it doesn't exist
  801. self.assertNotIn(self.module_key, self.loader)
  802. # update both the module and the lib
  803. for x in range(1, 3):
  804. self.update_lib()
  805. self.update_module()
  806. self.loader.clear()
  807. self.assertNotIn(self.module_key, self.loader._dict)
  808. self.assertIn(self.module_key, self.loader)
  809. self.assertEqual(
  810. self.loader[self.module_key](), (self.count, self.lib_count)
  811. )
  812. # update just the module
  813. for x in range(1, 3):
  814. self.update_module()
  815. self.loader.clear()
  816. self.assertNotIn(self.module_key, self.loader._dict)
  817. self.assertIn(self.module_key, self.loader)
  818. self.assertEqual(
  819. self.loader[self.module_key](), (self.count, self.lib_count)
  820. )
  821. # update just the lib
  822. for x in range(1, 3):
  823. self.update_lib()
  824. self.loader.clear()
  825. self.assertNotIn(self.module_key, self.loader._dict)
  826. self.assertIn(self.module_key, self.loader)
  827. self.assertEqual(
  828. self.loader[self.module_key](), (self.count, self.lib_count)
  829. )
  830. self.rm_module()
  831. # make sure that even if we remove the module, its still loaded until a clear
  832. self.assertEqual(self.loader[self.module_key](), (self.count, self.lib_count))
  833. self.loader.clear()
  834. self.assertNotIn(self.module_key, self.loader)
  835. @slowTest
  836. def test_reload_missing_lib(self):
  837. # ensure it doesn't exist
  838. self.assertNotIn(self.module_key, self.loader)
  839. # update both the module and the lib
  840. self.update_module()
  841. self.update_lib()
  842. self.loader.clear()
  843. self.assertEqual(self.loader[self.module_key](), (self.count, self.lib_count))
  844. # remove the lib, this means we should fail to load the module next time
  845. self.rm_lib()
  846. self.loader.clear()
  847. self.assertNotIn(self.module_key, self.loader)
  848. mod_template = """
  849. def test():
  850. return ({val})
  851. """
  852. class LazyLoaderModulePackageTest(TestCase):
  853. """
  854. Test the loader of salt with changing modules
  855. """
  856. module_name = "loadertestmodpkg"
  857. module_key = "loadertestmodpkg.test"
  858. @classmethod
  859. def setUpClass(cls):
  860. cls.opts = salt.config.minion_config(None)
  861. cls.opts["grains"] = salt.loader.grains(cls.opts)
  862. if not os.path.isdir(RUNTIME_VARS.TMP):
  863. os.makedirs(RUNTIME_VARS.TMP)
  864. cls.utils = salt.loader.utils(copy.deepcopy(cls.opts))
  865. cls.proxy = salt.loader.proxy(cls.opts)
  866. cls.funcs = salt.loader.minion_mods(cls.opts, utils=cls.utils, proxy=cls.proxy)
  867. def setUp(self):
  868. self.tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
  869. self.addCleanup(shutil.rmtree, self.tmp_dir, ignore_errors=True)
  870. dirs = salt.loader._module_dirs(copy.deepcopy(self.opts), "modules", "module")
  871. dirs.append(self.tmp_dir)
  872. self.loader = salt.loader.LazyLoader(
  873. dirs,
  874. copy.deepcopy(self.opts),
  875. pack={
  876. "__utils__": self.utils,
  877. "__salt__": self.funcs,
  878. "__proxy__": self.proxy,
  879. },
  880. tag="module",
  881. )
  882. def tearDown(self):
  883. del self.tmp_dir
  884. del self.loader
  885. @classmethod
  886. def tearDownClass(cls):
  887. del cls.opts
  888. del cls.funcs
  889. del cls.utils
  890. del cls.proxy
  891. def update_pyfile(self, pyfile, contents):
  892. dirname = os.path.dirname(pyfile)
  893. if not os.path.exists(dirname):
  894. os.makedirs(dirname)
  895. with salt.utils.files.fopen(pyfile, "wb") as fh:
  896. fh.write(salt.utils.stringutils.to_bytes(contents))
  897. fh.flush()
  898. os.fsync(fh.fileno()) # flush to disk
  899. # pyc files don't like it when we change the original quickly
  900. # since the header bytes only contain the timestamp (granularity of seconds)
  901. # TODO: don't write them? Is *much* slower on re-load (~3x)
  902. # https://docs.python.org/2/library/sys.html#sys.dont_write_bytecode
  903. remove_bytecode(pyfile)
  904. def rm_pyfile(self, pyfile):
  905. os.unlink(pyfile)
  906. remove_bytecode(pyfile)
  907. def update_module(self, relative_path, contents):
  908. self.update_pyfile(os.path.join(self.tmp_dir, relative_path), contents)
  909. def rm_module(self, relative_path):
  910. self.rm_pyfile(os.path.join(self.tmp_dir, relative_path))
  911. @slowTest
  912. def test_module(self):
  913. # ensure it doesn't exist
  914. self.assertNotIn("foo", self.loader)
  915. self.assertNotIn("foo.test", self.loader)
  916. self.update_module("foo.py", mod_template.format(val=1))
  917. self.loader.clear()
  918. self.assertIn("foo.test", self.loader)
  919. self.assertEqual(self.loader["foo.test"](), 1)
  920. @slowTest
  921. def test_package(self):
  922. # ensure it doesn't exist
  923. self.assertNotIn("foo", self.loader)
  924. self.assertNotIn("foo.test", self.loader)
  925. self.update_module("foo/__init__.py", mod_template.format(val=2))
  926. self.loader.clear()
  927. self.assertIn("foo.test", self.loader)
  928. self.assertEqual(self.loader["foo.test"](), 2)
  929. @slowTest
  930. def test_module_package_collision(self):
  931. # ensure it doesn't exist
  932. self.assertNotIn("foo", self.loader)
  933. self.assertNotIn("foo.test", self.loader)
  934. self.update_module("foo.py", mod_template.format(val=3))
  935. self.loader.clear()
  936. self.assertIn("foo.test", self.loader)
  937. self.assertEqual(self.loader["foo.test"](), 3)
  938. self.update_module("foo/__init__.py", mod_template.format(val=4))
  939. self.loader.clear()
  940. self.assertIn("foo.test", self.loader)
  941. self.assertEqual(self.loader["foo.test"](), 4)
  942. deep_init_base = """
  943. from __future__ import absolute_import
  944. import {0}.top_lib
  945. import {0}.top_lib.mid_lib
  946. import {0}.top_lib.mid_lib.bot_lib
  947. def top():
  948. return {0}.top_lib.test()
  949. def mid():
  950. return {0}.top_lib.mid_lib.test()
  951. def bot():
  952. return {0}.top_lib.mid_lib.bot_lib.test()
  953. """
  954. class LazyLoaderDeepSubmodReloadingTest(TestCase):
  955. module_name = "loadertestsubmoddeep"
  956. libs = ("top_lib", "mid_lib", "bot_lib")
  957. @classmethod
  958. def setUpClass(cls):
  959. cls.opts = salt.config.minion_config(None)
  960. cls.opts["grains"] = salt.loader.grains(cls.opts)
  961. if not os.path.isdir(RUNTIME_VARS.TMP):
  962. os.makedirs(RUNTIME_VARS.TMP)
  963. def setUp(self):
  964. self.tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
  965. self.addCleanup(shutil.rmtree, self.tmp_dir, ignore_errors=True)
  966. os.makedirs(self.module_dir)
  967. self.lib_count = collections.defaultdict(int) # mapping of path -> count
  968. # bootstrap libs
  969. with salt.utils.files.fopen(
  970. os.path.join(self.module_dir, "__init__.py"), "w"
  971. ) as fh:
  972. # No .decode() needed here as deep_init_base is defined as str and
  973. # not bytes.
  974. fh.write(
  975. salt.utils.stringutils.to_str(deep_init_base.format(self.module_name))
  976. )
  977. fh.flush()
  978. os.fsync(fh.fileno()) # flush to disk
  979. self.lib_paths = {}
  980. dir_path = self.module_dir
  981. for lib_name in self.libs:
  982. dir_path = os.path.join(dir_path, lib_name)
  983. self.lib_paths[lib_name] = dir_path
  984. os.makedirs(dir_path)
  985. self.update_lib(lib_name)
  986. opts = copy.deepcopy(self.opts)
  987. dirs = salt.loader._module_dirs(opts, "modules", "module")
  988. dirs.append(self.tmp_dir)
  989. self.utils = salt.loader.utils(opts)
  990. self.proxy = salt.loader.proxy(opts)
  991. self.minion_mods = salt.loader.minion_mods(opts)
  992. self.loader = salt.loader.LazyLoader(
  993. dirs,
  994. copy.deepcopy(opts),
  995. tag="module",
  996. pack={
  997. "__utils__": self.utils,
  998. "__proxy__": self.proxy,
  999. "__salt__": self.minion_mods,
  1000. },
  1001. )
  1002. self.assertIn("{}.top".format(self.module_name), self.loader)
  1003. def tearDown(self):
  1004. del self.tmp_dir
  1005. del self.lib_paths
  1006. del self.utils
  1007. del self.proxy
  1008. del self.minion_mods
  1009. del self.loader
  1010. del self.lib_count
  1011. @classmethod
  1012. def tearDownClass(cls):
  1013. del cls.opts
  1014. @property
  1015. def module_dir(self):
  1016. return os.path.join(self.tmp_dir, self.module_name)
  1017. def update_lib(self, lib_name):
  1018. for modname in list(sys.modules):
  1019. if modname.startswith(self.module_name):
  1020. del sys.modules[modname]
  1021. path = os.path.join(self.lib_paths[lib_name], "__init__.py")
  1022. self.lib_count[lib_name] += 1
  1023. with salt.utils.files.fopen(path, "wb") as fh:
  1024. fh.write(
  1025. salt.utils.stringutils.to_bytes(
  1026. submodule_lib_template.format(count=self.lib_count[lib_name])
  1027. )
  1028. )
  1029. fh.flush()
  1030. os.fsync(fh.fileno()) # flush to disk
  1031. # pyc files don't like it when we change the original quickly
  1032. # since the header bytes only contain the timestamp (granularity of seconds)
  1033. # TODO: don't write them? Is *much* slower on re-load (~3x)
  1034. # https://docs.python.org/2/library/sys.html#sys.dont_write_bytecode
  1035. remove_bytecode(path)
  1036. @slowTest
  1037. def test_basic(self):
  1038. self.assertIn("{}.top".format(self.module_name), self.loader)
  1039. def _verify_libs(self):
  1040. for lib in self.libs:
  1041. self.assertEqual(
  1042. self.loader[
  1043. "{}.{}".format(self.module_name, lib.replace("_lib", ""))
  1044. ](),
  1045. self.lib_count[lib],
  1046. )
  1047. @slowTest
  1048. def test_reload(self):
  1049. """
  1050. Make sure that we can reload all libraries of arbitrary depth
  1051. """
  1052. self._verify_libs()
  1053. # update them all
  1054. for lib in self.libs:
  1055. for x in range(5):
  1056. self.update_lib(lib)
  1057. self.loader.clear()
  1058. self._verify_libs()
  1059. class LoaderMultipleGlobalTest(ModuleCase):
  1060. """
  1061. Tests when using multiple lazyloaders
  1062. """
  1063. def setUp(self):
  1064. opts = salt.config.minion_config(None)
  1065. self.loader1 = salt.loader.LazyLoader(
  1066. salt.loader._module_dirs(copy.deepcopy(opts), "modules", "module"),
  1067. copy.deepcopy(opts),
  1068. pack={},
  1069. tag="module",
  1070. loaded_base_name="salt.loader1",
  1071. )
  1072. self.loader2 = salt.loader.LazyLoader(
  1073. salt.loader._module_dirs(copy.deepcopy(opts), "modules", "module"),
  1074. copy.deepcopy(opts),
  1075. pack={},
  1076. tag="module",
  1077. loaded_base_name="salt.loader2",
  1078. )
  1079. def tearDown(self):
  1080. del self.loader1
  1081. del self.loader2
  1082. def test_loader_globals(self):
  1083. """
  1084. Test to ensure loaders do not edit
  1085. each others loader's namespace
  1086. """
  1087. self.loader1.pack["__foo__"] = "bar1"
  1088. func1 = self.loader1["test.ping"]
  1089. self.loader2.pack["__foo__"] = "bar2"
  1090. func2 = self.loader2["test.ping"]
  1091. token = salt.loader_context.loader_ctxvar.set(self.loader1)
  1092. try:
  1093. assert func1.__globals__["__foo__"].value() == "bar1"
  1094. finally:
  1095. salt.loader_context.loader_ctxvar.reset(token)
  1096. token = salt.loader_context.loader_ctxvar.set(self.loader2)
  1097. try:
  1098. assert func2.__globals__["__foo__"].value() == "bar2"
  1099. finally:
  1100. salt.loader_context.loader_ctxvar.reset(token)
  1101. class LoaderCleanupTest(ModuleCase):
  1102. """
  1103. Tests the loader cleanup procedures
  1104. """
  1105. def setUp(self):
  1106. opts = salt.config.minion_config(None)
  1107. self.loader1 = salt.loader.LazyLoader(
  1108. salt.loader._module_dirs(copy.deepcopy(opts), "modules", "module"),
  1109. copy.deepcopy(opts),
  1110. pack={},
  1111. tag="module",
  1112. loaded_base_name="salt.test",
  1113. )
  1114. def tearDown(self):
  1115. del self.loader1
  1116. def test_loader_clean_modules(self):
  1117. loaded_base_name = self.loader1.loaded_base_name
  1118. self.loader1.clean_modules()
  1119. for name in list(sys.modules):
  1120. if name.startswith(loaded_base_name):
  1121. self.fail(
  1122. "Found a real module reference in sys.modules matching {!r}".format(
  1123. loaded_base_name
  1124. )
  1125. )
  1126. break
  1127. class LoaderGlobalsTest(ModuleCase):
  1128. """
  1129. Test all of the globals that the loader is responsible for adding to modules
  1130. This shouldn't be done here, but should rather be done per module type (in the cases where they are used)
  1131. so they can check ALL globals that they have (or should have) access to.
  1132. This is intended as a shorter term way of testing these so we don't break the loader
  1133. """
  1134. def _verify_globals(self, mod_dict):
  1135. """
  1136. Verify that the globals listed in the doc string (from the test) are in these modules
  1137. """
  1138. # find the globals
  1139. global_vars = {}
  1140. for val in mod_dict.values():
  1141. # only find salty globals
  1142. if val.__module__.startswith("salt.loaded"):
  1143. if hasattr(val, "__globals__"):
  1144. if hasattr(val, "__wrapped__") or "__wrapped__" in val.__globals__:
  1145. global_vars[val.__module__] = sys.modules[
  1146. val.__module__
  1147. ].__dict__
  1148. else:
  1149. global_vars[val.__module__] = val.__globals__
  1150. # if we couldn't find any, then we have no modules -- so something is broken
  1151. self.assertNotEqual(global_vars, {}, msg="No modules were loaded.")
  1152. # get the names of the globals you should have
  1153. func_name = inspect.stack()[1][3]
  1154. names = next(
  1155. iter(salt.utils.yaml.safe_load(getattr(self, func_name).__doc__).values())
  1156. )
  1157. # Now, test each module!
  1158. for item in global_vars.values():
  1159. for name in names:
  1160. self.assertIn(name, list(item.keys()))
  1161. def test_auth(self):
  1162. """
  1163. Test that auth mods have:
  1164. - __pillar__
  1165. - __grains__
  1166. - __salt__
  1167. - __context__
  1168. """
  1169. self._verify_globals(salt.loader.auth(self.master_opts))
  1170. def test_runners(self):
  1171. """
  1172. Test that runners have:
  1173. - __pillar__
  1174. - __salt__
  1175. - __opts__
  1176. - __grains__
  1177. - __context__
  1178. """
  1179. self._verify_globals(salt.loader.runner(self.master_opts))
  1180. def test_returners(self):
  1181. """
  1182. Test that returners have:
  1183. - __salt__
  1184. - __opts__
  1185. - __pillar__
  1186. - __grains__
  1187. - __context__
  1188. """
  1189. self._verify_globals(salt.loader.returners(self.master_opts, {}))
  1190. def test_pillars(self):
  1191. """
  1192. Test that pillars have:
  1193. - __salt__
  1194. - __opts__
  1195. - __pillar__
  1196. - __grains__
  1197. - __context__
  1198. """
  1199. self._verify_globals(salt.loader.pillars(self.master_opts, {}))
  1200. def test_tops(self):
  1201. """
  1202. Test that tops have: []
  1203. """
  1204. self._verify_globals(salt.loader.tops(self.master_opts))
  1205. def test_outputters(self):
  1206. """
  1207. Test that outputters have:
  1208. - __opts__
  1209. - __pillar__
  1210. - __grains__
  1211. - __context__
  1212. """
  1213. self._verify_globals(salt.loader.outputters(self.master_opts))
  1214. def test_serializers(self):
  1215. """
  1216. Test that serializers have: []
  1217. """
  1218. self._verify_globals(salt.loader.serializers(self.master_opts))
  1219. @slowTest
  1220. def test_states(self):
  1221. """
  1222. Test that states have:
  1223. - __pillar__
  1224. - __salt__
  1225. - __opts__
  1226. - __grains__
  1227. - __context__
  1228. """
  1229. opts = salt.config.minion_config(None)
  1230. opts["grains"] = salt.loader.grains(opts)
  1231. utils = salt.loader.utils(opts)
  1232. proxy = salt.loader.proxy(opts)
  1233. funcs = salt.loader.minion_mods(opts, utils=utils, proxy=proxy)
  1234. self._verify_globals(salt.loader.states(opts, funcs, utils, {}, proxy=proxy))
  1235. def test_renderers(self):
  1236. """
  1237. Test that renderers have:
  1238. - __salt__ # Execution functions (i.e. __salt__['test.echo']('foo'))
  1239. - __grains__ # Grains (i.e. __grains__['os'])
  1240. - __pillar__ # Pillar data (i.e. __pillar__['foo'])
  1241. - __opts__ # Minion configuration options
  1242. - __context__ # Context dict shared amongst all modules of the same type
  1243. """
  1244. self._verify_globals(salt.loader.render(self.master_opts, {}))
  1245. class RawModTest(TestCase):
  1246. """
  1247. Test the interface of raw_mod
  1248. """
  1249. def setUp(self):
  1250. self.opts = salt.config.minion_config(None)
  1251. def tearDown(self):
  1252. del self.opts
  1253. @slowTest
  1254. def test_basic(self):
  1255. testmod = salt.loader.raw_mod(self.opts, "test", None)
  1256. for k, v in testmod.items():
  1257. self.assertEqual(k.split(".")[0], "test")
  1258. def test_bad_name(self):
  1259. testmod = salt.loader.raw_mod(self.opts, "module_we_do_not_have", None)
  1260. self.assertEqual(testmod, {})
  1261. class NetworkUtilsTestCase(ModuleCase):
  1262. def test_is_private(self):
  1263. mod = salt.loader.raw_mod(self.minion_opts, "network", None)
  1264. self.assertTrue(mod["network.is_private"]("10.0.0.1"), True)
  1265. def test_is_loopback(self):
  1266. mod = salt.loader.raw_mod(self.minion_opts, "network", None)
  1267. self.assertTrue(mod["network.is_loopback"]("127.0.0.1"), True)
  1268. class LazyLoaderOptimizationOrderTest(TestCase):
  1269. """
  1270. Test the optimization order priority in the loader (PY3)
  1271. """
  1272. module_name = "lazyloadertest"
  1273. module_content = textwrap.dedent(
  1274. """\
  1275. # -*- coding: utf-8 -*-
  1276. from __future__ import absolute_import
  1277. def test():
  1278. return True
  1279. """
  1280. )
  1281. @classmethod
  1282. def setUpClass(cls):
  1283. cls.opts = salt.config.minion_config(None)
  1284. cls.opts["grains"] = salt.loader.grains(cls.opts)
  1285. cls.utils = salt.loader.utils(copy.deepcopy(cls.opts))
  1286. cls.proxy = salt.loader.proxy(cls.opts)
  1287. cls.funcs = salt.loader.minion_mods(cls.opts, utils=cls.utils, proxy=cls.proxy)
  1288. @classmethod
  1289. def tearDownClass(cls):
  1290. del cls.opts
  1291. del cls.funcs
  1292. del cls.utils
  1293. del cls.proxy
  1294. def setUp(self):
  1295. # Setup the module
  1296. self.module_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
  1297. self.addCleanup(shutil.rmtree, self.module_dir, ignore_errors=True)
  1298. self.module_file = os.path.join(
  1299. self.module_dir, "{}.py".format(self.module_name)
  1300. )
  1301. def tearDown(self):
  1302. try:
  1303. delattr(self, "loader")
  1304. except AttributeError:
  1305. pass
  1306. def _get_loader(self, order=None):
  1307. opts = copy.deepcopy(self.opts)
  1308. if order is not None:
  1309. opts["optimization_order"] = order
  1310. # Return a loader
  1311. return salt.loader.LazyLoader(
  1312. [self.module_dir],
  1313. opts,
  1314. pack={
  1315. "__utils__": self.utils,
  1316. "__salt__": self.funcs,
  1317. "__proxy__": self.proxy,
  1318. },
  1319. tag="module",
  1320. )
  1321. def _get_module_filename(self):
  1322. # The act of referencing the loader entry forces the module to be
  1323. # loaded by the LazyDict.
  1324. mod_fullname = self.loader[next(iter(self.loader))].__module__
  1325. return sys.modules[mod_fullname].__file__
  1326. def _expected(self, optimize=0):
  1327. return "lazyloadertest.cpython-{}{}{}.pyc".format(
  1328. sys.version_info[0],
  1329. sys.version_info[1],
  1330. "" if not optimize else ".opt-{}".format(optimize),
  1331. )
  1332. def _write_module_file(self):
  1333. with salt.utils.files.fopen(self.module_file, "w") as fh:
  1334. fh.write(self.module_content)
  1335. fh.flush()
  1336. os.fsync(fh.fileno())
  1337. def _byte_compile(self):
  1338. compileall.compile_file(self.module_file, quiet=1, optimize=0)
  1339. compileall.compile_file(self.module_file, quiet=1, optimize=1)
  1340. compileall.compile_file(self.module_file, quiet=1, optimize=2)
  1341. def _test_optimization_order(self, order):
  1342. self._write_module_file()
  1343. self._byte_compile()
  1344. # Clean up the original file so that we can be assured we're only
  1345. # loading the byte-compiled files(s).
  1346. os.remove(self.module_file)
  1347. self.loader = self._get_loader(order)
  1348. filename = self._get_module_filename()
  1349. basename = os.path.basename(filename)
  1350. assert basename == self._expected(order[0]), basename
  1351. # Remove the file and make a new loader. We should now load the
  1352. # byte-compiled file with an optimization level matching the 2nd
  1353. # element of the order list.
  1354. os.remove(filename)
  1355. self.loader = self._get_loader(order)
  1356. filename = self._get_module_filename()
  1357. basename = os.path.basename(filename)
  1358. assert basename == self._expected(order[1]), basename
  1359. # Remove the file and make a new loader. We should now load the
  1360. # byte-compiled file with an optimization level matching the 3rd
  1361. # element of the order list.
  1362. os.remove(filename)
  1363. self.loader = self._get_loader(order)
  1364. filename = self._get_module_filename()
  1365. basename = os.path.basename(filename)
  1366. assert basename == self._expected(order[2]), basename
  1367. def test_optimization_order(self):
  1368. """
  1369. Test the optimization_order config param
  1370. """
  1371. self._test_optimization_order([0, 1, 2])
  1372. self._test_optimization_order([0, 2, 1])
  1373. self._test_optimization_order([1, 2, 0])
  1374. self._test_optimization_order([1, 0, 2])
  1375. self._test_optimization_order([2, 0, 1])
  1376. self._test_optimization_order([2, 1, 0])
  1377. def test_load_source_file(self):
  1378. """
  1379. Make sure that .py files are preferred over .pyc files
  1380. """
  1381. self._write_module_file()
  1382. self._byte_compile()
  1383. self.loader = self._get_loader()
  1384. filename = self._get_module_filename()
  1385. basename = os.path.basename(filename)
  1386. expected = "lazyloadertest.py"
  1387. assert basename == expected, basename
  1388. class LoaderLoadCachedGrainsTest(TestCase):
  1389. """
  1390. Test how the loader works with cached grains
  1391. """
  1392. @classmethod
  1393. def setUpClass(cls):
  1394. cls.opts = salt.config.minion_config(None)
  1395. if not os.path.isdir(RUNTIME_VARS.TMP):
  1396. os.makedirs(RUNTIME_VARS.TMP)
  1397. def setUp(self):
  1398. self.cache_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
  1399. self.addCleanup(shutil.rmtree, self.cache_dir, ignore_errors=True)
  1400. self.opts["cachedir"] = self.cache_dir
  1401. self.opts["grains_cache"] = True
  1402. self.opts["grains"] = salt.loader.grains(self.opts)
  1403. @slowTest
  1404. def test_osrelease_info_has_correct_type(self):
  1405. """
  1406. Make sure osrelease_info is tuple after caching
  1407. """
  1408. grains = salt.loader.grains(self.opts)
  1409. osrelease_info = grains["osrelease_info"]
  1410. assert isinstance(osrelease_info, tuple), osrelease_info
  1411. class LazyLoaderRefreshFileMappingTest(TestCase):
  1412. """
  1413. Test that _refresh_file_mapping is called using acquiring LazyLoader._lock
  1414. """
  1415. @classmethod
  1416. def setUpClass(cls):
  1417. cls.opts = salt.config.minion_config(None)
  1418. cls.opts["grains"] = salt.loader.grains(cls.opts)
  1419. cls.utils = salt.loader.utils(copy.deepcopy(cls.opts))
  1420. cls.proxy = salt.loader.proxy(cls.opts)
  1421. cls.funcs = salt.loader.minion_mods(cls.opts, utils=cls.utils, proxy=cls.proxy)
  1422. def setUp(self):
  1423. class LazyLoaderMock(salt.loader.LazyLoader):
  1424. pass
  1425. self.LOADER_CLASS = LazyLoaderMock
  1426. def __init_loader(self):
  1427. return self.LOADER_CLASS(
  1428. salt.loader._module_dirs(copy.deepcopy(self.opts), "modules", "module"),
  1429. copy.deepcopy(self.opts),
  1430. tag="module",
  1431. pack={
  1432. "__utils__": self.utils,
  1433. "__salt__": self.funcs,
  1434. "__proxy__": self.proxy,
  1435. },
  1436. )
  1437. @classmethod
  1438. def tearDownClass(cls):
  1439. del cls.opts
  1440. del cls.utils
  1441. del cls.funcs
  1442. del cls.proxy
  1443. def test_lazyloader_refresh_file_mapping_called_with_lock_at___init__(self):
  1444. func_mock = MagicMock()
  1445. lock_mock = MagicMock()
  1446. lock_mock.__enter__ = MagicMock()
  1447. self.LOADER_CLASS._refresh_file_mapping = func_mock
  1448. with patch("threading.RLock", MagicMock(return_value=lock_mock)):
  1449. loader = self.__init_loader()
  1450. lock_mock.__enter__.assert_called()
  1451. func_mock.assert_called()
  1452. assert len(func_mock.call_args_list) == len(lock_mock.__enter__.call_args_list)
  1453. del loader
  1454. def test_lazyloader_zip_modules(self):
  1455. self.opts["enable_zip_modules"] = True
  1456. try:
  1457. loader = self.__init_loader()
  1458. assert ".zip" in loader.suffix_map
  1459. assert ".zip" in loader.suffix_order
  1460. finally:
  1461. self.opts["enable_zip_modules"] = False
  1462. loader = self.__init_loader()
  1463. assert ".zip" not in loader.suffix_map
  1464. assert ".zip" not in loader.suffix_order
  1465. def test_lazyloader_pyx_modules(self):
  1466. self.opts["cython_enable"] = True
  1467. try:
  1468. loader = self.__init_loader()
  1469. # Don't assert if the current environment has no pyximport
  1470. if salt.loader.pyximport is not None:
  1471. assert ".pyx" in loader.suffix_map
  1472. assert ".pyx" in loader.suffix_order
  1473. finally:
  1474. self.opts["cython_enable"] = False
  1475. loader = self.__init_loader()
  1476. assert ".pyx" not in loader.suffix_map
  1477. assert ".pyx" not in loader.suffix_order