Merge pull request #28632 from terminalmage/pkgbuild-fixes

Fixes/improvements to pkgbuild state/modules
This commit is contained in:
Mike Place 2015-11-06 13:48:07 -07:00
commit 0583575f82
3 changed files with 193 additions and 92 deletions

View file

@ -12,16 +12,18 @@ This module impliments the pkgbuild interface
# import python libs
from __future__ import absolute_import, print_function
import errno
import logging
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
from salt.exceptions import SaltInvocationError
from salt.ext.six.moves.urllib.parse import urlparse as _urlparse # pylint: disable=no-name-in-module,import-error
# pylint: disable=import-error
log = logging.getLogger(__name__)
__virtualname__ = 'pkgbuild'
@ -329,7 +331,16 @@ def make_src_pkg(dest_dir, spec, sources, env=None, template=None, saltenv='base
return ret
def build(runas, tgt, dest_dir, spec, sources, deps, env, template, saltenv='base'):
def build(runas,
tgt,
dest_dir,
spec,
sources,
deps,
env,
template,
saltenv='base',
log_dir='/var/log/salt/pkgbuild'): # pylint: disable=unused-argument
'''
Given the package destination directory, the tarball containing debian files (e.g. control)
and package sources, use pbuilder to safely build the platform package
@ -337,19 +348,24 @@ def build(runas, tgt, dest_dir, spec, sources, deps, env, template, saltenv='bas
CLI Example:
Debian
salt '*' pkgbuild.make_src_pkg deb-8-x86_64 /var/www/html/ https://raw.githubusercontent.com/saltstack/libnacl/master/pkg/deb/python-libnacl.control https://pypi.python.org/packages/source/l/libnacl/libnacl-1.3.5.tar.gz
salt '*' pkgbuild.make_src_pkg deb-8-x86_64 /var/www/html https://raw.githubusercontent.com/saltstack/libnacl/master/pkg/deb/python-libnacl.control https://pypi.python.org/packages/source/l/libnacl/libnacl-1.3.5.tar.gz
This example command should build the libnacl package for Debian using pbuilder
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
try:
os.makedirs(dest_dir)
except OSError as exc:
if exc.errno != errno.EEXIST:
raise
dsc_dir = tempfile.mkdtemp()
dscs = make_src_pkg(dsc_dir, spec, sources, env, template, saltenv)
try:
dscs = make_src_pkg(dsc_dir, spec, sources, env, template, saltenv)
except Exception as exc:
shutil.rmtree(dsc_dir)
log.error('Failed to make src package')
return ret
# dscs should only contain salt orig and debian tarballs and dsc file
for dsc in dscs:
@ -359,23 +375,26 @@ def build(runas, tgt, dest_dir, spec, sources, deps, env, template, saltenv='bas
if dsc.endswith('.dsc'):
dbase = os.path.dirname(dsc)
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)
try:
__salt__['cmd.run']('chown {0} -R {1}'.format(runas, dbase))
__salt__['cmd.run']('chown {0} -R {1}'.format(runas, results_dir))
cmd = 'pbuilder --create'
__salt__['cmd.run'](cmd, runas=runas, python_shell=True)
cmd = 'pbuilder --build --buildresult {1} {0}'.format(dsc, results_dir)
__salt__['cmd.run'](cmd, runas=runas, python_shell=True)
cmd = 'pbuilder --create'
__salt__['cmd.run'](cmd, runas=runas, python_shell=True)
cmd = 'pbuilder --build --buildresult {1} {0}'.format(
dsc, results_dir)
__salt__['cmd.run'](cmd, runas=runas, python_shell=True)
for bfile in os.listdir(results_dir):
full = os.path.join(results_dir, bfile)
bdist = os.path.join(dest_dir, bfile)
shutil.copy(full, bdist)
shutil.rmtree(results_dir)
for bfile in os.listdir(results_dir):
full = os.path.join(results_dir, bfile)
bdist = os.path.join(dest_dir, bfile)
shutil.copy(full, bdist)
ret.setdefault('Packages', []).append(bdist)
except Exception as exc:
log.error('Error building from {0}: {1}'.format(dsc, exc))
finally:
shutil.rmtree(results_dir)
shutil.rmtree(dsc_dir)
return ret

View file

@ -12,14 +12,18 @@ This module impliments the pkgbuild interface
# Import python libs
from __future__ import absolute_import, print_function
import errno
import logging
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 tempfile
# Import salt libs
import salt.utils
from salt.exceptions import SaltInvocationError
from salt.ext.six.moves.urllib.parse import urlparse as _urlparse # pylint: disable=no-name-in-module,import-error
log = logging.getLogger(__name__)
__virtualname__ = 'pkgbuild'
@ -164,26 +168,42 @@ def make_src_pkg(dest_dir, spec, sources, env=None, template=None, saltenv='base
return ret
def build(runas, tgt, dest_dir, spec, sources, deps, env, template, saltenv='base'):
def build(runas,
tgt,
dest_dir,
spec,
sources,
deps,
env,
template,
saltenv='base',
log_dir='/var/log/salt/pkgbuild'):
'''
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.build 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
salt '*' pkgbuild.build 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, env, template, saltenv)
try:
os.makedirs(dest_dir)
except OSError as exc:
if exc.errno != errno.EEXIST:
raise
srpm_dir = os.path.join(dest_dir, 'SRPMS')
srpm_build_dir = tempfile.mkdtemp()
try:
srpms = make_src_pkg(srpm_build_dir, spec, sources,
env, template, saltenv)
except Exception as exc:
shutil.rmtree(srpm_build_dir)
log.error('Failed to make src package')
return ret
distset = _get_distset(tgt)
@ -197,37 +217,51 @@ def build(runas, tgt, dest_dir, spec, sources, deps, env, template, saltenv='bas
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 --root={0} --resultdir={1} {2} {3} {4}'.format(
tgt,
results_dir,
distset,
noclean,
srpm)
__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:
__salt__['cmd.run']('chown {0} -R {1}'.format(runas, dbase))
__salt__['cmd.run']('chown {0} -R {1}'.format(runas, results_dir))
cmd = 'mock --root={0} --resultdir={1} {2} {3} {4}'.format(
tgt,
results_dir,
distset,
noclean,
srpm)
__salt__['cmd.run'](cmd, runas=runas)
cmd = ['rpm', '-qp', '--queryformat',
'{0}/%{{name}}/%{{version}}-%{{release}}'.format(log_dir),
srpm]
log_dest = __salt__['cmd.run_stdout'](cmd, python_shell=False)
for filename in os.listdir(results_dir):
full = os.path.join(results_dir, filename)
if filename.endswith('src.rpm'):
sdest = os.path.join(srpm_dir, filename)
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)
os.makedirs(srpm_dir)
except OSError as exc:
if exc.errno != errno.EEXIST:
raise
shutil.copy(full, sdest)
ret.setdefault('Source Packages', []).append(sdest)
elif filename.endswith('.rpm'):
bdist = os.path.join(dest_dir, filename)
shutil.copy(full, bdist)
ret.setdefault('Packages', []).append(bdist)
else:
log_file = os.path.join(log_dest, filename)
try:
os.makedirs(log_dest)
except OSError as exc:
if exc.errno != errno.EEXIST:
raise
shutil.copy(full, log_file)
ret.setdefault('Log Files', []).append(log_file)
except Exception as exc:
log.error('Error building from {0}: {1}'.format(srpm, exc))
finally:
shutil.rmtree(results_dir)
shutil.rmtree(deps_dir)
shutil.rmtree(srpm_dir)
shutil.rmtree(srpm_build_dir)
return ret

