test_loader.py 46 KB

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