1
0

setup.py 50 KB

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