View file

@ -44,26 +44,53 @@ automatically
'''
# Import python libs
from __future__ import absolute_import, print_function
import errno
import logging
import os
# Import salt libs
import salt.utils
from salt.ext import six
log = logging.getLogger(__name__)
def built(
name,
runas,
dest_dir,
spec,
sources,
tgt,
template=None,
deps=None,
env=None,
results=None,
force=False,
always=None,
saltenv='base'):
def _get_missing_results(results, dest_dir):
'''
Return a list of the filenames specified in the ``results`` argument, which
are not present in the dest_dir.
'''
try:
present = set(os.listdir(dest_dir))
except OSError as exc:
if exc.errno == errno.ENOENT:
log.debug(
'pkgbuild.built: dest_dir \'{0}\' does not exist'
.format(dest_dir)
)
elif exc.errno == errno.EACCES:
log.error(
'pkgbuilt.built: cannot access dest_dir \'{0}\''
.format(dest_dir)
)
present = set()
return sorted(set(results).difference(present))
def built(name,
runas,
dest_dir,
spec,
sources,
tgt,
template=None,
deps=None,
env=None,
results=None,
force=False,
always=None,
saltenv='base',
log_dir='/var/log/salt/pkgbuild'):
'''
Ensure that the named package is built and exists in the named directory
@ -137,6 +164,13 @@ def built(
saltenv
The saltenv to use for files downloaded from the salt filesever
log_dir : /var/log/salt/rpmbuild
Root directory for log files created from the build. Logs will be
organized by package name, version, OS release, and CPU architecture
under this directory.
.. versionadded:: 2015.8.2
'''
ret = {'name': name,
'changes': {},
@ -151,21 +185,26 @@ def built(
)
force = always
if not force:
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.add(fn_)
need = results.difference(present)
if not need:
ret['comment'] = 'All needed packages exist'
return ret
if not results:
ret['comment'] = '\'results\' argument is required'
ret['result'] = False
return ret
if isinstance(results, six.string_types):
results = results.split(',')
needed = _get_missing_results(results, dest_dir)
if not force and not needed:
ret['comment'] = 'All needed packages exist'
return ret
if __opts__['test']:
ret['comment'] = 'Packages need to be built'
ret['result'] = None
if force:
ret['comment'] = 'Packages will be force-built'
else:
ret['comment'] = 'The following packages need to be built: '
ret['comment'] += ', '.join(needed)
return ret
# Need the check for None here, if env is not provided then it falls back
@ -173,6 +212,7 @@ def built(
if env is not None and not isinstance(env, dict):
ret['comment'] = ('Invalidly-formatted \'env\' parameter. See '
'documentation.')
ret['result'] = False
return ret
ret['changes'] = __salt__['pkgbuild.build'](
@ -184,8 +224,16 @@ def built(
deps,
env,
template,
saltenv)
ret['comment'] = 'Packages Built'
saltenv,
log_dir)
needed = _get_missing_results(results, dest_dir)
if needed:
ret['comment'] = 'The following packages were not built: '
ret['comment'] += ', '.join(needed)
ret['result'] = False
else:
ret['comment'] = 'All needed packages were built'
return ret