test_config.py 74 KB

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