Merge pull request #24476 from thatch45/rpmbuild

Rpmbuild
This commit is contained in:
Thomas S Hatch 2015-06-09 14:27:02 -06:00
commit 1023f9b03f
2 changed files with 309 additions and 0 deletions

165
salt/modules/rpmbuild.py Normal file
View File

@ -0,0 +1,165 @@
# -*- coding: utf-8 -*-
'''
RPM Package builder system
.. versionadded:: Beryllium
This system allows for all of the components to build rpms safely in chrooted
environments. This also provides a function to generate yum repositories
This module impliments the pkgbuild interface
'''
# Import python libs
from __future__ import absolute_import, print_function
import os
import tempfile
import shutil
from salt.ext.six.moves.urllib.parse import urlparse as _urlparse # pylint: disable=no-name-in-module,import-error
# Import salt libs
import salt.utils
__virtualname__ = 'pkgbuild'
def __virtual__():
'''
Only if rpmdevtools, createrepo and mock are available
'''
if salt.utils.which('mock'):
return __virtualname__
def _mk_tree():
'''
Create the rpm build tree
'''
basedir = tempfile.mkdtemp()
paths = ['BUILD', 'RPMS', 'SOURCES', 'SPECS', 'SRPMS']
for path in paths:
full = os.path.join(basedir, path)
os.makedirs(full)
return basedir
def _get_spec(tree_base, spec, template, saltenv='base'):
'''
Get the spec file and place it in the SPECS dir
'''
spec_tgt = os.path.basename(spec)
dest = os.path.join(tree_base, 'SPECS', spec_tgt)
return __salt__['cp.get_file'](
spec,
dest,
saltenv=saltenv,
template=template)
def _get_src(tree_base, source, saltenv='base'):
'''
Get the named sources and place them into the tree_base
'''
parsed = _urlparse(source)
sbase = os.path.basename(source)
dest = os.path.join(tree_base, 'SOURCES', sbase)
if parsed.scheme:
lsrc = __salt__['cp.get_file'](source, dest, saltenv=saltenv)
else:
shutil.copy(source, dest)
def make_src_pkg(dest_dir, spec, sources, template=None, saltenv='base'):
'''
Create a source rpm from the given spec file and sources
CLI Example:
salt '*' pkgbuild.make_src_pkg /var/www/html/ https://raw.githubusercontent.com/saltstack/libnacl/master/pkg/rpm/python-libnacl.spec https://pypi.python.org/packages/source/l/libnacl/libnacl-1.3.5.tar.gz
This example command should build the libnacl SOURCE package and place it in
/var/www/html/ on the minion
'''
tree_base = _mk_tree()
spec_path = _get_spec(tree_base, spec, template, saltenv)
if isinstance(sources, str):
sources = sources.split(',')
for src in sources:
_get_src(tree_base, src, saltenv)
cmd = 'rpmbuild --define "_topdir {0}" -bs {1}'.format(tree_base, spec_path)
__salt__['cmd.run'](cmd)
srpms = os.path.join(tree_base, 'SRPMS')
ret = []
if not os.path.isdir(dest_dir):
os.makedirs(dest_dir)
for fn_ in os.listdir(srpms):
full = os.path.join(srpms, fn_)
tgt = os.path.join(dest_dir, fn_)
shutil.copy(full, tgt)
ret.append(tgt)
return ret
def build(runas, tgt, dest_dir, spec, sources, template, saltenv='base'):
'''
Given the package destination directory, the spec file source and package
sources, use mock to safely build the rpm defined in the spec file
CLI Example:
salt '*' pkgbuild.make_src_pkg mock epel-7-x86_64 /var/www/html/ https://raw.githubusercontent.com/saltstack/libnacl/master/pkg/rpm/python-libnacl.spec https://pypi.python.org/packages/source/l/libnacl/libnacl-1.3.5.tar.gz
This example command should build the libnacl package for rhel 7 using user
"mock" and place it in /var/www/html/ on the minion
'''
ret = {}
if not os.path.isdir(dest_dir):
try:
os.makedirs(dest_dir)
except (IOError, OSError):
pass
srpm_dir = tempfile.mkdtemp()
srpms = make_src_pkg(srpm_dir, spec, sources, template, saltenv)
for srpm in srpms:
dbase = os.path.dirname(srpm)
cmd = 'chown {0} -R {1}'.format(runas, dbase)
__salt__['cmd.run'](cmd)
results_dir = tempfile.mkdtemp()
cmd = 'chown {0} -R {1}'.format(runas, results_dir)
__salt__['cmd.run'](cmd)
cmd = 'mock -r {0} --rebuild {1} --resultdir {2}'.format(
tgt,
srpm,
results_dir)
__salt__['cmd.run'](cmd, runas=runas)
for rpm in os.listdir(results_dir):
full = os.path.join(results_dir, rpm)
if rpm.endswith('src.rpm'):
sdest = os.path.join(dest_dir, 'SRPMS', rpm)
if not os.path.isdir(sdest):
try:
os.makedirs(sdest)
except (IOError, OSError):
pass
shutil.copy(full, sdest)
elif rpm.endswith('.rpm'):
bdist = os.path.join(dest_dir, rpm)
shutil.copy(full, bdist)
else:
with salt.utils.fopen(full, 'r') as fp_:
ret[rpm] = fp_.read()
shutil.rmtree(results_dir)
shutil.rmtree(srpm_dir)
return ret
def make_repo(repodir):
'''
Given the repodir, create a yum repository out of the rpms therein
CLI Example::
salt '*' pkgbuild.make_repo /var/www/html/
'''
cmd = 'createrepo {0}'.format(repodir)
__salt__['cmd.run'](cmd)

