paths.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. # -*- coding: utf-8 -*-
  2. '''
  3. :codeauthor: Pedro Algarvio (pedro@algarvio.me)
  4. :copyright: Copyright 2017 by the SaltStack Team, see AUTHORS for more details.
  5. :license: Apache 2.0, see LICENSE for more details.
  6. tests.support.paths
  7. ~~~~~~~~~~~~~~~~~~~
  8. Tests related paths
  9. '''
  10. # Import python libs
  11. from __future__ import absolute_import
  12. import os
  13. import re
  14. import sys
  15. import stat
  16. import logging
  17. import tempfile
  18. import textwrap
  19. import salt.utils.path
  20. log = logging.getLogger(__name__)
  21. TESTS_DIR = os.path.dirname(os.path.dirname(os.path.normpath(os.path.abspath(__file__))))
  22. if TESTS_DIR.startswith('//'):
  23. # Have we been given an initial double forward slash? Ditch it!
  24. TESTS_DIR = TESTS_DIR[1:]
  25. if sys.platform.startswith('win'):
  26. TESTS_DIR = os.path.normcase(TESTS_DIR)
  27. CODE_DIR = os.path.dirname(TESTS_DIR)
  28. if sys.platform.startswith('win'):
  29. CODE_DIR = CODE_DIR.replace('\\', '\\\\')
  30. UNIT_TEST_DIR = os.path.join(TESTS_DIR, 'unit')
  31. INTEGRATION_TEST_DIR = os.path.join(TESTS_DIR, 'integration')
  32. MULTIMASTER_TEST_DIR = os.path.join(TESTS_DIR, 'multimaster')
  33. # Let's inject CODE_DIR so salt is importable if not there already
  34. if TESTS_DIR in sys.path:
  35. sys.path.remove(TESTS_DIR)
  36. if CODE_DIR in sys.path and sys.path[0] != CODE_DIR:
  37. sys.path.remove(CODE_DIR)
  38. if CODE_DIR not in sys.path:
  39. sys.path.insert(0, CODE_DIR)
  40. if TESTS_DIR not in sys.path:
  41. sys.path.insert(1, TESTS_DIR)
  42. SYS_TMP_DIR = os.path.abspath(os.path.realpath(
  43. # Avoid ${TMPDIR} and gettempdir() on MacOS as they yield a base path too long
  44. # for unix sockets: ``error: AF_UNIX path too long``
  45. # Gentoo Portage prefers ebuild tests are rooted in ${TMPDIR}
  46. os.environ.get('TMPDIR', tempfile.gettempdir()) if not sys.platform.startswith('darwin') else '/tmp'
  47. ))
  48. TMP = os.path.join(SYS_TMP_DIR, 'salt-tests-tmpdir')
  49. TMP_ROOT_DIR = os.path.join(TMP, 'rootdir')
  50. FILES = os.path.join(INTEGRATION_TEST_DIR, 'files')
  51. BASE_FILES = os.path.join(INTEGRATION_TEST_DIR, 'files', 'file', 'base')
  52. PROD_FILES = os.path.join(INTEGRATION_TEST_DIR, 'files', 'file', 'prod')
  53. PYEXEC = 'python{0}.{1}'.format(*sys.version_info)
  54. MOCKBIN = os.path.join(INTEGRATION_TEST_DIR, 'mockbin')
  55. SCRIPT_DIR = os.path.join(CODE_DIR, 'scripts')
  56. TMP_STATE_TREE = os.path.join(SYS_TMP_DIR, 'salt-temp-state-tree')
  57. TMP_PILLAR_TREE = os.path.join(SYS_TMP_DIR, 'salt-temp-pillar-tree')
  58. TMP_PRODENV_STATE_TREE = os.path.join(SYS_TMP_DIR, 'salt-temp-prodenv-state-tree')
  59. TMP_CONF_DIR = os.path.join(TMP, 'config')
  60. TMP_SUB_MINION_CONF_DIR = os.path.join(TMP_CONF_DIR, 'sub-minion')
  61. TMP_SYNDIC_MINION_CONF_DIR = os.path.join(TMP_CONF_DIR, 'syndic-minion')
  62. TMP_SYNDIC_MASTER_CONF_DIR = os.path.join(TMP_CONF_DIR, 'syndic-master')
  63. TMP_MM_CONF_DIR = os.path.join(TMP_CONF_DIR, 'multimaster')
  64. TMP_MM_SUB_CONF_DIR = os.path.join(TMP_CONF_DIR, 'sub-multimaster')
  65. TMP_PROXY_CONF_DIR = os.path.join(TMP_CONF_DIR, 'proxy')
  66. CONF_DIR = os.path.join(INTEGRATION_TEST_DIR, 'files', 'conf')
  67. PILLAR_DIR = os.path.join(FILES, 'pillar')
  68. TMP_SCRIPT_DIR = os.path.join(TMP, 'scripts')
  69. ENGINES_DIR = os.path.join(FILES, 'engines')
  70. LOG_HANDLERS_DIR = os.path.join(FILES, 'log_handlers')
  71. SCRIPT_TEMPLATES = {
  72. 'salt': [
  73. 'from salt.scripts import salt_main\n',
  74. 'if __name__ == \'__main__\':\n'
  75. ' salt_main()'
  76. ],
  77. 'salt-api': [
  78. 'import salt.cli\n',
  79. 'def main():\n',
  80. ' sapi = salt.cli.SaltAPI()',
  81. ' sapi.start()\n',
  82. 'if __name__ == \'__main__\':',
  83. ' main()'
  84. ],
  85. 'common': [
  86. 'from salt.scripts import salt_{0}\n',
  87. 'import salt.utils.platform\n\n',
  88. 'if __name__ == \'__main__\':\n',
  89. ' if salt.utils.platform.is_windows():\n',
  90. ' import os.path\n',
  91. ' import py_compile\n',
  92. ' cfile = os.path.splitext(__file__)[0] + ".pyc"\n',
  93. ' if not os.path.exists(cfile):\n',
  94. ' py_compile.compile(__file__, cfile)\n',
  95. ' salt_{0}()'
  96. ],
  97. 'coverage': textwrap.dedent(
  98. '''
  99. SITECUSTOMIZE_DIR = os.path.join(CODE_DIR, 'tests', 'support', 'coverage')
  100. COVERAGE_FILE = os.path.join(CODE_DIR, '.coverage')
  101. COVERAGE_PROCESS_START = os.path.join(CODE_DIR, '.coveragerc')
  102. PYTHONPATH = os.environ.get('PYTHONPATH') or None
  103. if PYTHONPATH is None:
  104. PYTHONPATH_ENV_VAR = SITECUSTOMIZE_DIR
  105. else:
  106. PYTHON_PATH_ENTRIES = PYTHONPATH.split(os.pathsep)
  107. if SITECUSTOMIZE_DIR in PYTHON_PATH_ENTRIES:
  108. PYTHON_PATH_ENTRIES.remove(SITECUSTOMIZE_DIR)
  109. PYTHON_PATH_ENTRIES.insert(0, SITECUSTOMIZE_DIR)
  110. PYTHONPATH_ENV_VAR = os.pathsep.join(PYTHON_PATH_ENTRIES)
  111. os.environ['PYTHONPATH'] = PYTHONPATH_ENV_VAR
  112. os.environ['COVERAGE_FILE'] = COVERAGE_FILE
  113. os.environ['COVERAGE_PROCESS_START'] = COVERAGE_PROCESS_START
  114. '''
  115. )
  116. }
  117. def test_mods():
  118. '''
  119. A generator which returns all of the test files
  120. '''
  121. test_re = re.compile(r'^test_.+\.py$')
  122. for dirname in (UNIT_TEST_DIR, INTEGRATION_TEST_DIR, MULTIMASTER_TEST_DIR):
  123. test_type = os.path.basename(dirname)
  124. for root, _, files in salt.utils.path.os_walk(dirname):
  125. parent_mod = root[len(dirname):].lstrip(os.sep).replace(os.sep, '.')
  126. for filename in files:
  127. if test_re.match(filename):
  128. mod_name = test_type
  129. if parent_mod:
  130. mod_name += '.' + parent_mod
  131. mod_name += '.' + filename[:-3]
  132. yield mod_name
  133. class ScriptPathMixin(object):
  134. def get_script_path(self, script_name):
  135. '''
  136. Return the path to a testing runtime script
  137. '''
  138. if not os.path.isdir(TMP_SCRIPT_DIR):
  139. os.makedirs(TMP_SCRIPT_DIR)
  140. script_path = os.path.join(TMP_SCRIPT_DIR,
  141. 'cli_{0}.py'.format(script_name.replace('-', '_')))
  142. if not os.path.isfile(script_path):
  143. log.info('Generating %s', script_path)
  144. # Late import
  145. import salt.utils.files
  146. with salt.utils.files.fopen(script_path, 'w') as sfh:
  147. script_template = SCRIPT_TEMPLATES.get(script_name, None)
  148. if script_template is None:
  149. script_template = SCRIPT_TEMPLATES.get('common', None)
  150. if script_template is None:
  151. raise RuntimeError(
  152. '{0} does not know how to handle the {1} script'.format(
  153. self.__class__.__name__,
  154. script_name
  155. )
  156. )
  157. shebang = sys.executable
  158. if len(shebang) > 128:
  159. # Too long for a shebang, let's use /usr/bin/env and hope
  160. # the right python is picked up
  161. shebang = '/usr/bin/env python'
  162. if 'COVERAGE_PROCESS_START' in os.environ:
  163. coverage_snippet = SCRIPT_TEMPLATES['coverage']
  164. else:
  165. coverage_snippet = ''
  166. sfh.write(
  167. '#!{0}\n\n'.format(shebang) +
  168. 'from __future__ import absolute_import\n'
  169. 'import os\n'
  170. 'import sys\n' +
  171. 'CODE_DIR = r"{0}"\n'.format(CODE_DIR) +
  172. 'if CODE_DIR not in sys.path:\n' +
  173. ' sys.path.insert(0, CODE_DIR)\n' +
  174. coverage_snippet + '\n' +
  175. '\n'.join(script_template).format(script_name.replace('salt-', ''))
  176. )
  177. fst = os.stat(script_path)
  178. os.chmod(script_path, fst.st_mode | stat.S_IEXEC)
  179. log.info('Returning script path %r', script_path)
  180. return script_path