Fuel plugins
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

build.py 8.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2014 Mirantis, Inc.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  5. # not use this file except in compliance with the License. You may obtain
  6. # a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. # License for the specific language governing permissions and limitations
  14. # under the License.
  15. from __future__ import unicode_literals
  16. import abc
  17. import logging
  18. import os
  19. from os.path import join as join_path
  20. from fuel_plugin_builder.actions import BaseAction
  21. from fuel_plugin_builder import errors
  22. from fuel_plugin_builder import utils
  23. from fuel_plugin_builder.validators import ValidatorManager
  24. from fuel_plugin_builder import version_mapping
  25. logger = logging.getLogger(__name__)
  26. class BaseBuildPlugin(BaseAction):
  27. @abc.abstractproperty
  28. def requires(self):
  29. """Should return a list of commands which
  30. are required for the builder
  31. """
  32. @abc.abstractproperty
  33. def result_package_mask(self):
  34. """Should return mask for built package
  35. """
  36. @abc.abstractmethod
  37. def make_package(self):
  38. """Method should be implemented in child classes
  39. """
  40. def __init__(self, plugin_path):
  41. self.plugin_path = plugin_path
  42. self.pre_build_hook_path = join_path(self.plugin_path,
  43. 'pre_build_hook')
  44. self.meta = utils.parse_yaml(
  45. join_path(self.plugin_path, 'metadata.yaml')
  46. )
  47. self.build_dir = join_path(self.plugin_path, '.build')
  48. self.build_src_dir = join_path(self.build_dir, 'src')
  49. self.checksums_path = join_path(self.build_src_dir, 'checksums.sha1')
  50. self.name = self.meta['name']
  51. def run(self):
  52. logger.debug('Start plugin building "%s"', self.plugin_path)
  53. self.clean()
  54. self.run_pre_build_hook()
  55. self.check()
  56. self.build_repos()
  57. self.add_checksums_file()
  58. self.make_package()
  59. def clean(self):
  60. utils.remove(self.build_dir)
  61. utils.create_dir(self.build_dir)
  62. utils.remove_by_mask(self.result_package_mask)
  63. def run_pre_build_hook(self):
  64. if utils.which(self.pre_build_hook_path):
  65. utils.exec_cmd(self.pre_build_hook_path)
  66. def add_checksums_file(self):
  67. utils.create_checksums_file(self.build_src_dir, self.checksums_path)
  68. def build_repos(self):
  69. utils.create_dir(self.build_src_dir)
  70. utils.copy_files_in_dir(
  71. join_path(self.plugin_path, '*'),
  72. self.build_src_dir)
  73. releases_paths = {}
  74. for release in self.meta['releases']:
  75. releases_paths.setdefault(release['os'], [])
  76. releases_paths[release['os']].append(
  77. join_path(self.build_src_dir, release['repository_path']))
  78. self.build_ubuntu_repos(releases_paths.get('ubuntu', []))
  79. self.build_centos_repos(releases_paths.get('centos', []))
  80. def build_ubuntu_repos(cls, releases_paths):
  81. for repo_path in releases_paths:
  82. utils.exec_piped_cmds(
  83. ['dpkg-scanpackages -m .', 'gzip -c9 > Packages.gz'],
  84. cwd=repo_path)
  85. @classmethod
  86. def build_centos_repos(cls, releases_paths):
  87. for repo_path in releases_paths:
  88. repo_packages = join_path(repo_path, 'Packages')
  89. utils.create_dir(repo_packages)
  90. utils.move_files_in_dir(
  91. join_path(repo_path, '*.rpm'),
  92. repo_packages)
  93. utils.exec_cmd('createrepo -o {0} {0}'.format(repo_path))
  94. def check(self):
  95. self._check_requirements()
  96. self._check_structure()
  97. def _check_requirements(self):
  98. not_found = filter(lambda r: not utils.which(r), self.requires)
  99. if not_found:
  100. raise errors.FuelCannotFindCommandError(
  101. 'Cannot find commands "{0}", '
  102. 'install required commands and try again'.format(
  103. ', '.join(not_found)))
  104. def _check_structure(self):
  105. ValidatorManager(self.plugin_path).get_validator().validate()
  106. class BuildPluginV1(BaseBuildPlugin):
  107. requires = ['rpm', 'createrepo', 'dpkg-scanpackages']
  108. @property
  109. def result_package_mask(self):
  110. return join_path(self.plugin_path, '{0}-*.fp'.format(self.name))
  111. def make_package(self):
  112. full_name = '{0}-{1}'.format(self.meta['name'],
  113. self.meta['version'])
  114. tar_name = '{0}.fp'.format(full_name)
  115. tar_path = join_path(
  116. self.plugin_path,
  117. tar_name)
  118. utils.make_tar_gz(self.build_src_dir, tar_path, full_name)
  119. class BuildPluginV2(BaseBuildPlugin):
  120. requires = ['rpmbuild', 'rpm', 'createrepo', 'dpkg-scanpackages']
  121. rpm_spec_src_path = 'templates/v2/build/plugin_rpm.spec.mako'
  122. release_tmpl_src_path = 'templates/v2/build/Release.mako'
  123. def __init__(self, *args, **kwargs):
  124. super(BuildPluginV2, self).__init__(*args, **kwargs)
  125. self.plugin_version, self.full_version = utils.version_split_name_rpm(
  126. self.meta['version'])
  127. self.rpm_path = os.path.abspath(
  128. join_path(self.plugin_path, '.build', 'rpm'))
  129. self.rpm_src_path = join_path(self.rpm_path, 'SOURCES')
  130. self.full_name = '{0}-{1}'.format(
  131. self.meta['name'], self.plugin_version)
  132. tar_name = '{0}.fp'.format(self.full_name)
  133. self.tar_path = join_path(self.rpm_src_path, tar_name)
  134. fpb_dir = join_path(os.path.dirname(__file__), '..')
  135. self.spec_src = os.path.abspath(join_path(
  136. fpb_dir, self.rpm_spec_src_path))
  137. self.release_tmpl_src = os.path.abspath(join_path(
  138. fpb_dir, self.release_tmpl_src_path))
  139. self.spec_dst = join_path(self.rpm_path, 'plugin_rpm.spec')
  140. self.rpm_packages_mask = join_path(
  141. self.rpm_path, 'RPMS', 'noarch', '*.rpm')
  142. @property
  143. def result_package_mask(self):
  144. return join_path(
  145. self.plugin_path, '{0}-*.noarch.rpm'.format(self.name))
  146. def make_package(self):
  147. """Builds rpm package
  148. """
  149. utils.create_dir(self.rpm_src_path)
  150. utils.make_tar_gz(self.build_src_dir, self.tar_path, self.full_name)
  151. utils.render_to_file(
  152. self.spec_src,
  153. self.spec_dst,
  154. self._make_data_for_template())
  155. utils.exec_cmd(
  156. 'rpmbuild -vv --nodeps --define "_topdir {0}" '
  157. '-bb {1}'.format(self.rpm_path, self.spec_dst))
  158. utils.copy_files_in_dir(self.rpm_packages_mask, self.plugin_path)
  159. def _make_data_for_template(self):
  160. """Generates data for spec template
  161. :returns: dictionary with required data
  162. """
  163. return {
  164. 'name': self.full_name,
  165. 'version': self.full_version,
  166. 'summary': self.meta['title'],
  167. 'description': self.meta['description'],
  168. 'license': ' and '.join(self.meta.get('licenses', [])),
  169. 'homepage': self.meta.get('homepage'),
  170. 'vendor': ', '.join(self.meta.get('authors', [])),
  171. 'year': utils.get_current_year()}
  172. def build_ubuntu_repos(self, releases_paths):
  173. for repo_path in releases_paths:
  174. utils.exec_piped_cmds(
  175. ['dpkg-scanpackages -m .', 'gzip -c9 > Packages.gz'],
  176. cwd=repo_path)
  177. release_path = join_path(repo_path, 'Release')
  178. utils.render_to_file(
  179. self.release_tmpl_src,
  180. release_path,
  181. {'plugin_name': self.meta['name'],
  182. 'major_version': self.plugin_version})
  183. class BuildPluginV3(BuildPluginV2):
  184. rpm_spec_src_path = 'templates/v3/build/plugin_rpm.spec.mako'
  185. release_tmpl_src_path = 'templates/v3/build/Release.mako'
  186. def _make_data_for_template(self):
  187. data = super(BuildPluginV3, self)._make_data_for_template()
  188. uninst = utils.read_if_exist(
  189. join_path(self.plugin_path, "uninstall.sh"))
  190. preinst = utils.read_if_exist(
  191. join_path(self.plugin_path, "pre_install.sh"))
  192. postinst = utils.read_if_exist(
  193. join_path(self.plugin_path, "post_install.sh"))
  194. data.update(
  195. {'postinstall_hook': postinst,
  196. 'preinstall_hook': preinst,
  197. 'uninstall_hook': uninst}
  198. )
  199. return data
  200. class BuildPluginV4(BuildPluginV3):
  201. pass
  202. def make_builder(plugin_path):
  203. """Creates build object.
  204. :param str plugin_path: path to the plugin
  205. :returns: specific version of builder object
  206. """
  207. builder = version_mapping.get_version_mapping_from_plugin(
  208. plugin_path)['builder']
  209. return builder(plugin_path)