win_installer.py 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. # -*- coding: utf-8 -*-
  2. """
  3. :copyright: Copyright 2013-2017 by the SaltStack Team, see AUTHORS for more details.
  4. :license: Apache 2.0, see LICENSE for more details.
  5. tests.support.win_installer
  6. ~~~~~~~~~~~~~~~~~~~~~~~~~~~
  7. Fetches the binary Windows installer
  8. """
  9. from __future__ import absolute_import
  10. import hashlib
  11. import re
  12. import requests
  13. PREFIX = "Salt-Minion-"
  14. REPO = "https://repo.saltstack.com/windows"
  15. def iter_installers(content):
  16. """
  17. Parse a list of windows installer links and their corresponding md5
  18. checksum links.
  19. """
  20. HREF_RE = '<a href="(.*?)">'
  21. installer, md5 = None, None
  22. for m in re.finditer(HREF_RE, content):
  23. x = m.groups()[0]
  24. if not x.startswith(PREFIX):
  25. continue
  26. if x.endswith(("zip", "sha256")):
  27. continue
  28. if installer:
  29. if x != installer + ".md5":
  30. raise Exception("Unable to parse response")
  31. md5 = x
  32. yield installer, md5
  33. installer, md5 = None, None
  34. else:
  35. installer = x
  36. def split_installer(name):
  37. """
  38. Return a tuple of the salt version, python verison and architecture from an
  39. installer name.
  40. """
  41. x = name[len(PREFIX) :]
  42. return x.split("-")[:3]
  43. def latest_version(repo=REPO):
  44. """
  45. Return the latest version found on the salt repository webpage.
  46. """
  47. content = requests.get(repo).content.decode("utf-8")
  48. for name, md5 in iter_installers(content):
  49. pass
  50. return split_installer(name)[0]
  51. def installer_name(salt_ver, py_ver="Py2", arch="AMD64"):
  52. """
  53. Create an installer file name
  54. """
  55. return "Salt-Minion-{}-{}-{}-Setup.exe".format(salt_ver, py_ver, arch)
  56. def latest_installer_name(repo=REPO, **kwargs):
  57. """
  58. Fetch the latest installer name
  59. """
  60. return installer_name(latest_version(repo), **kwargs)
  61. def download_and_verify(fp, name, repo=REPO):
  62. """
  63. Download an installer and verify it's contents.
  64. """
  65. md5 = "{}.md5".format(name)
  66. url = lambda x: "{}/{}".format(repo, x)
  67. resp = requests.get(url(md5))
  68. if resp.status_code != 200:
  69. raise Exception("Unable to fetch installer md5")
  70. installer_md5 = resp.text.strip().split()[0].lower()
  71. resp = requests.get(url(name), stream=True)
  72. if resp.status_code != 200:
  73. raise Exception("Unable to fetch installer")
  74. md5hsh = hashlib.md5()
  75. for chunk in resp.iter_content(chunk_size=1024):
  76. md5hsh.update(chunk)
  77. fp.write(chunk)
  78. if md5hsh.hexdigest() != installer_md5:
  79. raise Exception(
  80. "Installer's hash does not match {} != {}".format(
  81. md5hsh.hexdigest(), installer_md5
  82. )
  83. )