144
salt/states/pkgbuild.py Normal file
View File

@ -0,0 +1,144 @@
# -*- coding: utf-8 -*-
'''
The pkgbuild state is the front of Salt package building backend. It
automatically
.. versionadded:: Beryllium
.. code-block:: yaml
salt_2015.5.2:
pkgbuild.built:
- runas: thatch
- results:
- salt-2015.5.2-2.el7.centos.noarch.rpm
- salt-api-2015.5.2-2.el7.centos.noarch.rpm
- salt-cloud-2015.5.2-2.el7.centos.noarch.rpm
- salt-master-2015.5.2-2.el7.centos.noarch.rpm
- salt-minion-2015.5.2-2.el7.centos.noarch.rpm
- salt-ssh-2015.5.2-2.el7.centos.noarch.rpm
- salt-syndic-2015.5.2-2.el7.centos.noarch.rpm
- dest_dir: /tmp/pkg
- spec: salt://pkg/salt/spec/salt.spec
- template: jinja
- tgt: epel-7-x86_64
- sources:
- salt://pkg/salt/sources/logrotate.salt
- salt://pkg/salt/sources/README.fedora
- salt://pkg/salt/sources/salt-2015.5.2.tar.gz
- salt://pkg/salt/sources/salt-2015.5.2-tests.patch
- salt://pkg/salt/sources/salt-api
- salt://pkg/salt/sources/salt-api.service
- salt://pkg/salt/sources/salt-master
- salt://pkg/salt/sources/salt-master.service
- salt://pkg/salt/sources/salt-minion
- salt://pkg/salt/sources/salt-minion.service
- salt://pkg/salt/sources/saltpkg.sls
- salt://pkg/salt/sources/salt-syndic
- salt://pkg/salt/sources/salt-syndic.service
- salt://pkg/salt/sources/SaltTesting-2015.5.8.tar.gz
/tmp/pkg:
pkgbuild.repo
'''
# Import python libs
from __future__ import absolute_import, print_function
import os
def built(
name,
runas,
dest_dir,
spec,
sources,
template,
tgt,
results=None,
always=False,
saltenv='base'):
'''
Ensure that the named package is built and exists in the named directory
name
The name to track the build, the name value is otherwise unused
runas
The user to run the build process as
dest_dir
The directory on the minion to place the built package(s)
spec
The location of the spec file (used for rpms)
sources
The list of package sources
template
Set to run the spec file through a templating engine
tgt
The target platform to run the build on
results
The names of the expected rpms that will be built
always
Build with every run (good if the package is for continuous or
nightly package builds)
saltenv
The saltenv to use for files downloaded from the salt filesever
'''
ret = {'name': name,
'changes': {},
'comment': '',
'result': True}
if not always:
if isinstance(results, str):
results = results.split(',')
results = set(results)
present = set()
if os.path.isdir(dest_dir):
for fn_ in os.listdir(dest_dir):
present.appd(fn_)
need = results.difference(present)
if not need:
ret['comment'] = 'All needed packages exist'
return ret
if __opts__['test']:
ret['comment'] = 'Packages need to be built'
ret['result'] = None
return ret
ret['changes'] = __salt__['pkgbuild.build'](
runas,
tgt,
dest_dir,
spec,
sources,
template,
saltenv)
ret['comment'] = 'Packages Built'
return ret
def repo(name):
'''
Make a package repository, the name is directoty to turn into a repo.
This state is best used with onchanges linked to your package building
states
name
The directory to find packages that will be in the repository
'''
ret = {'name': name,
'changes': {},
'comment': '',
'result': True}
if __opts__['test'] is True:
ret['result'] = None
ret['comment'] = 'Package repo at {0} will be rebuilt'.format(name)
return ret
__salt__['pkgbuild.make_repo'](name)
ret['changes'] = {'refresh': True}
return ret