test_loader.py 50 KB

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