images.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. from __future__ import absolute_import
  2. from __future__ import print_function
  3. from __future__ import unicode_literals
  4. import base64
  5. import json
  6. import os
  7. import docker
  8. from enum import Enum
  9. from .const import NAME
  10. from .const import REPO_ROOT
  11. from .utils import ScriptError
  12. from .utils import yesno
  13. from script.release.release.const import COMPOSE_TESTS_IMAGE_BASE_NAME
  14. class Platform(Enum):
  15. ALPINE = 'alpine'
  16. DEBIAN = 'debian'
  17. def __str__(self):
  18. return self.value
  19. # Checks if this version respects the GA version format ('x.y.z') and not an RC
  20. def is_tag_latest(version):
  21. ga_version = all(n.isdigit() for n in version.split('.')) and version.count('.') == 2
  22. return ga_version and yesno('Should this release be tagged as \"latest\"? [Y/n]: ', default=True)
  23. class ImageManager(object):
  24. def __init__(self, version, latest=False):
  25. self.docker_client = docker.APIClient(**docker.utils.kwargs_from_env())
  26. self.version = version
  27. self.latest = latest
  28. if 'HUB_CREDENTIALS' in os.environ:
  29. print('HUB_CREDENTIALS found in environment, issuing login')
  30. credentials = json.loads(base64.urlsafe_b64decode(os.environ['HUB_CREDENTIALS']))
  31. self.docker_client.login(
  32. username=credentials['Username'], password=credentials['Password']
  33. )
  34. def _tag(self, image, existing_tag, new_tag):
  35. existing_repo_tag = '{image}:{tag}'.format(image=image, tag=existing_tag)
  36. new_repo_tag = '{image}:{tag}'.format(image=image, tag=new_tag)
  37. self.docker_client.tag(existing_repo_tag, new_repo_tag)
  38. def get_full_version(self, platform=None):
  39. return self.version + '-' + platform.__str__() if platform else self.version
  40. def get_runtime_image_tag(self, tag):
  41. return '{image_base_image}:{tag}'.format(
  42. image_base_image=NAME,
  43. tag=self.get_full_version(tag)
  44. )
  45. def build_runtime_image(self, repository, platform):
  46. git_sha = repository.write_git_sha()
  47. compose_image_base_name = NAME
  48. print('Building {image} image ({platform} based)'.format(
  49. image=compose_image_base_name,
  50. platform=platform
  51. ))
  52. full_version = self.get_full_version(platform)
  53. build_tag = self.get_runtime_image_tag(platform)
  54. logstream = self.docker_client.build(
  55. REPO_ROOT,
  56. tag=build_tag,
  57. buildargs={
  58. 'BUILD_PLATFORM': platform.value,
  59. 'GIT_COMMIT': git_sha,
  60. },
  61. decode=True
  62. )
  63. for chunk in logstream:
  64. if 'error' in chunk:
  65. raise ScriptError('Build error: {}'.format(chunk['error']))
  66. if 'stream' in chunk:
  67. print(chunk['stream'], end='')
  68. if platform == Platform.ALPINE:
  69. self._tag(compose_image_base_name, full_version, self.version)
  70. if self.latest:
  71. self._tag(compose_image_base_name, full_version, platform)
  72. if platform == Platform.ALPINE:
  73. self._tag(compose_image_base_name, full_version, 'latest')
  74. def get_ucp_test_image_tag(self, tag=None):
  75. return '{image}:{tag}'.format(
  76. image=COMPOSE_TESTS_IMAGE_BASE_NAME,
  77. tag=tag or self.version
  78. )
  79. # Used for producing a test image for UCP
  80. def build_ucp_test_image(self, repository):
  81. print('Building test image (debian based for UCP e2e)')
  82. git_sha = repository.write_git_sha()
  83. ucp_test_image_tag = self.get_ucp_test_image_tag()
  84. logstream = self.docker_client.build(
  85. REPO_ROOT,
  86. tag=ucp_test_image_tag,
  87. target='build',
  88. buildargs={
  89. 'BUILD_PLATFORM': Platform.DEBIAN.value,
  90. 'GIT_COMMIT': git_sha,
  91. },
  92. decode=True
  93. )
  94. for chunk in logstream:
  95. if 'error' in chunk:
  96. raise ScriptError('Build error: {}'.format(chunk['error']))
  97. if 'stream' in chunk:
  98. print(chunk['stream'], end='')
  99. self._tag(COMPOSE_TESTS_IMAGE_BASE_NAME, self.version, 'latest')
  100. def build_images(self, repository):
  101. self.build_runtime_image(repository, Platform.ALPINE)
  102. self.build_runtime_image(repository, Platform.DEBIAN)
  103. self.build_ucp_test_image(repository)
  104. def check_images(self):
  105. for name in self.get_images_to_push():
  106. try:
  107. self.docker_client.inspect_image(name)
  108. except docker.errors.ImageNotFound:
  109. print('Expected image {} was not found'.format(name))
  110. return False
  111. return True
  112. def get_images_to_push(self):
  113. tags_to_push = {
  114. "{}:{}".format(NAME, self.version),
  115. self.get_runtime_image_tag(Platform.ALPINE),
  116. self.get_runtime_image_tag(Platform.DEBIAN),
  117. self.get_ucp_test_image_tag(),
  118. self.get_ucp_test_image_tag('latest'),
  119. }
  120. if is_tag_latest(self.version):
  121. tags_to_push.add("{}:latest".format(NAME))
  122. return tags_to_push
  123. def push_images(self):
  124. tags_to_push = self.get_images_to_push()
  125. print('Build tags to push {}'.format(tags_to_push))
  126. for name in tags_to_push:
  127. print('Pushing {} to Docker Hub'.format(name))
  128. logstream = self.docker_client.push(name, stream=True, decode=True)
  129. for chunk in logstream:
  130. if 'status' in chunk:
  131. print(chunk['status'])
  132. if 'error' in chunk:
  133. raise ScriptError(
  134. 'Error pushing {name}: {err}'.format(name=name, err=chunk['error'])
  135. )