setup.py 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. The setup script for salt
  5. """
  6. # pylint: disable=file-perms,ungrouped-imports,wrong-import-order,wrong-import-position,repr-flag-used-in-string
  7. # pylint: disable=3rd-party-local-module-not-gated,resource-leakage,blacklisted-module
  8. # pylint: disable=C0111,E1101,E1103,F0401,W0611,W0201,W0232,R0201,R0902,R0903
  9. # For Python 2.5. A no-op on 2.6 and above.
  10. from __future__ import absolute_import, print_function, with_statement
  11. import distutils.dist
  12. import glob
  13. import inspect
  14. import operator
  15. import os
  16. import platform
  17. import sys
  18. from ctypes.util import find_library
  19. from datetime import datetime
  20. from distutils import log
  21. from distutils.cmd import Command
  22. from distutils.command.build import build
  23. from distutils.command.clean import clean
  24. from distutils.command.install_lib import install_lib
  25. from distutils.errors import DistutilsArgError
  26. from distutils.version import LooseVersion # pylint: disable=blacklisted-module
  27. # pylint: disable=E0611
  28. import setuptools
  29. from setuptools import setup
  30. from setuptools.command.develop import develop
  31. from setuptools.command.egg_info import egg_info
  32. from setuptools.command.install import install
  33. from setuptools.command.sdist import sdist
  34. try:
  35. from urllib2 import urlopen
  36. except ImportError:
  37. from urllib.request import urlopen # pylint: disable=no-name-in-module
  38. try:
  39. from wheel.bdist_wheel import bdist_wheel
  40. HAS_BDIST_WHEEL = True
  41. except ImportError:
  42. HAS_BDIST_WHEEL = False
  43. # pylint: enable=E0611
  44. try:
  45. import zmq
  46. HAS_ZMQ = True
  47. except ImportError:
  48. HAS_ZMQ = False
  49. try:
  50. DATE = datetime.utcfromtimestamp(int(os.environ["SOURCE_DATE_EPOCH"]))
  51. except (KeyError, ValueError):
  52. DATE = datetime.utcnow()
  53. # Change to salt source's directory prior to running any command
  54. try:
  55. SETUP_DIRNAME = os.path.dirname(__file__)
  56. except NameError:
  57. # We're most likely being frozen and __file__ triggered this NameError
  58. # Let's work around that
  59. SETUP_DIRNAME = os.path.dirname(sys.argv[0])
  60. if SETUP_DIRNAME != "":
  61. os.chdir(SETUP_DIRNAME)
  62. SETUP_DIRNAME = os.path.abspath(SETUP_DIRNAME)
  63. BOOTSTRAP_SCRIPT_DISTRIBUTED_VERSION = os.environ.get(
  64. # The user can provide a different bootstrap-script version.
  65. # ATTENTION: A tag for that version MUST exist
  66. "BOOTSTRAP_SCRIPT_VERSION",
  67. # If no bootstrap-script version was provided from the environment, let's
  68. # provide the one we define.
  69. "v2014.06.21",
  70. )
  71. # Store a reference to the executing platform
  72. IS_OSX_PLATFORM = sys.platform.startswith("darwin")
  73. IS_WINDOWS_PLATFORM = sys.platform.startswith("win")
  74. if IS_WINDOWS_PLATFORM or IS_OSX_PLATFORM:
  75. IS_SMARTOS_PLATFORM = False
  76. else:
  77. # os.uname() not available on Windows.
  78. IS_SMARTOS_PLATFORM = os.uname()[0] == "SunOS" and os.uname()[3].startswith(
  79. "joyent_"
  80. )
  81. # Store a reference whether if we're running under Python 3 and above
  82. IS_PY3 = sys.version_info > (3,)
  83. try:
  84. # Add the esky bdist target if the module is available
  85. # may require additional modules depending on platform
  86. from esky import bdist_esky
  87. # bbfreeze chosen for its tight integration with distutils
  88. import bbfreeze
  89. HAS_ESKY = True
  90. except ImportError:
  91. HAS_ESKY = False
  92. SALT_VERSION = os.path.join(os.path.abspath(SETUP_DIRNAME), "salt", "version.py")
  93. SALT_VERSION_HARDCODED = os.path.join(
  94. os.path.abspath(SETUP_DIRNAME), "salt", "_version.py"
  95. )
  96. SALT_SYSPATHS_HARDCODED = os.path.join(
  97. os.path.abspath(SETUP_DIRNAME), "salt", "_syspaths.py"
  98. )
  99. SALT_REQS = os.path.join(os.path.abspath(SETUP_DIRNAME), "requirements", "base.txt")
  100. SALT_CRYPTO_REQS = os.path.join(
  101. os.path.abspath(SETUP_DIRNAME), "requirements", "crypto.txt"
  102. )
  103. SALT_ZEROMQ_REQS = os.path.join(
  104. os.path.abspath(SETUP_DIRNAME), "requirements", "zeromq.txt"
  105. )
  106. SALT_LONG_DESCRIPTION_FILE = os.path.join(os.path.abspath(SETUP_DIRNAME), "README.rst")
  107. SALT_OSX_REQS = [
  108. os.path.join(os.path.abspath(SETUP_DIRNAME), "pkg", "osx", "req.txt"),
  109. os.path.join(os.path.abspath(SETUP_DIRNAME), "pkg", "osx", "req_ext.txt"),
  110. os.path.join(os.path.abspath(SETUP_DIRNAME), "pkg", "osx", "req_pyobjc.txt"),
  111. ]
  112. SALT_WINDOWS_REQS = [
  113. os.path.join(os.path.abspath(SETUP_DIRNAME), "pkg", "windows", "req.txt"),
  114. os.path.join(os.path.abspath(SETUP_DIRNAME), "pkg", "windows", "req_win.txt"),
  115. ]
  116. # Salt SSH Packaging Detection
  117. PACKAGED_FOR_SALT_SSH_FILE = os.path.join(
  118. os.path.abspath(SETUP_DIRNAME), ".salt-ssh-package"
  119. )
  120. PACKAGED_FOR_SALT_SSH = os.path.isfile(PACKAGED_FOR_SALT_SSH_FILE)
  121. # pylint: disable=W0122
  122. exec(compile(open(SALT_VERSION).read(), SALT_VERSION, "exec"))
  123. # pylint: enable=W0122
  124. # ----- Helper Functions -------------------------------------------------------------------------------------------->
  125. def _parse_op(op):
  126. """
  127. >>> _parse_op('>')
  128. 'gt'
  129. >>> _parse_op('>=')
  130. 'ge'
  131. >>> _parse_op('=>')
  132. 'ge'
  133. >>> _parse_op('=> ')
  134. 'ge'
  135. >>> _parse_op('<')
  136. 'lt'
  137. >>> _parse_op('<=')
  138. 'le'
  139. >>> _parse_op('==')
  140. 'eq'
  141. >>> _parse_op(' <= ')
  142. 'le'
  143. """
  144. op = op.strip()
  145. if ">" in op:
  146. if "=" in op:
  147. return "ge"
  148. else:
  149. return "gt"
  150. elif "<" in op:
  151. if "=" in op:
  152. return "le"
  153. else:
  154. return "lt"
  155. elif "!" in op:
  156. return "ne"
  157. else:
  158. return "eq"
  159. def _parse_ver(ver):
  160. """
  161. >>> _parse_ver("'3.4' # pyzmq 17.1.0 stopped building wheels for python3.4")
  162. '3.4'
  163. >>> _parse_ver('"3.4"')
  164. '3.4'
  165. >>> _parse_ver('"2.6.17"')
  166. '2.6.17'
  167. """
  168. if "#" in ver:
  169. ver, _ = ver.split("#", 1)
  170. ver = ver.strip()
  171. return ver.strip("'").strip('"')
  172. def _check_ver(pyver, op, wanted):
  173. """
  174. >>> _check_ver('2.7.15', 'gt', '2.7')
  175. True
  176. >>> _check_ver('2.7.15', 'gt', '2.7.15')
  177. False
  178. >>> _check_ver('2.7.15', 'ge', '2.7.15')
  179. True
  180. >>> _check_ver('2.7.15', 'eq', '2.7.15')
  181. True
  182. """
  183. pyver = distutils.version.LooseVersion(pyver)
  184. wanted = distutils.version.LooseVersion(wanted)
  185. if IS_PY3:
  186. if not isinstance(pyver, str):
  187. pyver = str(pyver)
  188. if not isinstance(wanted, str):
  189. wanted = str(wanted)
  190. return getattr(operator, "__{}__".format(op))(pyver, wanted)
  191. def _parse_requirements_file(requirements_file):
  192. parsed_requirements = []
  193. with open(requirements_file) as rfh:
  194. for line in rfh.readlines():
  195. line = line.strip()
  196. if not line or line.startswith(("#", "-r")):
  197. continue
  198. if IS_WINDOWS_PLATFORM:
  199. if "libcloud" in line:
  200. continue
  201. if IS_PY3 and "futures" in line.lower():
  202. # Python 3 already has futures, installing it will only break
  203. # the current python installation whenever futures is imported
  204. continue
  205. try:
  206. pkg, pyverspec = line.rsplit(";", 1)
  207. except ValueError:
  208. pkg, pyverspec = line, ""
  209. pyverspec = pyverspec.strip()
  210. if pyverspec and (
  211. not pkg.startswith("pycrypto") or pkg.startswith("pycryptodome")
  212. ):
  213. _, op, ver = pyverspec.split(" ", 2)
  214. if not _check_ver(
  215. platform.python_version(), _parse_op(op), _parse_ver(ver)
  216. ):
  217. continue
  218. parsed_requirements.append(pkg)
  219. return parsed_requirements
  220. # <---- Helper Functions ---------------------------------------------------------------------------------------------
  221. # ----- Custom Distutils/Setuptools Commands ------------------------------------------------------------------------>
  222. class WriteSaltVersion(Command):
  223. description = "Write salt's hardcoded version file"
  224. user_options = []
  225. def initialize_options(self):
  226. """
  227. Abstract method that is required to be overwritten
  228. """
  229. def finalize_options(self):
  230. """
  231. Abstract method that is required to be overwritten
  232. """
  233. def run(self):
  234. if (
  235. not os.path.exists(SALT_VERSION_HARDCODED)
  236. or self.distribution.with_salt_version
  237. ):
  238. # Write the version file
  239. if getattr(self.distribution, "salt_version_hardcoded_path", None) is None:
  240. print("This command is not meant to be called on it's own")
  241. exit(1)
  242. if not self.distribution.with_salt_version:
  243. salt_version = (
  244. __saltstack_version__ # pylint: disable=undefined-variable
  245. )
  246. else:
  247. from salt.version import SaltStackVersion
  248. salt_version = SaltStackVersion.parse(
  249. self.distribution.with_salt_version
  250. )
  251. # pylint: disable=E0602
  252. open(self.distribution.salt_version_hardcoded_path, "w").write(
  253. INSTALL_VERSION_TEMPLATE.format(
  254. date=DATE, full_version_info=salt_version.full_info_all_versions
  255. )
  256. )
  257. # pylint: enable=E0602
  258. class GenerateSaltSyspaths(Command):
  259. description = "Generate salt's hardcoded syspaths file"
  260. def initialize_options(self):
  261. pass
  262. def finalize_options(self):
  263. pass
  264. def run(self):
  265. # Write the syspaths file
  266. if getattr(self.distribution, "salt_syspaths_hardcoded_path", None) is None:
  267. print("This command is not meant to be called on it's own")
  268. exit(1)
  269. # Write the system paths file
  270. open(self.distribution.salt_syspaths_hardcoded_path, "w").write(
  271. INSTALL_SYSPATHS_TEMPLATE.format(
  272. date=DATE,
  273. root_dir=self.distribution.salt_root_dir,
  274. share_dir=self.distribution.salt_share_dir,
  275. config_dir=self.distribution.salt_config_dir,
  276. cache_dir=self.distribution.salt_cache_dir,
  277. sock_dir=self.distribution.salt_sock_dir,
  278. srv_root_dir=self.distribution.salt_srv_root_dir,
  279. base_file_roots_dir=self.distribution.salt_base_file_roots_dir,
  280. base_pillar_roots_dir=self.distribution.salt_base_pillar_roots_dir,
  281. base_master_roots_dir=self.distribution.salt_base_master_roots_dir,
  282. base_thorium_roots_dir=self.distribution.salt_base_thorium_roots_dir,
  283. logs_dir=self.distribution.salt_logs_dir,
  284. pidfile_dir=self.distribution.salt_pidfile_dir,
  285. spm_parent_path=self.distribution.salt_spm_parent_dir,
  286. spm_formula_path=self.distribution.salt_spm_formula_dir,
  287. spm_pillar_path=self.distribution.salt_spm_pillar_dir,
  288. spm_reactor_path=self.distribution.salt_spm_reactor_dir,
  289. home_dir=self.distribution.salt_home_dir,
  290. )
  291. )
  292. class WriteSaltSshPackagingFile(Command):
  293. description = "Write salt's ssh packaging file"
  294. user_options = []
  295. def initialize_options(self):
  296. """
  297. Abstract method that is required to be overwritten
  298. """
  299. def finalize_options(self):
  300. """
  301. Abstract method that is required to be overwritten
  302. """
  303. def run(self):
  304. if not os.path.exists(PACKAGED_FOR_SALT_SSH_FILE):
  305. # Write the salt-ssh packaging file
  306. if getattr(self.distribution, "salt_ssh_packaging_file", None) is None:
  307. print("This command is not meant to be called on it's own")
  308. exit(1)
  309. # pylint: disable=E0602
  310. open(self.distribution.salt_ssh_packaging_file, "w").write(
  311. "Packaged for Salt-SSH\n"
  312. )
  313. # pylint: enable=E0602
  314. class Develop(develop):
  315. user_options = develop.user_options + [
  316. (
  317. "write-salt-version",
  318. None,
  319. "Generate Salt's _version.py file which allows proper version "
  320. "reporting. This defaults to False on develop/editable setups. "
  321. "If WRITE_SALT_VERSION is found in the environment this flag is "
  322. "switched to True.",
  323. ),
  324. (
  325. "generate-salt-syspaths",
  326. None,
  327. "Generate Salt's _syspaths.py file which allows tweaking some "
  328. "common paths that salt uses. This defaults to False on "
  329. "develop/editable setups. If GENERATE_SALT_SYSPATHS is found in "
  330. "the environment this flag is switched to True.",
  331. ),
  332. (
  333. "mimic-salt-install",
  334. None,
  335. "Mimmic the install command when running the develop command. "
  336. "This will generate salt's _version.py and _syspaths.py files. "
  337. "Generate Salt's _syspaths.py file which allows tweaking some "
  338. "This defaults to False on develop/editable setups. "
  339. "If MIMIC_INSTALL is found in the environment this flag is "
  340. "switched to True.",
  341. ),
  342. ]
  343. boolean_options = develop.boolean_options + [
  344. "write-salt-version",
  345. "generate-salt-syspaths",
  346. "mimic-salt-install",
  347. ]
  348. def initialize_options(self):
  349. develop.initialize_options(self)
  350. self.write_salt_version = False
  351. self.generate_salt_syspaths = False
  352. self.mimic_salt_install = False
  353. def finalize_options(self):
  354. develop.finalize_options(self)
  355. if "WRITE_SALT_VERSION" in os.environ:
  356. self.write_salt_version = True
  357. if "GENERATE_SALT_SYSPATHS" in os.environ:
  358. self.generate_salt_syspaths = True
  359. if "MIMIC_SALT_INSTALL" in os.environ:
  360. self.mimic_salt_install = True
  361. if self.mimic_salt_install:
  362. self.write_salt_version = True
  363. self.generate_salt_syspaths = True
  364. def run(self):
  365. if IS_WINDOWS_PLATFORM:
  366. # Download the required DLLs
  367. self.distribution.salt_download_windows_dlls = True
  368. self.run_command("download-windows-dlls")
  369. self.distribution.salt_download_windows_dlls = None
  370. if self.write_salt_version is True:
  371. self.distribution.running_salt_install = True
  372. self.distribution.salt_version_hardcoded_path = SALT_VERSION_HARDCODED
  373. self.run_command("write_salt_version")
  374. if self.generate_salt_syspaths:
  375. self.distribution.salt_syspaths_hardcoded_path = SALT_SYSPATHS_HARDCODED
  376. self.run_command("generate_salt_syspaths")
  377. # Resume normal execution
  378. develop.run(self)
  379. class DownloadWindowsDlls(Command):
  380. description = "Download required DLL's for windows"
  381. def initialize_options(self):
  382. pass
  383. def finalize_options(self):
  384. pass
  385. def run(self):
  386. if getattr(self.distribution, "salt_download_windows_dlls", None) is None:
  387. print("This command is not meant to be called on it's own")
  388. exit(1)
  389. import pip
  390. # pip has moved many things to `_internal` starting with pip 10
  391. if LooseVersion(pip.__version__) < LooseVersion("10.0"):
  392. # pylint: disable=no-name-in-module
  393. from pip.utils.logging import indent_log
  394. # pylint: enable=no-name-in-module
  395. else:
  396. from pip._internal.utils.logging import (
  397. indent_log,
  398. ) # pylint: disable=no-name-in-module
  399. platform_bits, _ = platform.architecture()
  400. url = "https://repo.saltstack.com/windows/dependencies/{bits}/{fname}.dll"
  401. dest = os.path.join(os.path.dirname(sys.executable), "{fname}.dll")
  402. with indent_log():
  403. for fname in ("libeay32", "ssleay32", "msvcr120"):
  404. # See if the library is already on the system
  405. if find_library(fname):
  406. continue
  407. furl = url.format(bits=platform_bits[:2], fname=fname)
  408. fdest = dest.format(fname=fname)
  409. if not os.path.exists(fdest):
  410. log.info(
  411. "Downloading {0}.dll to {1} from {2}".format(fname, fdest, furl)
  412. )
  413. try:
  414. import requests
  415. from contextlib import closing
  416. with closing(requests.get(furl, stream=True)) as req:
  417. if req.status_code == 200:
  418. with open(fdest, "wb") as wfh:
  419. for chunk in req.iter_content(chunk_size=4096):
  420. if chunk: # filter out keep-alive new chunks
  421. wfh.write(chunk)
  422. wfh.flush()
  423. else:
  424. log.error(
  425. "Failed to download {0}.dll to {1} from {2}".format(
  426. fname, fdest, furl
  427. )
  428. )
  429. except ImportError:
  430. req = urlopen(furl)
  431. if req.getcode() == 200:
  432. with open(fdest, "wb") as wfh:
  433. if IS_PY3:
  434. while True:
  435. chunk = req.read(4096)
  436. if len(chunk) == 0:
  437. break
  438. wfh.write(chunk)
  439. wfh.flush()
  440. else:
  441. while True:
  442. for chunk in req.read(4096):
  443. if not chunk:
  444. break
  445. wfh.write(chunk)
  446. wfh.flush()
  447. else:
  448. log.error(
  449. "Failed to download {0}.dll to {1} from {2}".format(
  450. fname, fdest, furl
  451. )
  452. )
  453. class Sdist(sdist):
  454. def make_release_tree(self, base_dir, files):
  455. if self.distribution.ssh_packaging:
  456. self.distribution.salt_ssh_packaging_file = PACKAGED_FOR_SALT_SSH_FILE
  457. self.run_command("write_salt_ssh_packaging_file")
  458. self.filelist.files.append(os.path.basename(PACKAGED_FOR_SALT_SSH_FILE))
  459. if not IS_PY3 and not isinstance(base_dir, str):
  460. # Work around some bad code in distutils which logs unicode paths
  461. # against a str format string.
  462. base_dir = base_dir.encode("utf-8")
  463. sdist.make_release_tree(self, base_dir, files)
  464. # Let's generate salt/_version.py to include in the sdist tarball
  465. self.distribution.running_salt_sdist = True
  466. self.distribution.salt_version_hardcoded_path = os.path.join(
  467. base_dir, "salt", "_version.py"
  468. )
  469. self.run_command("write_salt_version")
  470. def make_distribution(self):
  471. sdist.make_distribution(self)
  472. if self.distribution.ssh_packaging:
  473. os.unlink(PACKAGED_FOR_SALT_SSH_FILE)
  474. class CloudSdist(Sdist): # pylint: disable=too-many-ancestors
  475. user_options = Sdist.user_options + [
  476. (
  477. "download-bootstrap-script",
  478. None,
  479. "Download the latest stable bootstrap-salt.sh script. This "
  480. "can also be triggered by having `DOWNLOAD_BOOTSTRAP_SCRIPT=1` as an "
  481. "environment variable.",
  482. )
  483. ]
  484. boolean_options = Sdist.boolean_options + ["download-bootstrap-script"]
  485. def initialize_options(self):
  486. Sdist.initialize_options(self)
  487. self.skip_bootstrap_download = True
  488. self.download_bootstrap_script = False
  489. def finalize_options(self):
  490. Sdist.finalize_options(self)
  491. if "SKIP_BOOTSTRAP_DOWNLOAD" in os.environ:
  492. # pylint: disable=not-callable
  493. log(
  494. "Please stop using 'SKIP_BOOTSTRAP_DOWNLOAD' and use "
  495. "'DOWNLOAD_BOOTSTRAP_SCRIPT' instead"
  496. )
  497. # pylint: enable=not-callable
  498. if "DOWNLOAD_BOOTSTRAP_SCRIPT" in os.environ:
  499. download_bootstrap_script = os.environ.get("DOWNLOAD_BOOTSTRAP_SCRIPT", "0")
  500. self.download_bootstrap_script = download_bootstrap_script == "1"
  501. def run(self):
  502. if self.download_bootstrap_script is True:
  503. # Let's update the bootstrap-script to the version defined to be
  504. # distributed. See BOOTSTRAP_SCRIPT_DISTRIBUTED_VERSION above.
  505. url = (
  506. "https://github.com/saltstack/salt-bootstrap/raw/{0}"
  507. "/bootstrap-salt.sh".format(BOOTSTRAP_SCRIPT_DISTRIBUTED_VERSION)
  508. )
  509. deploy_path = os.path.join(
  510. SETUP_DIRNAME, "salt", "cloud", "deploy", "bootstrap-salt.sh"
  511. )
  512. log.info(
  513. "Updating bootstrap-salt.sh."
  514. "\n\tSource: {0}"
  515. "\n\tDestination: {1}".format(url, deploy_path)
  516. )
  517. try:
  518. import requests
  519. req = requests.get(url)
  520. if req.status_code == 200:
  521. script_contents = req.text.encode(req.encoding)
  522. else:
  523. log.error(
  524. "Failed to update the bootstrap-salt.sh script. HTTP "
  525. "Error code: {0}".format(req.status_code)
  526. )
  527. except ImportError:
  528. req = urlopen(url)
  529. if req.getcode() == 200:
  530. script_contents = req.read()
  531. else:
  532. log.error(
  533. "Failed to update the bootstrap-salt.sh script. HTTP "
  534. "Error code: {0}".format(req.getcode())
  535. )
  536. try:
  537. with open(deploy_path, "w") as fp_:
  538. fp_.write(script_contents)
  539. except (OSError, IOError) as err:
  540. log.error("Failed to write the updated script: {0}".format(err))
  541. # Let's the rest of the build command
  542. Sdist.run(self)
  543. def write_manifest(self):
  544. # We only need to ship the scripts which are supposed to be installed
  545. dist_scripts = self.distribution.scripts
  546. for script in self.filelist.files[:]:
  547. if not script.startswith("scripts/"):
  548. continue
  549. if script not in dist_scripts:
  550. self.filelist.files.remove(script)
  551. return Sdist.write_manifest(self)
  552. class TestCommand(Command):
  553. description = "Run tests"
  554. user_options = [
  555. ("runtests-opts=", "R", "Command line options to pass to runtests.py")
  556. ]
  557. def initialize_options(self):
  558. self.runtests_opts = None
  559. def finalize_options(self):
  560. """
  561. Abstract method that is required to be overwritten
  562. """
  563. def run(self):
  564. from subprocess import Popen
  565. self.run_command("build")
  566. build_cmd = self.get_finalized_command("build_ext")
  567. runner = os.path.abspath("tests/runtests.py")
  568. test_cmd = sys.executable + " {0}".format(runner)
  569. if self.runtests_opts:
  570. test_cmd += " {0}".format(self.runtests_opts)
  571. print("running test")
  572. test_process = Popen(
  573. test_cmd,
  574. shell=True,
  575. stdout=sys.stdout,
  576. stderr=sys.stderr,
  577. cwd=build_cmd.build_lib,
  578. )
  579. test_process.communicate()
  580. sys.exit(test_process.returncode)
  581. class Clean(clean):
  582. def run(self):
  583. clean.run(self)
  584. # Let's clean compiled *.py[c,o]
  585. for subdir in ("salt", "tests", "doc"):
  586. root = os.path.join(os.path.dirname(__file__), subdir)
  587. for dirname, _, _ in os.walk(root):
  588. for to_remove_filename in glob.glob("{0}/*.py[oc]".format(dirname)):
  589. os.remove(to_remove_filename)
  590. if HAS_BDIST_WHEEL:
  591. class BDistWheel(bdist_wheel):
  592. def finalize_options(self):
  593. bdist_wheel.finalize_options(self)
  594. self.distribution.build_wheel = True
  595. INSTALL_VERSION_TEMPLATE = """\
  596. # This file was auto-generated by salt's setup
  597. from salt.version import SaltStackVersion
  598. __saltstack_version__ = SaltStackVersion{full_version_info!r}
  599. """
  600. INSTALL_SYSPATHS_TEMPLATE = """\
  601. # This file was auto-generated by salt's setup on \
  602. {date:%A, %d %B %Y @ %H:%m:%S UTC}.
  603. ROOT_DIR = {root_dir!r}
  604. SHARE_DIR = {share_dir!r}
  605. CONFIG_DIR = {config_dir!r}
  606. CACHE_DIR = {cache_dir!r}
  607. SOCK_DIR = {sock_dir!r}
  608. SRV_ROOT_DIR= {srv_root_dir!r}
  609. BASE_FILE_ROOTS_DIR = {base_file_roots_dir!r}
  610. BASE_PILLAR_ROOTS_DIR = {base_pillar_roots_dir!r}
  611. BASE_MASTER_ROOTS_DIR = {base_master_roots_dir!r}
  612. BASE_THORIUM_ROOTS_DIR = {base_thorium_roots_dir!r}
  613. LOGS_DIR = {logs_dir!r}
  614. PIDFILE_DIR = {pidfile_dir!r}
  615. SPM_PARENT_PATH = {spm_parent_path!r}
  616. SPM_FORMULA_PATH = {spm_formula_path!r}
  617. SPM_PILLAR_PATH = {spm_pillar_path!r}
  618. SPM_REACTOR_PATH = {spm_reactor_path!r}
  619. HOME_DIR = {home_dir!r}
  620. """
  621. class Build(build):
  622. def run(self):
  623. # Run build.run function
  624. build.run(self)
  625. salt_build_ver_file = os.path.join(self.build_lib, "salt", "_version.py")
  626. if getattr(self.distribution, "with_salt_version", False):
  627. # Write the hardcoded salt version module salt/_version.py
  628. self.distribution.salt_version_hardcoded_path = salt_build_ver_file
  629. self.run_command("write_salt_version")
  630. if getattr(self.distribution, "build_wheel", False):
  631. # we are building a wheel package. need to include _version.py
  632. self.distribution.salt_version_hardcoded_path = salt_build_ver_file
  633. self.run_command("write_salt_version")
  634. if getattr(self.distribution, "running_salt_install", False):
  635. # If our install attribute is present and set to True, we'll go
  636. # ahead and write our install time python modules.
  637. # Write the hardcoded salt version module salt/_version.py
  638. self.run_command("write_salt_version")
  639. # Write the system paths file
  640. self.distribution.salt_syspaths_hardcoded_path = os.path.join(
  641. self.build_lib, "salt", "_syspaths.py"
  642. )
  643. self.run_command("generate_salt_syspaths")
  644. class Install(install):
  645. def initialize_options(self):
  646. install.initialize_options(self)
  647. def finalize_options(self):
  648. install.finalize_options(self)
  649. def run(self):
  650. if LooseVersion(setuptools.__version__) < LooseVersion("9.1"):
  651. sys.stderr.write(
  652. "\n\nInstalling Salt requires setuptools >= 9.1\n"
  653. "Available setuptools version is {}\n\n".format(setuptools.__version__)
  654. )
  655. sys.stderr.flush()
  656. sys.exit(1)
  657. # Let's set the running_salt_install attribute so we can add
  658. # _version.py in the build command
  659. self.distribution.running_salt_install = True
  660. self.distribution.salt_version_hardcoded_path = os.path.join(
  661. self.build_lib, "salt", "_version.py"
  662. )
  663. if IS_WINDOWS_PLATFORM:
  664. # Download the required DLLs
  665. self.distribution.salt_download_windows_dlls = True
  666. self.run_command("download-windows-dlls")
  667. self.distribution.salt_download_windows_dlls = None
  668. # need to ensure _version.py is created in build dir before install
  669. if not os.path.exists(os.path.join(self.build_lib)):
  670. if not self.skip_build:
  671. self.run_command("build")
  672. else:
  673. self.run_command("write_salt_version")
  674. # Run install.run
  675. install.run(self)
  676. @staticmethod
  677. def _called_from_setup(run_frame):
  678. """
  679. Attempt to detect whether run() was called from setup() or by another
  680. command. If called by setup(), the parent caller will be the
  681. 'run_command' method in 'distutils.dist', and *its* caller will be
  682. the 'run_commands' method. If called any other way, the
  683. immediate caller *might* be 'run_command', but it won't have been
  684. called by 'run_commands'. Return True in that case or if a call stack
  685. is unavailable. Return False otherwise.
  686. """
  687. if run_frame is None:
  688. # If run_frame is None, just call the parent class logic
  689. return install._called_from_setup(run_frame)
  690. # Because Salt subclasses the setuptools install command, it needs to
  691. # override this static method to provide the right frame for the logic
  692. # so apply.
  693. # We first try the current run_frame in case the issue
  694. # https://github.com/pypa/setuptools/issues/456 is fixed.
  695. first_call = install._called_from_setup(run_frame)
  696. if first_call:
  697. return True
  698. # Fallback to providing the parent frame to have the right logic kick in
  699. second_call = install._called_from_setup(run_frame.f_back)
  700. if second_call is None:
  701. # There was no parent frame?!
  702. return first_call
  703. return second_call
  704. class InstallLib(install_lib):
  705. def run(self):
  706. executables = [
  707. "salt/templates/git/ssh-id-wrapper",
  708. "salt/templates/lxc/salt_tarball",
  709. ]
  710. install_lib.run(self)
  711. # input and outputs match 1-1
  712. inp = self.get_inputs()
  713. out = self.get_outputs()
  714. chmod = []
  715. for idx, inputfile in enumerate(inp):
  716. for executable in executables:
  717. if inputfile.endswith(executable):
  718. chmod.append(idx)
  719. for idx in chmod:
  720. filename = out[idx]
  721. os.chmod(filename, 0o755)
  722. # <---- Custom Distutils/Setuptools Commands -------------------------------------------------------------------------
  723. # ----- Custom Distribution Class ----------------------------------------------------------------------------------->
  724. # We use this to override the package name in case --ssh-packaging is passed to
  725. # setup.py or the special .salt-ssh-package is found
  726. class SaltDistribution(distutils.dist.Distribution):
  727. """
  728. Just so it's completely clear
  729. Under windows, the following scripts should be installed:
  730. * salt-call
  731. * salt-cp
  732. * salt-minion
  733. * salt-syndic
  734. * salt-unity
  735. * spm
  736. When packaged for salt-ssh, the following scripts should be installed:
  737. * salt-call
  738. * salt-run
  739. * salt-ssh
  740. * salt-cloud
  741. Under windows, the following scripts should be omitted from the salt-ssh
  742. package:
  743. * salt-cloud
  744. * salt-run
  745. Under *nix, all scripts should be installed
  746. """
  747. global_options = (
  748. distutils.dist.Distribution.global_options
  749. + [
  750. ("ssh-packaging", None, "Run in SSH packaging mode"),
  751. (
  752. "salt-transport=",
  753. None,
  754. "The transport to prepare salt for. Currently, the only choice "
  755. "is 'zeromq'. This may be expanded in the future. Defaults to "
  756. "'zeromq'",
  757. "zeromq",
  758. ),
  759. ]
  760. + [
  761. (
  762. "with-salt-version=",
  763. None,
  764. "Set a fixed version for Salt instead calculating it",
  765. ),
  766. # Salt's Paths Configuration Settings
  767. ("salt-root-dir=", None, "Salt's pre-configured root directory"),
  768. ("salt-share-dir=", None, "Salt's pre-configured share directory"),
  769. ("salt-config-dir=", None, "Salt's pre-configured configuration directory"),
  770. ("salt-cache-dir=", None, "Salt's pre-configured cache directory"),
  771. ("salt-sock-dir=", None, "Salt's pre-configured socket directory"),
  772. ("salt-srv-root-dir=", None, "Salt's pre-configured service directory"),
  773. (
  774. "salt-base-file-roots-dir=",
  775. None,
  776. "Salt's pre-configured file roots directory",
  777. ),
  778. (
  779. "salt-base-pillar-roots-dir=",
  780. None,
  781. "Salt's pre-configured pillar roots directory",
  782. ),
  783. (
  784. "salt-base-master-roots-dir=",
  785. None,
  786. "Salt's pre-configured master roots directory",
  787. ),
  788. ("salt-logs-dir=", None, "Salt's pre-configured logs directory"),
  789. ("salt-pidfile-dir=", None, "Salt's pre-configured pidfiles directory"),
  790. (
  791. "salt-spm-formula-dir=",
  792. None,
  793. "Salt's pre-configured SPM formulas directory",
  794. ),
  795. (
  796. "salt-spm-pillar-dir=",
  797. None,
  798. "Salt's pre-configured SPM pillar directory",
  799. ),
  800. (
  801. "salt-spm-reactor-dir=",
  802. None,
  803. "Salt's pre-configured SPM reactor directory",
  804. ),
  805. ("salt-home-dir=", None, "Salt's pre-configured user home directory"),
  806. ]
  807. )
  808. def __init__(self, attrs=None):
  809. distutils.dist.Distribution.__init__(self, attrs)
  810. self.ssh_packaging = PACKAGED_FOR_SALT_SSH
  811. self.salt_transport = None
  812. # Salt Paths Configuration Settings
  813. self.salt_root_dir = None
  814. self.salt_share_dir = None
  815. self.salt_config_dir = None
  816. self.salt_cache_dir = None
  817. self.salt_sock_dir = None
  818. self.salt_srv_root_dir = None
  819. self.salt_base_file_roots_dir = None
  820. self.salt_base_thorium_roots_dir = None
  821. self.salt_base_pillar_roots_dir = None
  822. self.salt_base_master_roots_dir = None
  823. self.salt_logs_dir = None
  824. self.salt_pidfile_dir = None
  825. self.salt_spm_parent_dir = None
  826. self.salt_spm_formula_dir = None
  827. self.salt_spm_pillar_dir = None
  828. self.salt_spm_reactor_dir = None
  829. self.salt_home_dir = None
  830. # Salt version
  831. self.with_salt_version = None
  832. self.name = "salt-ssh" if PACKAGED_FOR_SALT_SSH else "salt"
  833. self.salt_version = __version__ # pylint: disable=undefined-variable
  834. self.description = "Portable, distributed, remote execution and configuration management system"
  835. kwargs = {}
  836. if IS_PY3:
  837. kwargs["encoding"] = "utf-8"
  838. with open(SALT_LONG_DESCRIPTION_FILE, **kwargs) as f:
  839. self.long_description = f.read()
  840. self.long_description_content_type = "text/x-rst"
  841. self.author = "Thomas S Hatch"
  842. self.author_email = "thatch45@gmail.com"
  843. self.url = "http://saltstack.org"
  844. self.cmdclass.update(
  845. {
  846. "test": TestCommand,
  847. "clean": Clean,
  848. "build": Build,
  849. "sdist": Sdist,
  850. "install": Install,
  851. "develop": Develop,
  852. "write_salt_version": WriteSaltVersion,
  853. "generate_salt_syspaths": GenerateSaltSyspaths,
  854. "write_salt_ssh_packaging_file": WriteSaltSshPackagingFile,
  855. }
  856. )
  857. if not IS_WINDOWS_PLATFORM:
  858. self.cmdclass.update({"sdist": CloudSdist, "install_lib": InstallLib})
  859. if IS_WINDOWS_PLATFORM:
  860. self.cmdclass.update({"download-windows-dlls": DownloadWindowsDlls})
  861. if HAS_BDIST_WHEEL:
  862. self.cmdclass["bdist_wheel"] = BDistWheel
  863. self.license = "Apache Software License 2.0"
  864. self.packages = self.discover_packages()
  865. self.zip_safe = False
  866. if HAS_ESKY:
  867. self.setup_esky()
  868. self.update_metadata()
  869. def update_metadata(self):
  870. for attrname in dir(self):
  871. if attrname.startswith("__"):
  872. continue
  873. attrvalue = getattr(self, attrname, None)
  874. if attrvalue == 0:
  875. continue
  876. if attrname == "salt_version":
  877. attrname = "version"
  878. if hasattr(self.metadata, "set_{0}".format(attrname)):
  879. getattr(self.metadata, "set_{0}".format(attrname))(attrvalue)
  880. elif hasattr(self.metadata, attrname):
  881. try:
  882. setattr(self.metadata, attrname, attrvalue)
  883. except AttributeError:
  884. pass
  885. def discover_packages(self):
  886. modules = []
  887. for root, _, files in os.walk(os.path.join(SETUP_DIRNAME, "salt")):
  888. if "__init__.py" not in files:
  889. continue
  890. modules.append(os.path.relpath(root, SETUP_DIRNAME).replace(os.sep, "."))
  891. return modules
  892. # ----- Static Data -------------------------------------------------------------------------------------------->
  893. @property
  894. def _property_classifiers(self):
  895. return [
  896. "Programming Language :: Python",
  897. "Programming Language :: Cython",
  898. "Programming Language :: Python :: 2.6",
  899. "Programming Language :: Python :: 2.7",
  900. "Development Status :: 5 - Production/Stable",
  901. "Environment :: Console",
  902. "Intended Audience :: Developers",
  903. "Intended Audience :: Information Technology",
  904. "Intended Audience :: System Administrators",
  905. "License :: OSI Approved :: Apache Software License",
  906. "Operating System :: POSIX :: Linux",
  907. "Topic :: System :: Clustering",
  908. "Topic :: System :: Distributed Computing",
  909. ]
  910. @property
  911. def _property_dependency_links(self):
  912. return [
  913. "https://github.com/saltstack/salt-testing/tarball/develop#egg=SaltTesting"
  914. ]
  915. @property
  916. def _property_tests_require(self):
  917. return ["SaltTesting"]
  918. # <---- Static Data ----------------------------------------------------------------------------------------------
  919. # ----- Dynamic Data -------------------------------------------------------------------------------------------->
  920. @property
  921. def _property_package_data(self):
  922. package_data = {
  923. "salt.templates": [
  924. "rh_ip/*.jinja",
  925. "debian_ip/*.jinja",
  926. "virt/*.jinja",
  927. "git/*",
  928. "lxc/*",
  929. ]
  930. }
  931. if not IS_WINDOWS_PLATFORM:
  932. package_data["salt.cloud"] = ["deploy/*.sh"]
  933. if not self.ssh_packaging and not PACKAGED_FOR_SALT_SSH:
  934. package_data["salt.daemons.flo"] = ["*.flo"]
  935. return package_data
  936. @property
  937. def _property_data_files(self):
  938. # Data files common to all scenarios
  939. data_files = [
  940. ("share/man/man1", ["doc/man/salt-call.1", "doc/man/salt-run.1"]),
  941. ("share/man/man7", ["doc/man/salt.7"]),
  942. ]
  943. if self.ssh_packaging or PACKAGED_FOR_SALT_SSH:
  944. data_files[0][1].append("doc/man/salt-ssh.1")
  945. if IS_WINDOWS_PLATFORM:
  946. return data_files
  947. data_files[0][1].append("doc/man/salt-cloud.1")
  948. return data_files
  949. if IS_WINDOWS_PLATFORM:
  950. data_files[0][1].extend(
  951. [
  952. "doc/man/salt-cp.1",
  953. "doc/man/salt-key.1",
  954. "doc/man/salt-minion.1",
  955. "doc/man/salt-syndic.1",
  956. "doc/man/salt-unity.1",
  957. "doc/man/spm.1",
  958. ]
  959. )
  960. return data_files
  961. # *nix, so, we need all man pages
  962. data_files[0][1].extend(
  963. [
  964. "doc/man/salt-api.1",
  965. "doc/man/salt-cloud.1",
  966. "doc/man/salt-cp.1",
  967. "doc/man/salt-key.1",
  968. "doc/man/salt-master.1",
  969. "doc/man/salt-minion.1",
  970. "doc/man/salt-proxy.1",
  971. "doc/man/spm.1",
  972. "doc/man/salt.1",
  973. "doc/man/salt-ssh.1",
  974. "doc/man/salt-syndic.1",
  975. "doc/man/salt-unity.1",
  976. ]
  977. )
  978. return data_files
  979. @property
  980. def _property_install_requires(self):
  981. if IS_OSX_PLATFORM:
  982. install_requires = []
  983. for reqfile in SALT_OSX_REQS:
  984. install_requires += _parse_requirements_file(reqfile)
  985. return install_requires
  986. if IS_WINDOWS_PLATFORM:
  987. install_requires = []
  988. for reqfile in SALT_WINDOWS_REQS:
  989. install_requires += _parse_requirements_file(reqfile)
  990. return install_requires
  991. install_requires = _parse_requirements_file(SALT_REQS)
  992. if self.salt_transport == "zeromq":
  993. install_requires += _parse_requirements_file(SALT_CRYPTO_REQS)
  994. install_requires += _parse_requirements_file(SALT_ZEROMQ_REQS)
  995. return install_requires
  996. @property
  997. def _property_scripts(self):
  998. # Scripts common to all scenarios
  999. scripts = ["scripts/salt-call", "scripts/salt-run"]
  1000. if self.ssh_packaging or PACKAGED_FOR_SALT_SSH:
  1001. scripts.append("scripts/salt-ssh")
  1002. if IS_WINDOWS_PLATFORM:
  1003. return scripts
  1004. scripts.extend(["scripts/salt-cloud", "scripts/spm"])
  1005. return scripts
  1006. if IS_WINDOWS_PLATFORM:
  1007. scripts.extend(
  1008. [
  1009. "scripts/salt-cp",
  1010. "scripts/salt-key",
  1011. "scripts/salt-minion",
  1012. "scripts/salt-syndic",
  1013. "scripts/salt-unity",
  1014. "scripts/spm",
  1015. ]
  1016. )
  1017. return scripts
  1018. # *nix, so, we need all scripts
  1019. scripts.extend(
  1020. [
  1021. "scripts/salt",
  1022. "scripts/salt-api",
  1023. "scripts/salt-cloud",
  1024. "scripts/salt-cp",
  1025. "scripts/salt-key",
  1026. "scripts/salt-master",
  1027. "scripts/salt-minion",
  1028. "scripts/salt-proxy",
  1029. "scripts/salt-ssh",
  1030. "scripts/salt-syndic",
  1031. "scripts/salt-unity",
  1032. "scripts/spm",
  1033. ]
  1034. )
  1035. return scripts
  1036. @property
  1037. def _property_entry_points(self):
  1038. # console scripts common to all scenarios
  1039. scripts = [
  1040. "salt-call = salt.scripts:salt_call",
  1041. "salt-run = salt.scripts:salt_run",
  1042. ]
  1043. if self.ssh_packaging or PACKAGED_FOR_SALT_SSH:
  1044. scripts.append("salt-ssh = salt.scripts:salt_ssh")
  1045. if IS_WINDOWS_PLATFORM:
  1046. return {"console_scripts": scripts}
  1047. scripts.append("salt-cloud = salt.scripts:salt_cloud")
  1048. return {"console_scripts": scripts}
  1049. if IS_WINDOWS_PLATFORM:
  1050. scripts.extend(
  1051. [
  1052. "salt-cp = salt.scripts:salt_cp",
  1053. "salt-key = salt.scripts:salt_key",
  1054. "salt-minion = salt.scripts:salt_minion",
  1055. "salt-syndic = salt.scripts:salt_syndic",
  1056. "salt-unity = salt.scripts:salt_unity",
  1057. "spm = salt.scripts:salt_spm",
  1058. ]
  1059. )
  1060. return {"console_scripts": scripts}
  1061. # *nix, so, we need all scripts
  1062. scripts.extend(
  1063. [
  1064. "salt = salt.scripts:salt_main",
  1065. "salt-api = salt.scripts:salt_api",
  1066. "salt-cloud = salt.scripts:salt_cloud",
  1067. "salt-cp = salt.scripts:salt_cp",
  1068. "salt-key = salt.scripts:salt_key",
  1069. "salt-master = salt.scripts:salt_master",
  1070. "salt-minion = salt.scripts:salt_minion",
  1071. "salt-ssh = salt.scripts:salt_ssh",
  1072. "salt-syndic = salt.scripts:salt_syndic",
  1073. "salt-unity = salt.scripts:salt_unity",
  1074. "spm = salt.scripts:salt_spm",
  1075. ]
  1076. )
  1077. return {"console_scripts": scripts}
  1078. # <---- Dynamic Data ---------------------------------------------------------------------------------------------
  1079. # ----- Esky Setup ---------------------------------------------------------------------------------------------->
  1080. def setup_esky(self):
  1081. opt_dict = self.get_option_dict("bdist_esky")
  1082. opt_dict["freezer_module"] = ("setup script", "bbfreeze")
  1083. opt_dict["freezer_options"] = (
  1084. "setup script",
  1085. {"includes": self.get_esky_freezer_includes()},
  1086. )
  1087. @property
  1088. def _property_freezer_options(self):
  1089. return {"includes": self.get_esky_freezer_includes()}
  1090. def get_esky_freezer_includes(self):
  1091. # Sometimes the auto module traversal doesn't find everything, so we
  1092. # explicitly add it. The auto dependency tracking especially does not work for
  1093. # imports occurring in salt.modules, as they are loaded at salt runtime.
  1094. # Specifying includes that don't exist doesn't appear to cause a freezing
  1095. # error.
  1096. freezer_includes = [
  1097. "zmq.core.*",
  1098. "zmq.utils.*",
  1099. "ast",
  1100. "csv",
  1101. "difflib",
  1102. "distutils",
  1103. "distutils.version",
  1104. "numbers",
  1105. "json",
  1106. "M2Crypto",
  1107. "Cookie",
  1108. "asyncore",
  1109. "fileinput",
  1110. "sqlite3",
  1111. "email",
  1112. "email.mime.*",
  1113. "requests",
  1114. "sqlite3",
  1115. ]
  1116. if HAS_ZMQ and hasattr(zmq, "pyzmq_version_info"):
  1117. if HAS_ZMQ and zmq.pyzmq_version_info() >= (0, 14):
  1118. # We're freezing, and when freezing ZMQ needs to be installed, so this
  1119. # works fine
  1120. if "zmq.core.*" in freezer_includes:
  1121. # For PyZMQ >= 0.14, freezing does not need 'zmq.core.*'
  1122. freezer_includes.remove("zmq.core.*")
  1123. if IS_WINDOWS_PLATFORM:
  1124. freezer_includes.extend(
  1125. [
  1126. "imp",
  1127. "win32api",
  1128. "win32file",
  1129. "win32con",
  1130. "win32com",
  1131. "win32net",
  1132. "win32netcon",
  1133. "win32gui",
  1134. "win32security",
  1135. "ntsecuritycon",
  1136. "pywintypes",
  1137. "pythoncom",
  1138. "_winreg",
  1139. "wmi",
  1140. "site",
  1141. "psutil",
  1142. "pytz",
  1143. ]
  1144. )
  1145. elif IS_SMARTOS_PLATFORM:
  1146. # we have them as requirements in pkg/smartos/esky/requirements.txt
  1147. # all these should be safe to force include
  1148. freezer_includes.extend(
  1149. ["cherrypy", "python-dateutil", "pyghmi", "croniter", "mako", "gnupg"]
  1150. )
  1151. elif sys.platform.startswith("linux"):
  1152. freezer_includes.append("spwd")
  1153. try:
  1154. import yum # pylint: disable=unused-variable
  1155. freezer_includes.append("yum")
  1156. except ImportError:
  1157. pass
  1158. elif sys.platform.startswith("sunos"):
  1159. # (The sledgehammer approach)
  1160. # Just try to include everything
  1161. # (This may be a better way to generate freezer_includes generally)
  1162. try:
  1163. from bbfreeze.modulegraph.modulegraph import ModuleGraph
  1164. mgraph = ModuleGraph(sys.path[:])
  1165. for arg in glob.glob("salt/modules/*.py"):
  1166. mgraph.run_script(arg)
  1167. for mod in mgraph.flatten():
  1168. if type(mod).__name__ != "Script" and mod.filename:
  1169. freezer_includes.append(str(os.path.basename(mod.identifier)))
  1170. except ImportError:
  1171. pass
  1172. return freezer_includes
  1173. # <---- Esky Setup -----------------------------------------------------------------------------------------------
  1174. # ----- Overridden Methods -------------------------------------------------------------------------------------->
  1175. def parse_command_line(self):
  1176. args = distutils.dist.Distribution.parse_command_line(self)
  1177. if not self.ssh_packaging and PACKAGED_FOR_SALT_SSH:
  1178. self.ssh_packaging = 1
  1179. if self.ssh_packaging:
  1180. self.metadata.name = "salt-ssh"
  1181. self.salt_transport = "ssh"
  1182. elif self.salt_transport is None:
  1183. self.salt_transport = "zeromq"
  1184. if self.salt_transport not in ("zeromq", "both", "ssh", "none"):
  1185. raise DistutilsArgError(
  1186. "The value of --salt-transport needs be 'zeromq', "
  1187. "'both', 'ssh', or 'none' not '{0}'".format(self.salt_transport)
  1188. )
  1189. # Setup our property functions after class initialization and
  1190. # after parsing the command line since most are set to None
  1191. # ATTENTION: This should be the last step before returning the args or
  1192. # some of the requirements won't be correctly set
  1193. for funcname in dir(self):
  1194. if not funcname.startswith("_property_"):
  1195. continue
  1196. property_name = funcname.split("_property_", 1)[-1]
  1197. setattr(self, property_name, getattr(self, funcname))
  1198. return args
  1199. # <---- Overridden Methods ---------------------------------------------------------------------------------------
  1200. # <---- Custom Distribution Class ------------------------------------------------------------------------------------
  1201. if __name__ == "__main__":
  1202. setup(distclass=SaltDistribution)