test_loader.py 48 KB

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