paths.py 7.5 KB

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