test_config.py 70 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725
  1. # -*- coding: utf-8 -*-
  2. '''
  3. Unit tests for salt.config
  4. '''
  5. # Import Python libs
  6. from __future__ import absolute_import, print_function, unicode_literals
  7. import logging
  8. import os
  9. import textwrap
  10. # Import Salt Testing libs
  11. from tests.support.helpers import with_tempdir, with_tempfile, destructiveTest
  12. from tests.support.mixins import AdaptedConfigurationTestCaseMixin
  13. from tests.support.unit import skipIf, TestCase
  14. from tests.support.runtests import RUNTIME_VARS
  15. from tests.support.mock import (
  16. NO_MOCK,
  17. NO_MOCK_REASON,
  18. Mock,
  19. MagicMock,
  20. patch
  21. )
  22. # Import Salt libs
  23. import salt.config
  24. import salt.minion
  25. import salt.syspaths
  26. import salt.utils.files
  27. import salt.utils.network
  28. import salt.utils.platform
  29. import salt.utils.yaml
  30. from salt.ext import six
  31. from salt.syspaths import CONFIG_DIR
  32. from salt import config as sconfig
  33. from salt.exceptions import (
  34. CommandExecutionError,
  35. SaltConfigurationError,
  36. SaltCloudConfigError
  37. )
  38. log = logging.getLogger(__name__)
  39. SAMPLE_CONF_DIR = os.path.dirname(os.path.realpath(__file__)).split('tests')[0] + 'conf/'
  40. # mock hostname should be more complex than the systems FQDN
  41. MOCK_HOSTNAME = 'very.long.complex.fqdn.that.is.crazy.extra.long.example.com'
  42. MOCK_ETC_HOSTS = textwrap.dedent('''\
  43. ##
  44. # Host Database
  45. #
  46. # localhost is used to configure the loopback interface
  47. # when the system is booting. Do not change this entry.
  48. ## The empty line below must remain, it factors into the tests.
  49. 127.0.0.1 localhost {hostname}
  50. 10.0.0.100 {hostname}
  51. 200.200.200.2 other.host.alias.com
  52. ::1 ip6-localhost ip6-loopback
  53. fe00::0 ip6-localnet
  54. ff00::0 ip6-mcastprefix
  55. '''.format(hostname=MOCK_HOSTNAME))
  56. MOCK_ETC_HOSTNAME = '{0}\n'.format(MOCK_HOSTNAME)
  57. PATH = 'path/to/some/cloud/conf/file'
  58. DEFAULT = {'default_include': PATH}
  59. MOCK_MASTER_DEFAULT_OPTS = {
  60. 'log_file': '{0}/var/log/salt/master'.format(salt.syspaths.ROOT_DIR),
  61. 'pidfile': '{0}/var/run/salt-master.pid'.format(salt.syspaths.ROOT_DIR),
  62. 'root_dir': format(salt.syspaths.ROOT_DIR)
  63. }
  64. if salt.utils.platform.is_windows():
  65. MOCK_MASTER_DEFAULT_OPTS = {
  66. 'log_file': '{0}\\var\\log\\salt\\master'.format(
  67. salt.syspaths.ROOT_DIR),
  68. 'pidfile': '{0}\\var\\run\\salt-master.pid'.format(
  69. salt.syspaths.ROOT_DIR),
  70. 'root_dir': format(salt.syspaths.ROOT_DIR)
  71. }
  72. class SampleConfTest(TestCase):
  73. '''
  74. Validate files in the salt/conf directory.
  75. '''
  76. def test_conf_master_sample_is_commented(self):
  77. '''
  78. The sample config file located in salt/conf/master must be completely
  79. commented out. This test checks for any lines that are not commented or blank.
  80. '''
  81. master_config = SAMPLE_CONF_DIR + 'master'
  82. ret = salt.config._read_conf_file(master_config)
  83. self.assertEqual(
  84. ret,
  85. {},
  86. 'Sample config file \'{0}\' must be commented out.'.format(
  87. master_config
  88. )
  89. )
  90. def test_conf_minion_sample_is_commented(self):
  91. '''
  92. The sample config file located in salt/conf/minion must be completely
  93. commented out. This test checks for any lines that are not commented or blank.
  94. '''
  95. minion_config = SAMPLE_CONF_DIR + 'minion'
  96. ret = salt.config._read_conf_file(minion_config)
  97. self.assertEqual(
  98. ret,
  99. {},
  100. 'Sample config file \'{0}\' must be commented out.'.format(
  101. minion_config
  102. )
  103. )
  104. def test_conf_cloud_sample_is_commented(self):
  105. '''
  106. The sample config file located in salt/conf/cloud must be completely
  107. commented out. This test checks for any lines that are not commented or blank.
  108. '''
  109. cloud_config = SAMPLE_CONF_DIR + 'cloud'
  110. ret = salt.config._read_conf_file(cloud_config)
  111. self.assertEqual(
  112. ret,
  113. {},
  114. 'Sample config file \'{0}\' must be commented out.'.format(
  115. cloud_config
  116. )
  117. )
  118. def test_conf_cloud_profiles_sample_is_commented(self):
  119. '''
  120. The sample config file located in salt/conf/cloud.profiles must be completely
  121. commented out. This test checks for any lines that are not commented or blank.
  122. '''
  123. cloud_profiles_config = SAMPLE_CONF_DIR + 'cloud.profiles'
  124. ret = salt.config._read_conf_file(cloud_profiles_config)
  125. self.assertEqual(
  126. ret,
  127. {},
  128. 'Sample config file \'{0}\' must be commented out.'.format(
  129. cloud_profiles_config
  130. )
  131. )
  132. def test_conf_cloud_providers_sample_is_commented(self):
  133. '''
  134. The sample config file located in salt/conf/cloud.providers must be completely
  135. commented out. This test checks for any lines that are not commented or blank.
  136. '''
  137. cloud_providers_config = SAMPLE_CONF_DIR + 'cloud.providers'
  138. ret = salt.config._read_conf_file(cloud_providers_config)
  139. self.assertEqual(
  140. ret,
  141. {},
  142. 'Sample config file \'{0}\' must be commented out.'.format(
  143. cloud_providers_config
  144. )
  145. )
  146. def test_conf_proxy_sample_is_commented(self):
  147. '''
  148. The sample config file located in salt/conf/proxy must be completely
  149. commented out. This test checks for any lines that are not commented or blank.
  150. '''
  151. proxy_config = SAMPLE_CONF_DIR + 'proxy'
  152. ret = salt.config._read_conf_file(proxy_config)
  153. self.assertEqual(
  154. ret,
  155. {},
  156. 'Sample config file \'{0}\' must be commented out.'.format(
  157. proxy_config
  158. )
  159. )
  160. def test_conf_roster_sample_is_commented(self):
  161. '''
  162. The sample config file located in salt/conf/roster must be completely
  163. commented out. This test checks for any lines that are not commented or blank.
  164. '''
  165. roster_config = SAMPLE_CONF_DIR + 'roster'
  166. ret = salt.config._read_conf_file(roster_config)
  167. self.assertEqual(
  168. ret,
  169. {},
  170. 'Sample config file \'{0}\' must be commented out.'.format(
  171. roster_config
  172. )
  173. )
  174. def test_conf_cloud_profiles_d_files_are_commented(self):
  175. '''
  176. All cloud profile sample configs in salt/conf/cloud.profiles.d/* must be completely
  177. commented out. This test loops through all of the files in that directory to check
  178. for any lines that are not commented or blank.
  179. '''
  180. cloud_sample_dir = SAMPLE_CONF_DIR + 'cloud.profiles.d/'
  181. if not os.path.exists(cloud_sample_dir):
  182. self.skipTest("Sample config directory '{}' is missing.".format(cloud_sample_dir))
  183. cloud_sample_files = os.listdir(cloud_sample_dir)
  184. for conf_file in cloud_sample_files:
  185. profile_conf = cloud_sample_dir + conf_file
  186. ret = salt.config._read_conf_file(profile_conf)
  187. self.assertEqual(
  188. ret,
  189. {},
  190. 'Sample config file \'{0}\' must be commented out.'.format(
  191. conf_file
  192. )
  193. )
  194. def test_conf_cloud_providers_d_files_are_commented(self):
  195. '''
  196. All cloud profile sample configs in salt/conf/cloud.providers.d/* must be completely
  197. commented out. This test loops through all of the files in that directory to check
  198. for any lines that are not commented or blank.
  199. '''
  200. cloud_sample_dir = SAMPLE_CONF_DIR + 'cloud.providers.d/'
  201. if not os.path.exists(cloud_sample_dir):
  202. self.skipTest("Sample config directory '{}' is missing.".format(cloud_sample_dir))
  203. cloud_sample_files = os.listdir(cloud_sample_dir)
  204. for conf_file in cloud_sample_files:
  205. provider_conf = cloud_sample_dir + conf_file
  206. ret = salt.config._read_conf_file(provider_conf)
  207. self.assertEqual(
  208. ret,
  209. {},
  210. 'Sample config file \'{0}\' must be commented out.'.format(
  211. conf_file
  212. )
  213. )
  214. def test_conf_cloud_maps_d_files_are_commented(self):
  215. '''
  216. All cloud profile sample configs in salt/conf/cloud.maps.d/* must be completely
  217. commented out. This test loops through all of the files in that directory to check
  218. for any lines that are not commented or blank.
  219. '''
  220. cloud_sample_dir = SAMPLE_CONF_DIR + 'cloud.maps.d/'
  221. if not os.path.exists(cloud_sample_dir):
  222. self.skipTest("Sample config directory '{}' is missing.".format(cloud_sample_dir))
  223. cloud_sample_files = os.listdir(cloud_sample_dir)
  224. for conf_file in cloud_sample_files:
  225. map_conf = cloud_sample_dir + conf_file
  226. ret = salt.config._read_conf_file(map_conf)
  227. self.assertEqual(
  228. ret,
  229. {},
  230. 'Sample config file \'{0}\' must be commented out.'.format(
  231. conf_file
  232. )
  233. )
  234. def _unhandled_mock_read(filename):
  235. '''
  236. Raise an error because we should not be calling salt.utils.files.fopen()
  237. '''
  238. raise CommandExecutionError('Unhandled mock read for {0}'.format(filename))
  239. def _salt_configuration_error(filename):
  240. '''
  241. Raise an error to indicate error in the Salt configuration file
  242. '''
  243. raise SaltConfigurationError('Configuration error in {0}'.format(filename))
  244. class ConfigTestCase(TestCase, AdaptedConfigurationTestCaseMixin):
  245. @with_tempfile()
  246. def test_sha256_is_default_for_master(self, fpath):
  247. with salt.utils.files.fopen(fpath, 'w') as wfh:
  248. wfh.write(
  249. "root_dir: /\n"
  250. "key_logfile: key\n"
  251. )
  252. config = sconfig.master_config(fpath)
  253. self.assertEqual(config['hash_type'], 'sha256')
  254. @with_tempfile()
  255. def test_sha256_is_default_for_minion(self, fpath):
  256. with salt.utils.files.fopen(fpath, 'w') as wfh:
  257. wfh.write(
  258. "root_dir: /\n"
  259. "key_logfile: key\n"
  260. )
  261. config = sconfig.minion_config(fpath)
  262. self.assertEqual(config['hash_type'], 'sha256')
  263. @with_tempfile()
  264. def test_proper_path_joining(self, fpath):
  265. temp_config = 'root_dir: /\n'\
  266. 'key_logfile: key\n'
  267. if salt.utils.platform.is_windows():
  268. temp_config = 'root_dir: c:\\\n'\
  269. 'key_logfile: key\n'
  270. with salt.utils.files.fopen(fpath, 'w') as fp_:
  271. fp_.write(temp_config)
  272. config = sconfig.master_config(fpath)
  273. expect_path_join = os.path.join('/', 'key')
  274. expect_sep_join = '//key'
  275. if salt.utils.platform.is_windows():
  276. expect_path_join = os.path.join('c:\\', 'key')
  277. expect_sep_join = 'c:\\\\key'
  278. # os.path.join behavior
  279. self.assertEqual(config['key_logfile'], expect_path_join)
  280. # os.sep.join behavior
  281. self.assertNotEqual(config['key_logfile'], expect_sep_join)
  282. @with_tempdir()
  283. def test_common_prefix_stripping(self, tempdir):
  284. root_dir = os.path.join(tempdir, 'foo', 'bar')
  285. os.makedirs(root_dir)
  286. fpath = os.path.join(root_dir, 'config')
  287. with salt.utils.files.fopen(fpath, 'w') as fp_:
  288. fp_.write(
  289. 'root_dir: {0}\n'
  290. 'log_file: {1}\n'.format(root_dir, fpath)
  291. )
  292. config = sconfig.master_config(fpath)
  293. self.assertEqual(config['log_file'], fpath)
  294. @with_tempdir()
  295. def test_default_root_dir_included_in_config_root_dir(self, tempdir):
  296. root_dir = os.path.join(tempdir, 'foo', 'bar')
  297. os.makedirs(root_dir)
  298. fpath = os.path.join(root_dir, 'config')
  299. with salt.utils.files.fopen(fpath, 'w') as fp_:
  300. fp_.write(
  301. 'root_dir: {0}\n'
  302. 'log_file: {1}\n'.format(root_dir, fpath)
  303. )
  304. with patch('salt.syspaths.ROOT_DIR', RUNTIME_VARS.TMP):
  305. config = salt.config.master_config(fpath)
  306. self.assertEqual(config['log_file'], fpath)
  307. @skipIf(
  308. salt.utils.platform.is_windows(),
  309. 'You can\'t set an environment dynamically in Windows')
  310. @with_tempdir()
  311. def test_load_master_config_from_environ_var(self, tempdir):
  312. original_environ = os.environ.copy()
  313. env_root_dir = os.path.join(tempdir, 'foo', 'env')
  314. os.makedirs(env_root_dir)
  315. env_fpath = os.path.join(env_root_dir, 'config-env')
  316. with salt.utils.files.fopen(env_fpath, 'w') as fp_:
  317. fp_.write(
  318. 'root_dir: {0}\n'
  319. 'log_file: {1}\n'.format(env_root_dir, env_fpath)
  320. )
  321. os.environ['SALT_MASTER_CONFIG'] = env_fpath
  322. # Should load from env variable, not the default configuration file.
  323. config = sconfig.master_config('{0}/master'.format(CONFIG_DIR))
  324. self.assertEqual(config['log_file'], env_fpath)
  325. os.environ.clear()
  326. os.environ.update(original_environ)
  327. root_dir = os.path.join(tempdir, 'foo', 'bar')
  328. os.makedirs(root_dir)
  329. fpath = os.path.join(root_dir, 'config')
  330. with salt.utils.files.fopen(fpath, 'w') as fp_:
  331. fp_.write(
  332. 'root_dir: {0}\n'
  333. 'log_file: {1}\n'.format(root_dir, fpath)
  334. )
  335. # Let's set the environment variable, yet, since the configuration
  336. # file path is not the default one, i.e., the user has passed an
  337. # alternative configuration file form the CLI parser, the
  338. # environment variable will be ignored.
  339. os.environ['SALT_MASTER_CONFIG'] = env_fpath
  340. config = sconfig.master_config(fpath)
  341. self.assertEqual(config['log_file'], fpath)
  342. os.environ.clear()
  343. os.environ.update(original_environ)
  344. @skipIf(
  345. salt.utils.platform.is_windows(),
  346. 'You can\'t set an environment dynamically in Windows')
  347. @with_tempdir()
  348. def test_load_minion_config_from_environ_var(self, tempdir):
  349. original_environ = os.environ.copy()
  350. env_root_dir = os.path.join(tempdir, 'foo', 'env')
  351. os.makedirs(env_root_dir)
  352. env_fpath = os.path.join(env_root_dir, 'config-env')
  353. with salt.utils.files.fopen(env_fpath, 'w') as fp_:
  354. fp_.write(
  355. 'root_dir: {0}\n'
  356. 'log_file: {1}\n'.format(env_root_dir, env_fpath)
  357. )
  358. os.environ['SALT_MINION_CONFIG'] = env_fpath
  359. # Should load from env variable, not the default configuration file
  360. config = sconfig.minion_config('{0}/minion'.format(CONFIG_DIR))
  361. self.assertEqual(config['log_file'], env_fpath)
  362. os.environ.clear()
  363. os.environ.update(original_environ)
  364. root_dir = os.path.join(tempdir, 'foo', 'bar')
  365. os.makedirs(root_dir)
  366. fpath = os.path.join(root_dir, 'config')
  367. with salt.utils.files.fopen(fpath, 'w') as fp_:
  368. fp_.write(
  369. 'root_dir: {0}\n'
  370. 'log_file: {1}\n'.format(root_dir, fpath)
  371. )
  372. # Let's set the environment variable, yet, since the configuration
  373. # file path is not the default one, i.e., the user has passed an
  374. # alternative configuration file form the CLI parser, the
  375. # environment variable will be ignored.
  376. os.environ['SALT_MINION_CONFIG'] = env_fpath
  377. config = sconfig.minion_config(fpath)
  378. self.assertEqual(config['log_file'], fpath)
  379. os.environ.clear()
  380. os.environ.update(original_environ)
  381. @with_tempdir()
  382. def test_load_client_config_from_environ_var(self, tempdir):
  383. original_environ = os.environ.copy()
  384. env_root_dir = os.path.join(tempdir, 'foo', 'env')
  385. os.makedirs(env_root_dir)
  386. # Let's populate a master configuration file which should not get
  387. # picked up since the client configuration tries to load the master
  388. # configuration settings using the provided client configuration
  389. # file
  390. master_config = os.path.join(env_root_dir, 'master')
  391. with salt.utils.files.fopen(master_config, 'w') as fp_:
  392. fp_.write(
  393. 'blah: true\n'
  394. 'root_dir: {0}\n'
  395. 'log_file: {1}\n'.format(env_root_dir, master_config)
  396. )
  397. os.environ['SALT_MASTER_CONFIG'] = master_config
  398. # Now the client configuration file
  399. env_fpath = os.path.join(env_root_dir, 'config-env')
  400. with salt.utils.files.fopen(env_fpath, 'w') as fp_:
  401. fp_.write(
  402. 'root_dir: {0}\n'
  403. 'log_file: {1}\n'.format(env_root_dir, env_fpath)
  404. )
  405. os.environ['SALT_CLIENT_CONFIG'] = env_fpath
  406. # Should load from env variable, not the default configuration file
  407. config = sconfig.client_config(os.path.expanduser('~/.salt'))
  408. self.assertEqual(config['log_file'], env_fpath)
  409. self.assertTrue('blah' not in config)
  410. os.environ.clear()
  411. os.environ.update(original_environ)
  412. root_dir = os.path.join(tempdir, 'foo', 'bar')
  413. os.makedirs(root_dir)
  414. fpath = os.path.join(root_dir, 'config')
  415. with salt.utils.files.fopen(fpath, 'w') as fp_:
  416. fp_.write(
  417. 'root_dir: {0}\n'
  418. 'log_file: {1}\n'.format(root_dir, fpath)
  419. )
  420. # Let's set the environment variable, yet, since the configuration
  421. # file path is not the default one, i.e., the user has passed an
  422. # alternative configuration file form the CLI parser, the
  423. # environment variable will be ignored.
  424. os.environ['SALT_MASTER_CONFIG'] = env_fpath
  425. config = sconfig.master_config(fpath)
  426. self.assertEqual(config['log_file'], fpath)
  427. os.environ.clear()
  428. os.environ.update(original_environ)
  429. @with_tempdir()
  430. def test_issue_5970_minion_confd_inclusion(self, tempdir):
  431. minion_config = os.path.join(tempdir, 'minion')
  432. minion_confd = os.path.join(tempdir, 'minion.d')
  433. os.makedirs(minion_confd)
  434. # Let's populate a minion configuration file with some basic
  435. # settings
  436. with salt.utils.files.fopen(minion_config, 'w') as fp_:
  437. fp_.write(
  438. 'blah: false\n'
  439. 'root_dir: {0}\n'
  440. 'log_file: {1}\n'.format(tempdir, minion_config)
  441. )
  442. # Now, let's populate an extra configuration file under minion.d
  443. # Notice that above we've set blah as False and below as True.
  444. # Since the minion.d files are loaded after the main configuration
  445. # file so overrides can happen, the final value of blah should be
  446. # True.
  447. extra_config = os.path.join(minion_confd, 'extra.conf')
  448. with salt.utils.files.fopen(extra_config, 'w') as fp_:
  449. fp_.write('blah: true\n')
  450. # Let's load the configuration
  451. config = sconfig.minion_config(minion_config)
  452. self.assertEqual(config['log_file'], minion_config)
  453. # As proven by the assertion below, blah is True
  454. self.assertTrue(config['blah'])
  455. @with_tempdir()
  456. def test_master_confd_inclusion(self, tempdir):
  457. master_config = os.path.join(tempdir, 'master')
  458. master_confd = os.path.join(tempdir, 'master.d')
  459. os.makedirs(master_confd)
  460. # Let's populate a master configuration file with some basic
  461. # settings
  462. with salt.utils.files.fopen(master_config, 'w') as fp_:
  463. fp_.write(
  464. 'blah: false\n'
  465. 'root_dir: {0}\n'
  466. 'log_file: {1}\n'.format(tempdir, master_config)
  467. )
  468. # Now, let's populate an extra configuration file under master.d
  469. # Notice that above we've set blah as False and below as True.
  470. # Since the master.d files are loaded after the main configuration
  471. # file so overrides can happen, the final value of blah should be
  472. # True.
  473. extra_config = os.path.join(master_confd, 'extra.conf')
  474. with salt.utils.files.fopen(extra_config, 'w') as fp_:
  475. fp_.write('blah: true\n')
  476. # Let's load the configuration
  477. config = sconfig.master_config(master_config)
  478. self.assertEqual(config['log_file'], master_config)
  479. # As proven by the assertion below, blah is True
  480. self.assertTrue(config['blah'])
  481. @with_tempfile()
  482. @with_tempdir()
  483. def test_master_file_roots_glob(self, tempdir, fpath):
  484. # Create some files
  485. for f in 'abc':
  486. fpath = os.path.join(tempdir, f)
  487. with salt.utils.files.fopen(fpath, 'w') as wfh:
  488. wfh.write(f)
  489. with salt.utils.files.fopen(fpath, 'w') as wfh:
  490. wfh.write(
  491. 'file_roots:\n'
  492. ' base:\n'
  493. ' - {0}'.format(os.path.join(tempdir, '*'))
  494. )
  495. config = sconfig.master_config(fpath)
  496. base = config['file_roots']['base']
  497. self.assertEqual(set(base), set([
  498. os.path.join(tempdir, 'a'),
  499. os.path.join(tempdir, 'b'),
  500. os.path.join(tempdir, 'c')
  501. ]))
  502. def test_validate_bad_file_roots(self):
  503. expected = salt.config._expand_glob_path(
  504. [salt.syspaths.BASE_FILE_ROOTS_DIR]
  505. )
  506. with patch('salt.config._normalize_roots') as mk:
  507. ret = salt.config._validate_file_roots(None)
  508. assert not mk.called
  509. assert ret == {'base': expected}
  510. @with_tempfile()
  511. @with_tempdir()
  512. def test_master_pillar_roots_glob(self, tempdir, fpath):
  513. # Create some files.
  514. for f in 'abc':
  515. fpath = os.path.join(tempdir, f)
  516. with salt.utils.files.fopen(fpath, 'w') as wfh:
  517. wfh.write(f)
  518. with salt.utils.files.fopen(fpath, 'w') as wfh:
  519. wfh.write(
  520. 'pillar_roots:\n'
  521. ' base:\n'
  522. ' - {0}'.format(os.path.join(tempdir, '*'))
  523. )
  524. config = sconfig.master_config(fpath)
  525. base = config['pillar_roots']['base']
  526. self.assertEqual(set(base), set([
  527. os.path.join(tempdir, 'a'),
  528. os.path.join(tempdir, 'b'),
  529. os.path.join(tempdir, 'c')
  530. ]))
  531. def test_validate_bad_pillar_roots(self):
  532. expected = salt.config._expand_glob_path(
  533. [salt.syspaths.BASE_PILLAR_ROOTS_DIR]
  534. )
  535. with patch('salt.config._normalize_roots') as mk:
  536. ret = salt.config._validate_pillar_roots(None)
  537. assert not mk.called
  538. assert ret == {'base': expected}
  539. @with_tempdir()
  540. def test_master_id_function(self, tempdir):
  541. master_config = os.path.join(tempdir, 'master')
  542. with salt.utils.files.fopen(master_config, 'w') as fp_:
  543. fp_.write(
  544. 'id_function:\n'
  545. ' test.echo:\n'
  546. ' text: hello_world\n'
  547. 'root_dir: {0}\n'
  548. 'log_file: {1}\n'.format(tempdir, master_config)
  549. )
  550. # Let's load the configuration
  551. config = sconfig.master_config(master_config)
  552. self.assertEqual(config['log_file'], master_config)
  553. # 'master_config' appends '_master' to the ID
  554. self.assertEqual(config['id'], 'hello_world_master')
  555. @with_tempfile()
  556. @with_tempdir()
  557. def test_minion_file_roots_glob(self, tempdir, fpath):
  558. # Create some files.
  559. for f in 'abc':
  560. fpath = os.path.join(tempdir, f)
  561. with salt.utils.files.fopen(fpath, 'w') as wfh:
  562. wfh.write(f)
  563. with salt.utils.files.fopen(fpath, 'w') as wfh:
  564. wfh.write(
  565. 'file_roots:\n'
  566. ' base:\n'
  567. ' - {0}'.format(os.path.join(tempdir, '*'))
  568. )
  569. config = sconfig.minion_config(fpath)
  570. base = config['file_roots']['base']
  571. self.assertEqual(set(base), set([
  572. os.path.join(tempdir, 'a'),
  573. os.path.join(tempdir, 'b'),
  574. os.path.join(tempdir, 'c')
  575. ]))
  576. @with_tempfile()
  577. @with_tempdir()
  578. def test_minion_pillar_roots_glob(self, tempdir, fpath):
  579. # Create some files.
  580. for f in 'abc':
  581. fpath = os.path.join(tempdir, f)
  582. with salt.utils.files.fopen(fpath, 'w') as wfh:
  583. wfh.write(f)
  584. with salt.utils.files.fopen(fpath, 'w') as wfh:
  585. wfh.write(
  586. 'pillar_roots:\n'
  587. ' base:\n'
  588. ' - {0}'.format(os.path.join(tempdir, '*'))
  589. )
  590. config = sconfig.minion_config(fpath)
  591. base = config['pillar_roots']['base']
  592. self.assertEqual(set(base), set([
  593. os.path.join(tempdir, 'a'),
  594. os.path.join(tempdir, 'b'),
  595. os.path.join(tempdir, 'c')
  596. ]))
  597. @with_tempdir()
  598. def test_minion_id_function(self, tempdir):
  599. minion_config = os.path.join(tempdir, 'minion')
  600. with salt.utils.files.fopen(minion_config, 'w') as fp_:
  601. fp_.write(
  602. 'id_function:\n'
  603. ' test.echo:\n'
  604. ' text: hello_world\n'
  605. 'root_dir: {0}\n'
  606. 'log_file: {1}\n'.format(tempdir, minion_config)
  607. )
  608. # Let's load the configuration
  609. config = sconfig.minion_config(minion_config)
  610. self.assertEqual(config['log_file'], minion_config)
  611. self.assertEqual(config['id'], 'hello_world')
  612. @with_tempdir()
  613. def test_minion_id_lowercase(self, tempdir):
  614. '''
  615. This tests that setting `minion_id_lowercase: True` does lower case
  616. the minion id. Lowercase does not operate on a static `id: KING_BOB`
  617. setting, or a cached id.
  618. '''
  619. minion_config = os.path.join(tempdir, 'minion')
  620. with salt.utils.files.fopen(minion_config, 'w') as fp_:
  621. fp_.write(textwrap.dedent('''\
  622. id_function:
  623. test.echo:
  624. text: KING_BOB
  625. minion_id_caching: False
  626. minion_id_lowercase: True
  627. '''))
  628. config = sconfig.minion_config(minion_config) # Load the configuration
  629. self.assertEqual(config['minion_id_caching'], False) # Check the configuration
  630. self.assertEqual(config['minion_id_lowercase'], True) # Check the configuration
  631. self.assertEqual(config['id'], 'king_bob')
  632. @with_tempdir()
  633. def test_minion_id_remove_domain_string_positive(self, tempdir):
  634. '''
  635. This tests that the values of `minion_id_remove_domain` is suppressed from a generated minion id,
  636. effectivly generating a hostname minion_id.
  637. '''
  638. minion_config = os.path.join(tempdir, 'minion')
  639. with salt.utils.files.fopen(minion_config, 'w') as fp_:
  640. fp_.write(textwrap.dedent('''\
  641. id_function:
  642. test.echo:
  643. text: king_bob.foo.org
  644. minion_id_remove_domain: foo.org
  645. minion_id_caching: False
  646. '''))
  647. # Let's load the configuration
  648. config = sconfig.minion_config(minion_config)
  649. self.assertEqual(config['minion_id_remove_domain'], 'foo.org')
  650. self.assertEqual(config['id'], 'king_bob')
  651. @with_tempdir()
  652. def test_minion_id_remove_domain_string_negative(self, tempdir):
  653. '''
  654. See above
  655. '''
  656. minion_config = os.path.join(tempdir, 'minion')
  657. with salt.utils.files.fopen(minion_config, 'w') as fp_:
  658. fp_.write(textwrap.dedent('''\
  659. id_function:
  660. test.echo:
  661. text: king_bob.foo.org
  662. minion_id_remove_domain: bar.org
  663. minion_id_caching: False
  664. '''))
  665. config = sconfig.minion_config(minion_config)
  666. self.assertEqual(config['id'], 'king_bob.foo.org')
  667. @with_tempdir()
  668. def test_minion_id_remove_domain_bool_true(self, tempdir):
  669. '''
  670. See above
  671. '''
  672. minion_config = os.path.join(tempdir, 'minion')
  673. with salt.utils.files.fopen(minion_config, 'w') as fp_:
  674. fp_.write(textwrap.dedent('''\
  675. id_function:
  676. test.echo:
  677. text: king_bob.foo.org
  678. minion_id_remove_domain: True
  679. minion_id_caching: False
  680. '''))
  681. config = sconfig.minion_config(minion_config)
  682. self.assertEqual(config['id'], 'king_bob')
  683. @with_tempdir()
  684. def test_minion_id_remove_domain_bool_false(self, tempdir):
  685. '''
  686. See above
  687. '''
  688. minion_config = os.path.join(tempdir, 'minion')
  689. with salt.utils.files.fopen(minion_config, 'w') as fp_:
  690. fp_.write(textwrap.dedent('''\
  691. id_function:
  692. test.echo:
  693. text: king_bob.foo.org
  694. minion_id_remove_domain: False
  695. minion_id_caching: False
  696. '''))
  697. config = sconfig.minion_config(minion_config)
  698. self.assertEqual(config['id'], 'king_bob.foo.org')
  699. @with_tempdir()
  700. def test_backend_rename(self, tempdir):
  701. '''
  702. This tests that we successfully rename git, hg, svn, and minion to
  703. gitfs, hgfs, svnfs, and minionfs in the master and minion opts.
  704. '''
  705. fpath = salt.utils.files.mkstemp(dir=tempdir)
  706. with salt.utils.files.fopen(fpath, 'w') as fp_:
  707. fp_.write(textwrap.dedent('''\
  708. fileserver_backend:
  709. - roots
  710. - git
  711. - hg
  712. - svn
  713. - minion
  714. '''))
  715. master_config = sconfig.master_config(fpath)
  716. minion_config = sconfig.minion_config(fpath)
  717. expected = ['roots', 'gitfs', 'hgfs', 'svnfs', 'minionfs']
  718. self.assertEqual(master_config['fileserver_backend'], expected)
  719. self.assertEqual(minion_config['fileserver_backend'], expected)
  720. def test_syndic_config(self):
  721. syndic_conf_path = self.get_config_file_path('syndic')
  722. minion_conf_path = self.get_config_file_path('minion')
  723. syndic_opts = sconfig.syndic_config(
  724. syndic_conf_path, minion_conf_path
  725. )
  726. syndic_opts.update(salt.minion.resolve_dns(syndic_opts))
  727. root_dir = syndic_opts['root_dir']
  728. # id & pki dir are shared & so configured on the minion side
  729. self.assertEqual(syndic_opts['id'], 'minion')
  730. self.assertEqual(syndic_opts['pki_dir'], os.path.join(root_dir, 'pki'))
  731. # the rest is configured master side
  732. self.assertIn(syndic_opts['master_uri'], ['tcp://127.0.0.1:54506', 'tcp://[::1]:54506'])
  733. self.assertEqual(syndic_opts['master_port'], 54506)
  734. self.assertIn(syndic_opts['master_ip'], ['127.0.0.1', '[::1]'])
  735. self.assertEqual(syndic_opts['master'], 'localhost')
  736. self.assertEqual(syndic_opts['sock_dir'], os.path.join(root_dir, 'minion_sock'))
  737. self.assertEqual(syndic_opts['cachedir'], os.path.join(root_dir, 'cache'))
  738. self.assertEqual(syndic_opts['log_file'], os.path.join(root_dir, 'syndic.log'))
  739. self.assertEqual(syndic_opts['pidfile'], os.path.join(root_dir, 'syndic.pid'))
  740. # Show that the options of localclient that repub to local master
  741. # are not merged with syndic ones
  742. self.assertEqual(syndic_opts['_master_conf_file'], minion_conf_path)
  743. self.assertEqual(syndic_opts['_minion_conf_file'], syndic_conf_path)
  744. @with_tempfile()
  745. def _get_tally(self, fpath, conf_func):
  746. '''
  747. This ensures that any strings which are loaded are unicode strings
  748. '''
  749. tally = {}
  750. def _count_strings(config):
  751. if isinstance(config, dict):
  752. for key, val in six.iteritems(config):
  753. log.debug('counting strings in dict key: %s', key)
  754. log.debug('counting strings in dict val: %s', val)
  755. _count_strings(key)
  756. _count_strings(val)
  757. elif isinstance(config, list):
  758. log.debug('counting strings in list: %s', config)
  759. for item in config:
  760. _count_strings(item)
  761. else:
  762. if isinstance(config, six.string_types):
  763. if isinstance(config, six.text_type):
  764. tally['unicode'] = tally.get('unicode', 0) + 1
  765. else:
  766. # We will never reach this on PY3
  767. tally.setdefault('non_unicode', []).append(config)
  768. with salt.utils.files.fopen(fpath, 'w') as wfh:
  769. wfh.write(textwrap.dedent('''
  770. foo: bar
  771. mylist:
  772. - somestring
  773. - 9
  774. - 123.456
  775. - True
  776. - nested:
  777. - key: val
  778. - nestedlist:
  779. - foo
  780. - bar
  781. - baz
  782. mydict:
  783. - somestring: 9
  784. - 123.456: 789
  785. - True: False
  786. - nested:
  787. - key: val
  788. - nestedlist:
  789. - foo
  790. - bar
  791. - baz'''))
  792. if conf_func is sconfig.master_config:
  793. wfh.write('\n\n')
  794. wfh.write(textwrap.dedent('''
  795. rest_cherrypy:
  796. port: 8000
  797. disable_ssl: True
  798. app_path: /beacon_demo
  799. app: /srv/web/html/index.html
  800. static: /srv/web/static'''))
  801. config = conf_func(fpath)
  802. _count_strings(config)
  803. return tally
  804. def test_conf_file_strings_are_unicode_for_master(self):
  805. '''
  806. This ensures that any strings which are loaded are unicode strings
  807. '''
  808. tally = self._get_tally(sconfig.master_config) # pylint: disable=no-value-for-parameter
  809. non_unicode = tally.get('non_unicode', [])
  810. self.assertEqual(len(non_unicode), 8 if six.PY2 else 0, non_unicode)
  811. self.assertTrue(tally['unicode'] > 0)
  812. def test_conf_file_strings_are_unicode_for_minion(self):
  813. '''
  814. This ensures that any strings which are loaded are unicode strings
  815. '''
  816. tally = self._get_tally(sconfig.minion_config) # pylint: disable=no-value-for-parameter
  817. non_unicode = tally.get('non_unicode', [])
  818. self.assertEqual(len(non_unicode), 0, non_unicode)
  819. self.assertTrue(tally['unicode'] > 0)
  820. # <---- Salt Cloud Configuration Tests ---------------------------------------------
  821. # cloud_config tests
  822. @skipIf(NO_MOCK, NO_MOCK_REASON)
  823. def test_cloud_config_double_master_path(self):
  824. '''
  825. Tests passing in master_config_path and master_config kwargs.
  826. '''
  827. with patch('salt.config.load_config', MagicMock(return_value={})):
  828. self.assertRaises(SaltCloudConfigError, sconfig.cloud_config, PATH,
  829. master_config_path='foo', master_config='bar')
  830. @skipIf(NO_MOCK, NO_MOCK_REASON)
  831. def test_cloud_config_double_providers_path(self):
  832. '''
  833. Tests passing in providers_config_path and providers_config kwargs.
  834. '''
  835. with patch('salt.config.load_config', MagicMock(return_value={})):
  836. self.assertRaises(SaltCloudConfigError, sconfig.cloud_config, PATH,
  837. providers_config_path='foo', providers_config='bar')
  838. @skipIf(NO_MOCK, NO_MOCK_REASON)
  839. def test_cloud_config_double_profiles_path(self):
  840. '''
  841. Tests passing in profiles_config_path and profiles_config kwargs.
  842. '''
  843. with patch('salt.config.load_config', MagicMock(return_value={})):
  844. self.assertRaises(SaltCloudConfigError, sconfig.cloud_config, PATH,
  845. profiles_config_path='foo', profiles_config='bar')
  846. @skipIf(NO_MOCK, NO_MOCK_REASON)
  847. def test_cloud_config_providers_in_opts(self):
  848. '''
  849. Tests mixing old cloud providers with pre-configured providers configurations
  850. using the providers_config kwarg
  851. '''
  852. with patch('salt.config.load_config', MagicMock(return_value={})):
  853. with patch('salt.config.apply_cloud_config',
  854. MagicMock(return_value={'providers': 'foo'})):
  855. self.assertRaises(SaltCloudConfigError, sconfig.cloud_config, PATH,
  856. providers_config='bar')
  857. @skipIf(NO_MOCK, NO_MOCK_REASON)
  858. def test_cloud_config_providers_in_opts_path(self):
  859. '''
  860. Tests mixing old cloud providers with pre-configured providers configurations
  861. using the providers_config_path kwarg
  862. '''
  863. with patch('salt.config.load_config', MagicMock(return_value={})):
  864. with patch('salt.config.apply_cloud_config',
  865. MagicMock(return_value={'providers': 'foo'})):
  866. with patch('os.path.isfile', MagicMock(return_value=True)):
  867. self.assertRaises(SaltCloudConfigError, sconfig.cloud_config, PATH,
  868. providers_config_path='bar')
  869. @skipIf(NO_MOCK, NO_MOCK_REASON)
  870. def test_cloud_config_deploy_scripts_search_path(self):
  871. '''
  872. Tests the contents of the 'deploy_scripts_search_path' tuple to ensure that
  873. the correct deploy search paths are present.
  874. There should be two search paths reported in the tuple: ``/etc/salt/cloud.deploy.d``
  875. and ``<path-to-salt-install>/salt/cloud/deploy``. The first element is usually
  876. ``/etc/salt/cloud.deploy.d``, but sometimes is can be something like
  877. ``/etc/local/salt/cloud.deploy.d``, so we'll only test against the last part of
  878. the path.
  879. '''
  880. with patch('os.path.isdir', MagicMock(return_value=True)):
  881. search_paths = sconfig.cloud_config('/etc/salt/cloud').get('deploy_scripts_search_path')
  882. etc_deploy_path = '/salt/cloud.deploy.d'
  883. deploy_path = '/salt/cloud/deploy'
  884. if salt.utils.platform.is_windows():
  885. etc_deploy_path = '/salt\\cloud.deploy.d'
  886. deploy_path = '\\salt\\cloud\\deploy'
  887. # Check cloud.deploy.d path is the first element in the search_paths tuple
  888. self.assertTrue(search_paths[0].endswith(etc_deploy_path))
  889. # Check the second element in the search_paths tuple
  890. self.assertTrue(search_paths[1].endswith(deploy_path))
  891. # apply_cloud_config tests
  892. def test_apply_cloud_config_no_provider_detail_list(self):
  893. '''
  894. Tests when the provider is not contained in a list of details
  895. '''
  896. overrides = {'providers': {'foo': [{'bar': 'baz'}]}}
  897. self.assertRaises(SaltCloudConfigError, sconfig.apply_cloud_config,
  898. overrides, defaults=DEFAULT)
  899. def test_apply_cloud_config_no_provider_detail_dict(self):
  900. '''
  901. Tests when the provider is not contained in the details dictionary
  902. '''
  903. overrides = {'providers': {'foo': {'bar': 'baz'}}}
  904. self.assertRaises(SaltCloudConfigError, sconfig.apply_cloud_config,
  905. overrides, defaults=DEFAULT)
  906. @skipIf(NO_MOCK, NO_MOCK_REASON)
  907. def test_apply_cloud_config_success_list(self):
  908. '''
  909. Tests success when valid data is passed into the function as a list
  910. '''
  911. with patch('salt.config.old_to_new',
  912. MagicMock(return_value={'default_include': 'path/to/some/cloud/conf/file',
  913. 'providers': {
  914. 'foo': {
  915. 'bar': {
  916. 'driver': 'foo:bar'}}}})):
  917. overrides = {'providers': {'foo': [{'driver': 'bar'}]}}
  918. ret = {'default_include': 'path/to/some/cloud/conf/file',
  919. 'providers': {'foo': {'bar': {'driver': 'foo:bar'}}}}
  920. self.assertEqual(sconfig.apply_cloud_config(overrides, defaults=DEFAULT), ret)
  921. @skipIf(NO_MOCK, NO_MOCK_REASON)
  922. def test_apply_cloud_config_success_dict(self):
  923. '''
  924. Tests success when valid data is passed into function as a dictionary
  925. '''
  926. with patch('salt.config.old_to_new',
  927. MagicMock(return_value={'default_include': 'path/to/some/cloud/conf/file',
  928. 'providers': {
  929. 'foo': {
  930. 'bar': {
  931. 'driver': 'foo:bar'}}}})):
  932. overrides = {'providers': {'foo': {'driver': 'bar'}}}
  933. ret = {'default_include': 'path/to/some/cloud/conf/file',
  934. 'providers': {'foo': {'bar': {'driver': 'foo:bar'}}}}
  935. self.assertEqual(sconfig.apply_cloud_config(overrides, defaults=DEFAULT), ret)
  936. # apply_vm_profiles_config tests
  937. def test_apply_vm_profiles_config_bad_profile_format(self):
  938. '''
  939. Tests passing in a bad profile format in overrides
  940. '''
  941. overrides = {'foo': 'bar', 'conf_file': PATH}
  942. self.assertRaises(SaltCloudConfigError, sconfig.apply_vm_profiles_config,
  943. PATH, overrides, defaults=DEFAULT)
  944. def test_apply_vm_profiles_config_success(self):
  945. '''
  946. Tests passing in valid provider and profile config files successfully
  947. '''
  948. providers = {'test-provider':
  949. {'digitalocean':
  950. {'driver': 'digitalocean', 'profiles': {}}}}
  951. overrides = {'test-profile':
  952. {'provider': 'test-provider',
  953. 'image': 'Ubuntu 12.10 x64',
  954. 'size': '512MB'},
  955. 'conf_file': PATH}
  956. ret = {'test-profile':
  957. {'profile': 'test-profile',
  958. 'provider': 'test-provider:digitalocean',
  959. 'image': 'Ubuntu 12.10 x64',
  960. 'size': '512MB'}}
  961. self.assertEqual(sconfig.apply_vm_profiles_config(providers,
  962. overrides,
  963. defaults=DEFAULT), ret)
  964. def test_apply_vm_profiles_config_extend_success(self):
  965. '''
  966. Tests profile extends functionality with valid provider and profile configs
  967. '''
  968. providers = {'test-config': {'ec2': {'profiles': {}, 'driver': 'ec2'}}}
  969. overrides = {'Amazon': {'image': 'test-image-1',
  970. 'extends': 'dev-instances'},
  971. 'Fedora': {'image': 'test-image-2',
  972. 'extends': 'dev-instances'},
  973. 'conf_file': PATH,
  974. 'dev-instances': {'ssh_username': 'test_user',
  975. 'provider': 'test-config'}}
  976. ret = {'Amazon': {'profile': 'Amazon',
  977. 'ssh_username': 'test_user',
  978. 'image': 'test-image-1',
  979. 'provider': 'test-config:ec2'},
  980. 'Fedora': {'profile': 'Fedora',
  981. 'ssh_username': 'test_user',
  982. 'image': 'test-image-2',
  983. 'provider': 'test-config:ec2'},
  984. 'dev-instances': {'profile': 'dev-instances',
  985. 'ssh_username': 'test_user',
  986. 'provider': 'test-config:ec2'}}
  987. self.assertEqual(sconfig.apply_vm_profiles_config(providers,
  988. overrides,
  989. defaults=DEFAULT), ret)
  990. def test_apply_vm_profiles_config_extend_override_success(self):
  991. '''
  992. Tests profile extends and recursively merges data elements
  993. '''
  994. self.maxDiff = None
  995. providers = {'test-config': {'ec2': {'profiles': {}, 'driver': 'ec2'}}}
  996. overrides = {'Fedora': {'image': 'test-image-2',
  997. 'extends': 'dev-instances',
  998. 'minion': {'grains': {'stage': 'experimental'}}},
  999. 'conf_file': PATH,
  1000. 'dev-instances': {'ssh_username': 'test_user',
  1001. 'provider': 'test-config',
  1002. 'minion': {'grains': {'role': 'webserver'}}}}
  1003. ret = {'Fedora': {'profile': 'Fedora',
  1004. 'ssh_username': 'test_user',
  1005. 'image': 'test-image-2',
  1006. 'minion': {'grains': {'role': 'webserver',
  1007. 'stage': 'experimental'}},
  1008. 'provider': 'test-config:ec2'},
  1009. 'dev-instances': {'profile': 'dev-instances',
  1010. 'ssh_username': 'test_user',
  1011. 'minion': {'grains': {'role': 'webserver'}},
  1012. 'provider': 'test-config:ec2'}}
  1013. self.assertEqual(sconfig.apply_vm_profiles_config(providers,
  1014. overrides,
  1015. defaults=DEFAULT), ret)
  1016. # apply_cloud_providers_config tests
  1017. def test_apply_cloud_providers_config_same_providers(self):
  1018. '''
  1019. Tests when two providers are given with the same provider name
  1020. '''
  1021. overrides = {'my-dev-envs':
  1022. [{'id': 'ABCDEFGHIJKLMNOP',
  1023. 'key': 'supersecretkeysupersecretkey',
  1024. 'driver': 'ec2'},
  1025. {'apikey': 'abcdefghijklmnopqrstuvwxyz',
  1026. 'password': 'supersecret',
  1027. 'driver': 'ec2'}],
  1028. 'conf_file': PATH}
  1029. self.assertRaises(SaltCloudConfigError,
  1030. sconfig.apply_cloud_providers_config,
  1031. overrides,
  1032. DEFAULT)
  1033. def test_apply_cloud_providers_config_extend(self):
  1034. '''
  1035. Tests the successful extension of a cloud provider
  1036. '''
  1037. overrides = {'my-production-envs':
  1038. [{'extends': 'my-dev-envs:ec2',
  1039. 'location': 'us-east-1',
  1040. 'user': 'ec2-user@mycorp.com'
  1041. }],
  1042. 'my-dev-envs':
  1043. [{'id': 'ABCDEFGHIJKLMNOP',
  1044. 'user': 'user@mycorp.com',
  1045. 'location': 'ap-southeast-1',
  1046. 'key': 'supersecretkeysupersecretkey',
  1047. 'driver': 'ec2'
  1048. },
  1049. {'apikey': 'abcdefghijklmnopqrstuvwxyz',
  1050. 'password': 'supersecret',
  1051. 'driver': 'linode'
  1052. }],
  1053. 'conf_file': PATH}
  1054. ret = {'my-production-envs':
  1055. {'ec2':
  1056. {'profiles': {},
  1057. 'location': 'us-east-1',
  1058. 'key': 'supersecretkeysupersecretkey',
  1059. 'driver': 'ec2',
  1060. 'id': 'ABCDEFGHIJKLMNOP',
  1061. 'user': 'ec2-user@mycorp.com'}},
  1062. 'my-dev-envs':
  1063. {'linode':
  1064. {'apikey': 'abcdefghijklmnopqrstuvwxyz',
  1065. 'password': 'supersecret',
  1066. 'profiles': {},
  1067. 'driver': 'linode'},
  1068. 'ec2':
  1069. {'profiles': {},
  1070. 'location': 'ap-southeast-1',
  1071. 'key': 'supersecretkeysupersecretkey',
  1072. 'driver': 'ec2',
  1073. 'id': 'ABCDEFGHIJKLMNOP',
  1074. 'user': 'user@mycorp.com'}}}
  1075. self.assertEqual(ret,
  1076. sconfig.apply_cloud_providers_config(
  1077. overrides,
  1078. defaults=DEFAULT))
  1079. def test_apply_cloud_providers_config_extend_multiple(self):
  1080. '''
  1081. Tests the successful extension of two cloud providers
  1082. '''
  1083. overrides = {'my-production-envs':
  1084. [{'extends': 'my-dev-envs:ec2',
  1085. 'location': 'us-east-1',
  1086. 'user': 'ec2-user@mycorp.com'},
  1087. {'password': 'new-password',
  1088. 'extends': 'my-dev-envs:linode',
  1089. 'location': 'Salt Lake City'
  1090. }],
  1091. 'my-dev-envs':
  1092. [{'id': 'ABCDEFGHIJKLMNOP',
  1093. 'user': 'user@mycorp.com',
  1094. 'location': 'ap-southeast-1',
  1095. 'key': 'supersecretkeysupersecretkey',
  1096. 'driver': 'ec2'},
  1097. {'apikey': 'abcdefghijklmnopqrstuvwxyz',
  1098. 'password': 'supersecret',
  1099. 'driver': 'linode'}],
  1100. 'conf_file': PATH}
  1101. ret = {'my-production-envs':
  1102. {'linode':
  1103. {'apikey': 'abcdefghijklmnopqrstuvwxyz',
  1104. 'profiles': {},
  1105. 'location': 'Salt Lake City',
  1106. 'driver': 'linode',
  1107. 'password': 'new-password'},
  1108. 'ec2':
  1109. {'user': 'ec2-user@mycorp.com',
  1110. 'key': 'supersecretkeysupersecretkey',
  1111. 'driver': 'ec2',
  1112. 'id': 'ABCDEFGHIJKLMNOP',
  1113. 'profiles': {},
  1114. 'location': 'us-east-1'}},
  1115. 'my-dev-envs':
  1116. {'linode':
  1117. {'apikey': 'abcdefghijklmnopqrstuvwxyz',
  1118. 'password': 'supersecret',
  1119. 'profiles': {},
  1120. 'driver': 'linode'},
  1121. 'ec2':
  1122. {'profiles': {},
  1123. 'user': 'user@mycorp.com',
  1124. 'key': 'supersecretkeysupersecretkey',
  1125. 'driver': 'ec2',
  1126. 'id': 'ABCDEFGHIJKLMNOP',
  1127. 'location': 'ap-southeast-1'}}}
  1128. self.assertEqual(ret, sconfig.apply_cloud_providers_config(
  1129. overrides,
  1130. defaults=DEFAULT))
  1131. def test_apply_cloud_providers_config_extends_bad_alias(self):
  1132. '''
  1133. Tests when the extension contains an alias not found in providers list
  1134. '''
  1135. overrides = {'my-production-envs':
  1136. [{'extends': 'test-alias:ec2',
  1137. 'location': 'us-east-1',
  1138. 'user': 'ec2-user@mycorp.com'}],
  1139. 'my-dev-envs':
  1140. [{'id': 'ABCDEFGHIJKLMNOP',
  1141. 'user': 'user@mycorp.com',
  1142. 'location': 'ap-southeast-1',
  1143. 'key': 'supersecretkeysupersecretkey',
  1144. 'driver': 'ec2'}],
  1145. 'conf_file': PATH}
  1146. self.assertRaises(SaltCloudConfigError,
  1147. sconfig.apply_cloud_providers_config,
  1148. overrides,
  1149. DEFAULT)
  1150. def test_apply_cloud_providers_config_extends_bad_provider(self):
  1151. '''
  1152. Tests when the extension contains a provider not found in providers list
  1153. '''
  1154. overrides = {'my-production-envs':
  1155. [{'extends': 'my-dev-envs:linode',
  1156. 'location': 'us-east-1',
  1157. 'user': 'ec2-user@mycorp.com'}],
  1158. 'my-dev-envs':
  1159. [{'id': 'ABCDEFGHIJKLMNOP',
  1160. 'user': 'user@mycorp.com',
  1161. 'location': 'ap-southeast-1',
  1162. 'key': 'supersecretkeysupersecretkey',
  1163. 'driver': 'ec2'}],
  1164. 'conf_file': PATH}
  1165. self.assertRaises(SaltCloudConfigError,
  1166. sconfig.apply_cloud_providers_config,
  1167. overrides,
  1168. DEFAULT)
  1169. def test_apply_cloud_providers_config_extends_no_provider(self):
  1170. '''
  1171. Tests when no provider is supplied in the extends statement
  1172. '''
  1173. overrides = {'my-production-envs':
  1174. [{'extends': 'my-dev-envs',
  1175. 'location': 'us-east-1',
  1176. 'user': 'ec2-user@mycorp.com'}],
  1177. 'my-dev-envs':
  1178. [{'id': 'ABCDEFGHIJKLMNOP',
  1179. 'user': 'user@mycorp.com',
  1180. 'location': 'ap-southeast-1',
  1181. 'key': 'supersecretkeysupersecretkey',
  1182. 'driver': 'linode'}],
  1183. 'conf_file': PATH}
  1184. self.assertRaises(SaltCloudConfigError,
  1185. sconfig.apply_cloud_providers_config,
  1186. overrides,
  1187. DEFAULT)
  1188. def test_apply_cloud_providers_extends_not_in_providers(self):
  1189. '''
  1190. Tests when extends is not in the list of providers
  1191. '''
  1192. overrides = {'my-production-envs':
  1193. [{'extends': 'my-dev-envs ec2',
  1194. 'location': 'us-east-1',
  1195. 'user': 'ec2-user@mycorp.com'}],
  1196. 'my-dev-envs':
  1197. [{'id': 'ABCDEFGHIJKLMNOP',
  1198. 'user': 'user@mycorp.com',
  1199. 'location': 'ap-southeast-1',
  1200. 'key': 'supersecretkeysupersecretkey',
  1201. 'driver': 'linode'}],
  1202. 'conf_file': PATH}
  1203. self.assertRaises(SaltCloudConfigError,
  1204. sconfig.apply_cloud_providers_config,
  1205. overrides,
  1206. DEFAULT)
  1207. # is_provider_configured tests
  1208. def test_is_provider_configured_no_alias(self):
  1209. '''
  1210. Tests when provider alias is not in opts
  1211. '''
  1212. opts = {'providers': 'test'}
  1213. provider = 'foo:bar'
  1214. self.assertFalse(sconfig.is_provider_configured(opts, provider))
  1215. def test_is_provider_configured_no_driver(self):
  1216. '''
  1217. Tests when provider driver is not in opts
  1218. '''
  1219. opts = {'providers': {'foo': 'baz'}}
  1220. provider = 'foo:bar'
  1221. self.assertFalse(sconfig.is_provider_configured(opts, provider))
  1222. def test_is_provider_configured_key_is_none(self):
  1223. '''
  1224. Tests when a required configuration key is not set
  1225. '''
  1226. opts = {'providers': {'foo': {'bar': {'api_key': None}}}}
  1227. provider = 'foo:bar'
  1228. self.assertFalse(
  1229. sconfig.is_provider_configured(opts,
  1230. provider,
  1231. required_keys=('api_key',)))
  1232. def test_is_provider_configured_success(self):
  1233. '''
  1234. Tests successful cloud provider configuration
  1235. '''
  1236. opts = {'providers': {'foo': {'bar': {'api_key': 'baz'}}}}
  1237. provider = 'foo:bar'
  1238. ret = {'api_key': 'baz'}
  1239. self.assertEqual(
  1240. sconfig.is_provider_configured(opts,
  1241. provider,
  1242. required_keys=('api_key',)), ret)
  1243. def test_is_provider_configured_multiple_driver_not_provider(self):
  1244. '''
  1245. Tests when the drive is not the same as the provider when
  1246. searching through multiple providers
  1247. '''
  1248. opts = {'providers': {'foo': {'bar': {'api_key': 'baz'}}}}
  1249. provider = 'foo'
  1250. self.assertFalse(sconfig.is_provider_configured(opts, provider))
  1251. def test_is_provider_configured_multiple_key_is_none(self):
  1252. '''
  1253. Tests when a required configuration key is not set when
  1254. searching through multiple providers
  1255. '''
  1256. opts = {'providers': {'foo': {'bar': {'api_key': None}}}}
  1257. provider = 'bar'
  1258. self.assertFalse(
  1259. sconfig.is_provider_configured(opts,
  1260. provider,
  1261. required_keys=('api_key',)))
  1262. def test_is_provider_configured_multiple_success(self):
  1263. '''
  1264. Tests successful cloud provider configuration when searching
  1265. through multiple providers
  1266. '''
  1267. opts = {'providers': {'foo': {'bar': {'api_key': 'baz'}}}}
  1268. provider = 'bar'
  1269. ret = {'api_key': 'baz'}
  1270. self.assertEqual(
  1271. sconfig.is_provider_configured(opts,
  1272. provider,
  1273. required_keys=('api_key',)), ret)
  1274. # other cloud configuration tests
  1275. @skipIf(
  1276. salt.utils.platform.is_windows(),
  1277. 'You can\'t set an environment dynamically in Windows')
  1278. @with_tempdir()
  1279. def test_load_cloud_config_from_environ_var(self, tempdir):
  1280. original_environ = os.environ.copy()
  1281. try:
  1282. env_root_dir = os.path.join(tempdir, 'foo', 'env')
  1283. os.makedirs(env_root_dir)
  1284. env_fpath = os.path.join(env_root_dir, 'config-env')
  1285. with salt.utils.files.fopen(env_fpath, 'w') as fp_:
  1286. fp_.write(
  1287. 'root_dir: {0}\n'
  1288. 'log_file: {1}\n'.format(env_root_dir, env_fpath)
  1289. )
  1290. os.environ['SALT_CLOUD_CONFIG'] = env_fpath
  1291. # Should load from env variable, not the default configuration file
  1292. config = sconfig.cloud_config('/etc/salt/cloud')
  1293. self.assertEqual(config['log_file'], env_fpath)
  1294. os.environ.clear()
  1295. os.environ.update(original_environ)
  1296. root_dir = os.path.join(tempdir, 'foo', 'bar')
  1297. os.makedirs(root_dir)
  1298. fpath = os.path.join(root_dir, 'config')
  1299. with salt.utils.files.fopen(fpath, 'w') as fp_:
  1300. fp_.write(
  1301. 'root_dir: {0}\n'
  1302. 'log_file: {1}\n'.format(root_dir, fpath)
  1303. )
  1304. # Let's set the environment variable, yet, since the configuration
  1305. # file path is not the default one, i.e., the user has passed an
  1306. # alternative configuration file form the CLI parser, the
  1307. # environment variable will be ignored.
  1308. os.environ['SALT_CLOUD_CONFIG'] = env_fpath
  1309. config = sconfig.cloud_config(fpath)
  1310. self.assertEqual(config['log_file'], fpath)
  1311. finally:
  1312. # Reset the environ
  1313. os.environ.clear()
  1314. os.environ.update(original_environ)
  1315. @with_tempdir()
  1316. def test_deploy_search_path_as_string(self, temp_conf_dir):
  1317. config_file_path = os.path.join(temp_conf_dir, 'cloud')
  1318. deploy_dir_path = os.path.join(temp_conf_dir, 'test-deploy.d')
  1319. for directory in (temp_conf_dir, deploy_dir_path):
  1320. if not os.path.isdir(directory):
  1321. os.makedirs(directory)
  1322. default_config = sconfig.cloud_config(config_file_path)
  1323. default_config['deploy_scripts_search_path'] = deploy_dir_path
  1324. with salt.utils.files.fopen(config_file_path, 'w') as cfd:
  1325. salt.utils.yaml.safe_dump(default_config, cfd, default_flow_style=False)
  1326. default_config = sconfig.cloud_config(config_file_path)
  1327. # Our custom deploy scripts path was correctly added to the list
  1328. self.assertIn(
  1329. deploy_dir_path,
  1330. default_config['deploy_scripts_search_path']
  1331. )
  1332. # And it's even the first occurrence as it should
  1333. self.assertEqual(
  1334. deploy_dir_path,
  1335. default_config['deploy_scripts_search_path'][0]
  1336. )
  1337. def test_includes_load(self):
  1338. '''
  1339. Tests that cloud.{providers,profiles}.d directories are loaded, even if not
  1340. directly passed in through path
  1341. '''
  1342. config = sconfig.cloud_config(self.get_config_file_path('cloud'))
  1343. self.assertIn('ec2-config', config['providers'])
  1344. self.assertIn('ec2-test', config['profiles'])
  1345. # <---- Salt Cloud Configuration Tests ---------------------------------------------
  1346. @skipIf(NO_MOCK, NO_MOCK_REASON)
  1347. def test_include_config_without_errors(self):
  1348. '''
  1349. Tests that include_config function returns valid configuration
  1350. '''
  1351. include_file = 'minion.d/my.conf'
  1352. config_path = '/etc/salt/minion'
  1353. config_opts = {'id': 'myminion.example.com'}
  1354. with patch('glob.glob', MagicMock(return_value=include_file)):
  1355. with patch('salt.config._read_conf_file', MagicMock(return_value=config_opts)):
  1356. configuration = sconfig.include_config(include_file, config_path, verbose=False)
  1357. self.assertEqual(config_opts, configuration)
  1358. @skipIf(NO_MOCK, NO_MOCK_REASON)
  1359. def test_include_config_with_errors(self):
  1360. '''
  1361. Tests that include_config function returns valid configuration even on errors
  1362. '''
  1363. include_file = 'minion.d/my.conf'
  1364. config_path = '/etc/salt/minion'
  1365. config_opts = {}
  1366. with patch('glob.glob', MagicMock(return_value=include_file)):
  1367. with patch('salt.config._read_conf_file', _salt_configuration_error):
  1368. configuration = sconfig.include_config(include_file, config_path, verbose=False)
  1369. self.assertEqual(config_opts, configuration)
  1370. @skipIf(NO_MOCK, NO_MOCK_REASON)
  1371. def test_include_config_with_errors_exit(self):
  1372. '''
  1373. Tests that include_config exits on errors
  1374. '''
  1375. include_file = 'minion.d/my.conf'
  1376. config_path = '/etc/salt/minion'
  1377. with patch('glob.glob', MagicMock(return_value=include_file)):
  1378. with patch('salt.config._read_conf_file', _salt_configuration_error):
  1379. with self.assertRaises(SystemExit):
  1380. sconfig.include_config(include_file,
  1381. config_path,
  1382. verbose=False,
  1383. exit_on_config_errors=True)
  1384. @staticmethod
  1385. def _get_defaults(**kwargs):
  1386. ret = {
  1387. 'saltenv': kwargs.pop('saltenv', None),
  1388. 'id': 'test',
  1389. 'cachedir': '/A',
  1390. 'sock_dir': '/B',
  1391. 'root_dir': '/C',
  1392. 'fileserver_backend': 'roots',
  1393. 'open_mode': False,
  1394. 'auto_accept': False,
  1395. 'file_roots': {},
  1396. 'pillar_roots': {},
  1397. 'file_ignore_glob': [],
  1398. 'file_ignore_regex': [],
  1399. 'worker_threads': 5,
  1400. 'hash_type': 'sha256',
  1401. 'log_file': 'foo.log',
  1402. }
  1403. ret.update(kwargs)
  1404. return ret
  1405. @skipIf(NO_MOCK, NO_MOCK_REASON)
  1406. def test_apply_config(self):
  1407. '''
  1408. Ensure that the environment and saltenv options work properly
  1409. '''
  1410. with patch.object(sconfig, '_adjust_log_file_override', Mock()), \
  1411. patch.object(sconfig, '_update_ssl_config', Mock()), \
  1412. patch.object(sconfig, '_update_discovery_config', Mock()):
  1413. # MASTER CONFIG
  1414. # Ensure that environment overrides saltenv when saltenv not
  1415. # explicitly passed.
  1416. defaults = self._get_defaults(environment='foo')
  1417. ret = sconfig.apply_master_config(defaults=defaults)
  1418. self.assertEqual(ret['environment'], 'foo')
  1419. self.assertEqual(ret['saltenv'], 'foo')
  1420. # Ensure that environment overrides saltenv when saltenv not
  1421. # explicitly passed.
  1422. defaults = self._get_defaults(environment='foo', saltenv='bar')
  1423. ret = sconfig.apply_master_config(defaults=defaults)
  1424. self.assertEqual(ret['environment'], 'bar')
  1425. self.assertEqual(ret['saltenv'], 'bar')
  1426. # If environment was not explicitly set, it should not be in the
  1427. # opts at all.
  1428. defaults = self._get_defaults()
  1429. ret = sconfig.apply_master_config(defaults=defaults)
  1430. self.assertNotIn('environment', ret)
  1431. self.assertEqual(ret['saltenv'], None)
  1432. # Same test as above but with saltenv explicitly set
  1433. defaults = self._get_defaults(saltenv='foo')
  1434. ret = sconfig.apply_master_config(defaults=defaults)
  1435. self.assertNotIn('environment', ret)
  1436. self.assertEqual(ret['saltenv'], 'foo')
  1437. # MINION CONFIG
  1438. # Ensure that environment overrides saltenv when saltenv not
  1439. # explicitly passed.
  1440. defaults = self._get_defaults(environment='foo')
  1441. ret = sconfig.apply_minion_config(defaults=defaults)
  1442. self.assertEqual(ret['environment'], 'foo')
  1443. self.assertEqual(ret['saltenv'], 'foo')
  1444. # Ensure that environment overrides saltenv when saltenv not
  1445. # explicitly passed.
  1446. defaults = self._get_defaults(environment='foo', saltenv='bar')
  1447. ret = sconfig.apply_minion_config(defaults=defaults)
  1448. self.assertEqual(ret['environment'], 'bar')
  1449. self.assertEqual(ret['saltenv'], 'bar')
  1450. # If environment was not explicitly set, it should not be in the
  1451. # opts at all.
  1452. defaults = self._get_defaults()
  1453. ret = sconfig.apply_minion_config(defaults=defaults)
  1454. self.assertNotIn('environment', ret)
  1455. self.assertEqual(ret['saltenv'], None)
  1456. # Same test as above but with saltenv explicitly set
  1457. defaults = self._get_defaults(saltenv='foo')
  1458. ret = sconfig.apply_minion_config(defaults=defaults)
  1459. self.assertNotIn('environment', ret)
  1460. self.assertEqual(ret['saltenv'], 'foo')
  1461. @skipIf(NO_MOCK, NO_MOCK_REASON)
  1462. class APIConfigTestCase(TestCase):
  1463. '''
  1464. TestCase for the api_config function in salt.config.__init__.py
  1465. '''
  1466. def setUp(self):
  1467. # Copy DEFAULT_API_OPTS to restore after the test
  1468. self.default_api_opts = salt.config.DEFAULT_API_OPTS.copy()
  1469. def tearDown(self):
  1470. # Reset DEFAULT_API_OPTS settings as to not interfere with other unit tests
  1471. salt.config.DEFAULT_API_OPTS = self.default_api_opts
  1472. def test_api_config_log_file_values(self):
  1473. '''
  1474. Tests the opts value of the 'log_file' after running through the
  1475. various default dict updates. 'log_file' should be updated to match
  1476. the DEFAULT_API_OPTS 'api_logfile' value.
  1477. '''
  1478. with patch('salt.config.client_config', MagicMock(return_value=MOCK_MASTER_DEFAULT_OPTS)):
  1479. expected = '{0}/var/log/salt/api'.format(
  1480. salt.syspaths.ROOT_DIR if salt.syspaths.ROOT_DIR != '/' else '')
  1481. if salt.utils.platform.is_windows():
  1482. expected = '{0}\\var\\log\\salt\\api'.format(
  1483. salt.syspaths.ROOT_DIR)
  1484. ret = salt.config.api_config('/some/fake/path')
  1485. self.assertEqual(ret['log_file'], expected)
  1486. def test_api_config_pidfile_values(self):
  1487. '''
  1488. Tests the opts value of the 'pidfile' after running through the
  1489. various default dict updates. 'pidfile' should be updated to match
  1490. the DEFAULT_API_OPTS 'api_pidfile' value.
  1491. '''
  1492. with patch('salt.config.client_config', MagicMock(return_value=MOCK_MASTER_DEFAULT_OPTS)):
  1493. expected = '{0}/var/run/salt-api.pid'.format(
  1494. salt.syspaths.ROOT_DIR if salt.syspaths.ROOT_DIR != '/' else '')
  1495. if salt.utils.platform.is_windows():
  1496. expected = '{0}\\var\\run\\salt-api.pid'.format(
  1497. salt.syspaths.ROOT_DIR)
  1498. ret = salt.config.api_config('/some/fake/path')
  1499. self.assertEqual(ret['pidfile'], expected)
  1500. @destructiveTest
  1501. def test_master_config_file_overrides_defaults(self):
  1502. '''
  1503. Tests the opts value of the api config values after running through the
  1504. various default dict updates that should be overridden by settings in
  1505. the user's master config file.
  1506. '''
  1507. foo_dir = '/foo/bar/baz'
  1508. hello_dir = '/hello/world'
  1509. if salt.utils.platform.is_windows():
  1510. foo_dir = 'c:\\foo\\bar\\baz'
  1511. hello_dir = 'c:\\hello\\world'
  1512. mock_master_config = {
  1513. 'api_pidfile': foo_dir,
  1514. 'api_logfile': hello_dir,
  1515. 'rest_timeout': 5
  1516. }
  1517. mock_master_config.update(MOCK_MASTER_DEFAULT_OPTS.copy())
  1518. with patch('salt.config.client_config',
  1519. MagicMock(return_value=mock_master_config)):
  1520. ret = salt.config.api_config('/some/fake/path')
  1521. self.assertEqual(ret['rest_timeout'], 5)
  1522. self.assertEqual(ret['api_pidfile'], foo_dir)
  1523. self.assertEqual(ret['pidfile'], foo_dir)
  1524. self.assertEqual(ret['api_logfile'], hello_dir)
  1525. self.assertEqual(ret['log_file'], hello_dir)
  1526. @destructiveTest
  1527. def test_api_config_prepend_root_dirs_return(self):
  1528. '''
  1529. Tests the opts value of the api_logfile, log_file, api_pidfile, and pidfile
  1530. when a custom root directory is used. This ensures that each of these
  1531. values is present in the list of opts keys that should have the root_dir
  1532. prepended when the api_config function returns the opts dictionary.
  1533. '''
  1534. mock_log = '/mock/root/var/log/salt/api'
  1535. mock_pid = '/mock/root/var/run/salt-api.pid'
  1536. mock_master_config = MOCK_MASTER_DEFAULT_OPTS.copy()
  1537. mock_master_config['root_dir'] = '/mock/root/'
  1538. if salt.utils.platform.is_windows():
  1539. mock_log = 'c:\\mock\\root\\var\\log\\salt\\api'
  1540. mock_pid = 'c:\\mock\\root\\var\\run\\salt-api.pid'
  1541. mock_master_config['root_dir'] = 'c:\\mock\\root'
  1542. with patch('salt.config.client_config',
  1543. MagicMock(return_value=mock_master_config)):
  1544. ret = salt.config.api_config('/some/fake/path')
  1545. self.assertEqual(ret['api_logfile'], mock_log)
  1546. self.assertEqual(ret['log_file'], mock_log)
  1547. self.assertEqual(ret['api_pidfile'], mock_pid)
  1548. self.assertEqual(ret['pidfile'], mock_pid)