fuel-plugins/fuel_plugin_builder/actions/build.py

282 lines
8.8 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2014 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import unicode_literals
import abc
import logging
import os
from os.path import join as join_path
from fuel_plugin_builder.actions import BaseAction
from fuel_plugin_builder import errors
from fuel_plugin_builder import utils
from fuel_plugin_builder.validators import ValidatorManager
from fuel_plugin_builder import version_mapping
logger = logging.getLogger(__name__)
class BaseBuildPlugin(BaseAction):
release_tmpl_src_path = 'templates/base/build/Release.mako'
@abc.abstractproperty
def requires(self):
"""Should return a list of commands which
are required for the builder
"""
@abc.abstractproperty
def result_package_mask(self):
"""Should return mask for built package
"""
@abc.abstractmethod
def make_package(self):
"""Method should be implemented in child classes
"""
def __init__(self, plugin_path):
self.plugin_path = plugin_path
self.pre_build_hook_cmd = './pre_build_hook'
self.meta = utils.parse_yaml(
join_path(self.plugin_path, 'metadata.yaml')
)
self.build_dir = join_path(self.plugin_path, '.build')
self.build_src_dir = join_path(self.build_dir, 'src')
self.checksums_path = join_path(self.build_src_dir, 'checksums.sha1')
self.name = self.meta['name']
self.plugin_version, self.full_version = utils.version_split_name_rpm(
self.meta['version'])
fpb_dir = join_path(os.path.dirname(__file__), '..')
self.release_tmpl_src = os.path.abspath(join_path(
fpb_dir, self.release_tmpl_src_path))
def run(self):
logger.debug('Start plugin building "%s"', self.plugin_path)
self.clean()
self.run_pre_build_hook()
self.check()
self.build_repos()
self.add_checksums_file()
self.make_package()
def clean(self):
utils.remove(self.build_dir)
utils.create_dir(self.build_dir)
utils.remove_by_mask(self.result_package_mask)
def run_pre_build_hook(self):
if utils.which(join_path(self.plugin_path, self.pre_build_hook_cmd)):
utils.exec_cmd(self.pre_build_hook_cmd, self.plugin_path)
def add_checksums_file(self):
utils.create_checksums_file(self.build_src_dir, self.checksums_path)
def build_repos(self):
utils.create_dir(self.build_src_dir)
utils.copy_files_in_dir(
join_path(self.plugin_path, '*'),
self.build_src_dir)
releases_paths = {}
for release in self.meta['releases']:
releases_paths.setdefault(release['os'], [])
releases_paths[release['os']].append(
join_path(self.build_src_dir, release['repository_path']))
self.build_ubuntu_repos(releases_paths.get('ubuntu', []))
self.build_centos_repos(releases_paths.get('centos', []))
def build_ubuntu_repos(self, releases_paths):
for repo_path in releases_paths:
utils.exec_piped_cmds(
['dpkg-scanpackages -m .', 'gzip -c9 > Packages.gz'],
cwd=repo_path)
release_path = join_path(repo_path, 'Release')
utils.render_to_file(
self.release_tmpl_src,
release_path,
{'plugin_name': self.meta['name'],
'major_version': self.plugin_version})
@classmethod
def build_centos_repos(cls, releases_paths):
for repo_path in releases_paths:
repo_packages = join_path(repo_path, 'Packages')
utils.create_dir(repo_packages)
utils.move_files_in_dir(
join_path(repo_path, '*.rpm'),
repo_packages)
utils.exec_cmd('createrepo -o {0} {0}'.format(repo_path))
def check(self):
self._check_requirements()
self._check_structure()
def _check_requirements(self):
not_found = filter(lambda r: not utils.which(r), self.requires)
if not_found:
raise errors.FuelCannotFindCommandError(
'Cannot find commands "{0}", '
'install required commands and try again'.format(
', '.join(not_found)))
def _check_structure(self):
ValidatorManager(self.plugin_path).get_validator().validate()
class BuildPluginV1(BaseBuildPlugin):
requires = ['rpm', 'createrepo', 'dpkg-scanpackages']
@property
def result_package_mask(self):
return join_path(self.plugin_path, '{0}-*.fp'.format(self.name))
def make_package(self):
full_name = '{0}-{1}'.format(self.meta['name'],
self.meta['version'])
tar_name = '{0}.fp'.format(full_name)
tar_path = join_path(
self.plugin_path,
tar_name)
utils.make_tar_gz(self.build_src_dir, tar_path, full_name)
class BuildPluginV2(BaseBuildPlugin):
requires = ['rpmbuild', 'rpm', 'createrepo', 'dpkg-scanpackages']
rpm_spec_src_path = 'templates/v2/build/plugin_rpm.spec.mako'
def __init__(self, *args, **kwargs):
super(BuildPluginV2, self).__init__(*args, **kwargs)
self.rpm_path = os.path.abspath(
join_path(self.plugin_path, '.build', 'rpm'))
self.rpm_src_path = join_path(self.rpm_path, 'SOURCES')
self.full_name = '{0}-{1}'.format(
self.meta['name'], self.plugin_version)
tar_name = '{0}.fp'.format(self.full_name)
self.tar_path = join_path(self.rpm_src_path, tar_name)
fpb_dir = join_path(os.path.dirname(__file__), '..')
self.spec_src = os.path.abspath(join_path(
fpb_dir, self.rpm_spec_src_path))
self.spec_dst = join_path(self.rpm_path, 'plugin_rpm.spec')
self.rpm_packages_mask = join_path(
self.rpm_path, 'RPMS', 'noarch', '*.rpm')
@property
def result_package_mask(self):
return join_path(
self.plugin_path, '{0}-*.noarch.rpm'.format(self.name))
def make_package(self):
"""Builds rpm package
"""
utils.create_dir(self.rpm_src_path)
utils.make_tar_gz(self.build_src_dir, self.tar_path, self.full_name)
utils.render_to_file(
self.spec_src,
self.spec_dst,
self._make_data_for_template())
utils.exec_cmd(
'rpmbuild -vv --nodeps --define "_topdir {0}" '
'-bb {1}'.format(self.rpm_path, self.spec_dst))
utils.copy_files_in_dir(self.rpm_packages_mask, self.plugin_path)
def _make_data_for_template(self):
"""Generates data for spec template
:returns: dictionary with required data
"""
return {
'name': self.full_name,
'version': self.full_version,
'summary': self.meta['title'],
'description': self.meta['description'],
'license': ' and '.join(self.meta.get('licenses', [])),
'homepage': self.meta.get('homepage'),
'vendor': ', '.join(self.meta.get('authors', [])),
'year': utils.get_current_year()}
class BuildPluginV3(BuildPluginV2):
rpm_spec_src_path = 'templates/v3/build/plugin_rpm.spec.mako'
release_tmpl_src_path = 'templates/v3/build/Release.mako'
def _make_data_for_template(self):
data = super(BuildPluginV3, self)._make_data_for_template()
uninst = utils.read_if_exist(
join_path(self.plugin_path, "uninstall.sh"))
preinst = utils.read_if_exist(
join_path(self.plugin_path, "pre_install.sh"))
postinst = utils.read_if_exist(
join_path(self.plugin_path, "post_install.sh"))
plugin_build_version = str(self.meta.get('build_version', '1'))
data.update(
{'postinstall_hook': postinst,
'preinstall_hook': preinst,
'uninstall_hook': uninst,
'build_version': plugin_build_version}
)
return data
class BuildPluginV4(BuildPluginV3):
pass
class BuildPluginV5(BuildPluginV4):
pass
def make_builder(plugin_path):
"""Creates build object.
:param str plugin_path: path to the plugin
:returns: specific version of builder object
"""
builder = version_mapping.get_version_mapping_from_plugin(
plugin_path)['builder']
return builder(plugin_path)