1
0

test_loader.py 51 KB

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