setup.py 51 KB

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