Merge pull request #8914 from makinacorpus/issue-8856-buildout-support

feat: buildout support
This commit is contained in:
Thomas S Hatch 2013-12-03 09:56:59 -08:00
commit 77d61d92a4
27 changed files with 1675 additions and 0 deletions

View File

@ -27,6 +27,7 @@ Full list of builtin execution modules
brew
bridge
bsd_shadow
buildout
cassandra
chocolatey
cmdmod

View File

@ -0,0 +1,6 @@
=====================
salt.modules.buildout
=====================
.. automodule:: salt.modules.zcbuildout
:members:

View File

@ -15,6 +15,7 @@ Full list of builtin state modules
apt
augeas
aws_sqs
buildout
cmd
cron
ddns

View File

@ -0,0 +1,6 @@
====================
salt.states.buildout
====================
.. automodule:: salt.states.zcbuildout
:members:

896
salt/modules/zcbuildout.py Normal file
View File

@ -0,0 +1,896 @@
# -*- coding: utf-8 -*-
__docformat__ = 'restructuredtext en'
'''
Management of zc.buildout
===========================
This module is inspired from minitage's buildout maker
(https://github.com/minitage/minitage/blob/master/src/minitage/core/makers/buildout.py)
.. versionadded:: Boron
.. note::
The zc.buildout integration is still in beta; the API is subject to change
General notes
-------------
You have those following methods:
- upgrade_bootstrap
- bootstrap
- run_buildout
- buildout
'''
# Define the module's virtual name
__virtualname__ = 'buildout'
def __virtual__():
'''
Only load if buildout libs are present
'''
if True:
return __virtualname__
return False
from threading import Thread
import copy
import fcntl
import logging
import os
import pkg_resources
import re
import shutil
import subprocess
import sys
import traceback
import urllib2
from salt.modules import cmdmod
from salt.exceptions import CommandExecutionError
from salt._compat import string_types
import salt.utils
from salt.utils.odict import OrderedDict
INVALID_RESPONSE = 'We did not get any expectable answer from buildout'
VALID_RESPONSE = ''
NOTSET = object()
hr = u'{0}\n'.format('-' * 80)
re_f = re.S | re.M | re.U
base_status = {
'status': None,
'logs': {'debug': []},
'comment': '',
'out': None,
'logs': [],
'logs_by_level': {},
'outlog': None,
'outlog_by_level': None,
}
_url_versions = {
1: u'http://downloads.buildout.org/1/bootstrap.py',
2: u'http://downloads.buildout.org/2/bootstrap.py',
}
DEFAULT_VER = 2
def _salt_callback(func):
LOG.clear()
def _call_callback(*a, **kw):
ret, out = None, ''
status = base_status.copy()
directory = kw.get('directory', '.')
onlyif = kw.get('onlyif', None)
unless = kw.get('unless', None)
runas = kw.get('runas', None)
env = kw.get('env', ())
try:
# may rise ResultTransmission
_check_onlyif_unless(onlyif,
unless,
directory=directory,
runas=runas,
env=env)
comment, st, out = '', True, None
if not status['status']:
# may rise ResultTransmission
out = func(*a, **kw)
if isinstance(out, dict):
comment = out.get('comment', '')
out = out.get('out', None)
status = _set_status(status, status=st, comment=comment, out=out)
except ResultTransmission, ex:
status = ex.args[0]
except Exception:
trace = traceback.format_exc(None)
LOG.error(trace)
_invalid(status)
LOG.clear()
return status
return _call_callback
class Logger():
levels = ('info', 'warn', 'debug', 'error')
def __init__(self):
self._msgs = []
self._by_level = {}
def _log(self, level, msg):
if not level in self._by_level:
self._by_level[level] = []
self._msgs.append((level, msg))
self._by_level[level].append(msg)
def debug(self, msg):
self._log('debug', msg)
def info(self, msg):
self._log('info', msg)
def error(self, msg):
self._log('error', msg)
def warn(self, msg):
self._log('warn', msg)
warning = warn
def clear(self):
for i in self._by_level:
self._by_level[i] = []
for i in range(len(self._msgs)):
self._msgs.pop()
def get_logs(self, level):
return self._by_level.get(level, [])
@property
def messages(self):
return self._msgs
@property
def by_level(self):
return self._by_level
LOG = Logger()
def _set_status(m,
comment=INVALID_RESPONSE,
status=False,
out=None):
'''
Assign status data to a dict
'''
m['out'] = out
m['status'] = status
m['logs'] = LOG.messages[:]
m['logs_by_level'] = LOG.by_level.copy()
outlog, outlog_by_level = u'', u''
m['comment'] = comment
if out and isinstance(out, string_types):
outlog += hr
outlog += u'OUTPUT:\n'
outlog += u'{0}\n'.format(out)
outlog += hr
if m['logs']:
outlog += hr
outlog += u'Log summary:\n'
outlog += hr
outlog_by_level += hr
outlog_by_level += u'Log summary by level:\n'
outlog_by_level += hr
for level, msg in m['logs']:
outlog += '\n{0}: {1}\n'.format(level.upper(), msg)
for logger in 'error', 'warn', 'info', 'debug':
logs = m['logs_by_level'].get(logger, [])
if logs:
outlog_by_level += '\n{0}:\n'.format(logger.upper())
outlog_by_level += '\n'.join(logs)
outlog_by_level += '\n'
outlog += hr
m['outlog'] = outlog
m['outlog_by_level'] = outlog_by_level
return m
def _invalid(m, comment=INVALID_RESPONSE, out=None):
'''
Return invalid status
'''
return _set_status(m, status=False, comment=comment, out=out)
def _valid(m, comment=VALID_RESPONSE, out=None):
'''
Return valid status
'''
return _set_status(m, status=True, comment=comment, out=out)
def _Popen(command,
output=False,
directory='.',
runas=None,
env=(),
exitcode=0):
"""Run a command
output
return output if true
directory
directory to execute in
runas
user used to run buildout as
env
environment variables to set when running
exitcode
fails if cmd does not return this exit code
(set to None to disable check)
"""
ret = None
directory = os.path.abspath(directory)
if isinstance(command, list):
command = ' '.join(command)
LOG.debug(u'Running {0}'.format(command))
ret = __salt__['cmd.run_all'](command, cwd=directory, runas=runas, env=env)
out = ret['stdout'] + '\n\n' + ret['stderr']
if (exitcode is not None) and (ret['retcode'] != exitcode):
raise BuildoutError(out)
ret['output'] = out
if output:
ret = out
return ret
class ResultTransmission(Exception):
"""General Buildout Error."""
class BuildoutError(CommandExecutionError):
"""General Buildout Error."""
class MrDeveloperError(BuildoutError):
"""Arrives when mr.developer fails"""
def _has_old_distribute(python=sys.executable, runas=None, env=()):
old_distribute = False
try:
cmd = [python,
"-c",
"\"import pkg_resources;"
"print pkg_resources."
"get_distribution(\'distribute\').location\""]
#LOG.debug('Run %s' % " ".join(cmd))
ret = _Popen(cmd, runas=runas, env=env, output=True)
if 'distribute-0.6' in ret:
old_distribute = True
except Exception:
old_distribute = False
return old_distribute
def _has_setuptools7(python=sys.executable, runas=None, env=()):
new_st = False
try:
cmd = [python,
"-c",
"\"import pkg_resources;"
"print not pkg_resources."
"get_distribution('setuptools').version.startswith('0.6')\""]
#LOG.debug('Run %s' % " ".join(cmd))
ret = _Popen(cmd, runas=runas, env=env, output=True)
if 'true' in ret.lower():
new_st = True
except Exception:
new_st = False
return new_st
def _find_cfgs(path, cfgs=None):
"""Find all buildout configs in a sudirectory
only builout.cfg and etc/buildout.cfg are valid in::
path
directory where to start to search
cfg
a optionnal list to append to
.
buildout.cfg
etc
   buildout.cfg
foo
   buildout.cfg
var
buildout.cfg
"""
ignored = ['var', 'parts']
dirs = []
if not cfgs:
cfgs = []
for i in os.listdir(path):
fi = os.path.join(path, i)
if fi.endswith('.cfg') and os.path.isfile(fi):
cfgs.append(fi)
if os.path.isdir(fi) and (not i in ignored):
dirs.append(fi)
for fpath in dirs:
for p, ids, ifs in os.walk(fpath):
for i in ifs:
if i.endswith('.cfg'):
cfgs.append(os.path.join(p, i))
return cfgs
def _get_bootstrap_content(directory="."):
"""Get the current bootstrap.py script content"""
try:
fic = open(
os.path.join(
os.path.abspath(directory), 'bootstrap.py'))
oldcontent = fic.read()
fic.close()
except:
oldcontent = ""
return oldcontent
def _get_buildout_ver(directory="."):
"""Check for buildout versions
In any cases, check for a version pinning
Also check for buildout.dumppickedversions which is buildout1 specific
Also check for the version targeted by the local bootstrap file
Take as default buildout2
directory
directory to execute in
"""
directory = os.path.abspath(directory)
buildoutver = 2
try:
files = _find_cfgs(directory)
for f in files:
fic = open(f)
buildout1re = re.compile('^zc\.buildout\s*=\s*1', re_f)
dfic = fic.read()
if (
('buildout.dumppick' in dfic)
or
(buildout1re.search(dfic))
):
buildoutver = 1
fic.close()
bcontent = _get_bootstrap_content(directory)
if (
'--download-base' in bcontent
or '--setup-source' in bcontent
or '--distribute' in bcontent
):
buildoutver = 1
except:
pass
return buildoutver
def _get_bootstrap_url(directory):
"""Get the most appropriate download url for the bootstrap script
directory
directory to execute in
"""
v = _get_buildout_ver(directory)
return _url_versions.get(v, _url_versions[DEFAULT_VER])
def _dot_buildout(directory):
"""Get the local marker directory
directory
directory to execute in
"""
return os.path.join(
os.path.abspath(directory), '.buildout')
@_salt_callback
def upgrade_bootstrap(directory=".",
onlyif=None,
unless=None,
runas=None,
env=(),
offline=False,
buildout_ver=None):
"""Upgrade current bootstrap.py with the last released one.
Indeed, when we first run a buildout, a common source of problem
is to have an locally stale boostrap, we just try rab a new copy
directory
directory to execute in
offline
are we executing buildout in offline mode
buildout_ver
forcing to use a specific buildout version (1 | 2)
onlyif
Only execute cmd if statement on the host return 0
unless
Do not execute cmd if statement on the host return 0
CLI Example:
.. code-block:: bash
salt '*' buildout.upgrade_bootstrap /srv/mybuildout
"""
if buildout_ver:
booturl = _url_versions[buildout_ver]
else:
buildout_ver = _get_buildout_ver(directory)
booturl = _get_bootstrap_url(directory)
LOG.debug('Using %s' % booturl)
# try to donwload an uptodate bootstrap
# set defaulttimeout
# and add possible content
directory = os.path.abspath(directory)
b_py = os.path.join(directory, 'bootstrap.py')
comment = ''
try:
oldcontent = _get_bootstrap_content(directory)
dbuild = _dot_buildout(directory)
data = oldcontent
updated = False
dled = False
if not offline:
try:
if not os.path.isdir(dbuild):
os.makedirs(dbuild)
# only try to download once per buildout checkout
open(os.path.join(
dbuild,
'{0}.updated_bootstrap'.format(buildout_ver)))
except Exception:
LOG.info('Bootstrap updated from repository')
data = urllib2.urlopen(booturl).read()
updated = True
dled = True
if not 'socket.setdefaulttimeout' in data:
updated = True
ldata = data.splitlines()
ldata.insert(1, 'import socket;socket.setdefaulttimeout(2)')
data = '\n'.join(ldata)
if updated:
comment = 'Bootstrap updated'
fic = open(b_py, 'w')
fic.write(data)
fic.close()
if dled:
afic = open(os.path.join(
dbuild, '{0}.updated_bootstrap'.format(buildout_ver)
), 'w')
afic.write('foo')
afic.close()
except:
if oldcontent:
fic = open(b_py, 'w')
fic.write(oldcontent)
fic.close()
return {'comment': comment}
@_salt_callback
def bootstrap(directory=".",
config='buildout.cfg',
python=sys.executable,
onlyif=None,
unless=None,
runas=None,
env=(),
distribute=None,
buildout_ver=None,
test_release=False,
offline=False,
new_st=None):
"""Run the buildout bootstrap dance (python bootstrap.py)
directory
directory to execute in
config
alternative buildout configuration file to use
runas
User used to run buildout as
env
environment variables to set when running
buildout_ver
force a specific buildout version (1 | 2)
test_release
buildout accept test release
offline
are we executing buildout in offline mode
distribute
Forcing use of distribute
new_set
Forcing use of setuptools >= 0.7
python
path to a python executable to use in place of default (salt one)
onlyif
Only execute cmd if statement on the host return 0
unless
Do not execute cmd if statement on the host return 0
CLI Example:
.. code-block:: bash
salt '*' buildout.bootstrap /srv/mybuildout
"""
directory = os.path.abspath(directory)
dbuild = _dot_buildout(directory)
bootstrap_args = ''
has_distribute = _has_old_distribute(python=python, runas=runas, env=env)
has_new_st = _has_setuptools7(python=python, runas=runas, env=env)
if (
has_distribute and has_new_st
and not distribute and new_st
):
new_st = True
distribute = False
if (
has_distribute and has_new_st
and not distribute and new_st
):
new_st = True
distribute = False
if (
has_distribute and has_new_st
and distribute and not new_st
):
new_st = True
distribute = False
if (
has_distribute and has_new_st
and not distribute and not new_st
):
new_st = True
distribute = False
if (
not has_distribute and has_new_st
and not distribute and new_st
):
new_st = True
distribute = False
if (
not has_distribute and has_new_st
and not distribute and new_st
):
new_st = True
distribute = False
if (
not has_distribute and has_new_st
and distribute and not new_st
):
new_st = True
distribute = False
if (
not has_distribute and has_new_st
and not distribute and not new_st
):
new_st = True
distribute = False
if (
has_distribute and not has_new_st
and not distribute and new_st
):
new_st = True
distribute = False
if (
has_distribute and not has_new_st
and not distribute and new_st
):
new_st = True
distribute = False
if (
has_distribute and not has_new_st
and distribute and not new_st
):
new_st = False
distribute = True
if (
has_distribute and not has_new_st
and not distribute and not new_st
):
new_st = False
distribute = True
if (
not has_distribute and not has_new_st
and not distribute and new_st
):
new_st = True
distribute = False
if (
not has_distribute and not has_new_st
and not distribute and new_st
):
new_st = True
distribute = False
if (
not has_distribute and not has_new_st
and distribute and not new_st
):
new_st = False
distribute = True
if (
not has_distribute and not has_new_st
and not distribute and not new_st
):
new_st = True
distribute = False
if new_st and distribute:
distribute = False
if new_st:
distribute = False
LOG.warning(u'Forcing to use setuptools as we have setuptools >= 0.7')
if distribute:
new_st = False
if buildout_ver == 1:
LOG.warning(u'Using distribute !')
bootstrap_args += ' %s' % '--distribute'
if not os.path.isdir(dbuild):
os.makedirs(dbuild)
upgrade_bootstrap(directory,
offline=offline,
buildout_ver=buildout_ver)
# be sure which buildout bootstrap we have
b_py = os.path.join(directory, 'bootstrap.py')
fic = open(b_py)
content = fic.read()
fic.close()
if (
(False != test_release)
and ' --accept-buildout-test-releases' in content
):
bootstrap_args += ' --accept-buildout-test-releases'
if config and '"-c"' in content:
bootstrap_args += " -c %s" % config
cmd = '%s bootstrap.py %s ' % (python, bootstrap_args,)
ret = _Popen(cmd, directory=directory, runas=runas, env=env)
output = ret['output']
return {'comment': cmd, 'out': output}
@_salt_callback
def run_buildout(directory=".",
config='buildout.cfg',
parts=None,
onlyif=None,
unless=None,
offline=False,
newest=True,
runas=None,
env=(),
verbose=False,
debug=False,
python=sys.executable):
"""
directory
directory to execute in
config
alternative buildout configuration file to use
offline
are we executing buildout in offline mode
runas
user used to run buildout as
env
environment variables to set when running
onlyif
Only execute cmd if statement on the host return 0
unless
Do not execute cmd if statement on the host return 0
newest
run buildout in newest mode
force
run buildout unconditionnaly
verbose
run buildout in verbose mode (-vvvvv)
"""
directory = os.path.abspath(directory)
bcmd = os.path.join(directory, 'bin', 'buildout')
installed_cfg = os.path.join(directory, '.installed.cfg')
argv = []
if verbose:
LOG.debug(u'Buildout is running in verbose mode!')
argv.append('-vvvvvvv')
if newest and not os.path.exists(installed_cfg):
argv.append('-N')
else:
LOG.debug(u'Buildout is running in newest mode!')
argv.append('-n')
if offline:
LOG.debug(u'Buildout is running in offline mode!')
argv.append('-o')
if debug:
LOG.debug(u'Buildout is running in debug mode!')
argv.append('-D')
cmds, outputs = [], []
if parts:
for part in parts:
LOG.info(u'Installing single part: {0}'.format(part))
cmd = '{0} -c {1} {2} install {3}'.format(
bcmd, config, ' '.join(argv), part)
cmds.append(cmd)
outputs.append(
_Popen(
cmd, directory=directory,
runas=runas,
env=env,
output=True)
)
else:
LOG.info(u'Installing all buildout parts')
cmd = '{0} -c {1} {2}'.format(
bcmd, config, ' '.join(argv))
cmds.append(cmd)
outputs.append(
_Popen(cmd, directory=directory, runas=runas, env=env, output=True)
)
return {'comment': '\n'.join(cmds),
'out': '\n'.join(outputs)}
def _merge_statuses(statuses):
status = base_status.copy()
status['status'] = None
status['out'] = []
for st in statuses:
if status['status'] is not False:
status['status'] = st['status']
out = st['out']
comment = st['comment']
logs = st['logs']
logs_by_level = st['logs_by_level']
outlog_by_level = st['outlog_by_level']
outlog = st['outlog']
if out:
if not status['out']:
status['out'] = ''
status['out'] += '\n'
status['out'] += hr
status['out'] += '{0}\n'.format(out)
status['out'] += hr
if comment:
if not status['comment']:
status['comment'] = ''
status['comment'] += '\n{0}\n'.format(comment)
if outlog:
if not status['outlog']:
status['outlog'] = ''
status['outlog'] += '\n{0}'.format(hr)
status['outlog'] += outlog
if outlog_by_level:
if not status['outlog_by_level']:
status['outlog_by_level'] = ''
status['outlog_by_level'] += '\n{0}'.format(hr)
status['outlog_by_level'] += outlog_by_level
status['logs'].extend(logs)
for log in logs_by_level:
if not log in status['logs_by_level']:
status['logs_by_level'][log] = []
status['logs_by_level'][log].extend(logs_by_level[log])
return status
@_salt_callback
def buildout(directory=".",
config='buildout.cfg',
parts=None,
runas=None,
env=(),
buildout_ver=None,
test_release=False,
distribute=None,
new_st=None,
offline=False,
newest=False,
python=sys.executable,
debug=False,
verbose=False,
onlyif=None,
unless=None):
"""Run buildout in a directory
directory
directory to execute in
config
buildout config to use
parts
specific buildout parts to run
runas
user used to run buildout as
env
environment variables to set when running
buildout_ver
force a specific buildout version (1 | 2)
test_release
buildout accept test release
new_set
Forcing use of setuptools >= 0.7
distribute
use distribute over setuptools if possible
offline
does buildout run offline
python
python to use
debug
run buildout with -D debug flag
onlyif
Only execute cmd if statement on the host return 0
unless
Do not execute cmd if statement on the host return 0
newest
run buildout in newest mode
verbose
run buildout in verbose mode (-vvvvv)
"""
LOG.info(
'Running buildout in %s (%s)' % (directory,
config))
boot_ret = bootstrap(directory,
config=config,
buildout_ver=buildout_ver,
test_release=test_release,
offline=offline,
new_st=new_st,
env=env,
runas=runas,
distribute=distribute,
python=python)
buildout_ret = run_buildout(directory=directory,
config=config,
parts=parts,
offline=offline,
newest=newest,
runas=runas,
env=env,
verbose=verbose,
debug=debug)
# signal the decorator or our return
raise ResultTransmission(_merge_statuses([boot_ret, buildout_ret]))
def _check_onlyif_unless(onlyif, unless, directory, runas=None, env=()):
status = base_status.copy()
if os.path.exists(directory):
directory = os.path.abspath(directory)
status['status'] = False
retcode = __salt__['cmd.retcode']
if onlyif is not None:
if not isinstance(onlyif, string_types):
if not onlyif:
_valid(status, 'onlyif execution failed')
elif isinstance(onlyif, string_types):
if retcode(onlyif, cwd=directory, runas=runas, env=env) != 0:
_valid(status, 'onlyif execution failed')
if unless is not None:
if not isinstance(unless, string_types):
if unless:
_valid(status, 'unless execution succeeded')
elif isinstance(unless, string_types):
if retcode(unless, cwd=directory, runas=runas, env=env) == 0:
_valid(status, 'unless execution succeeded')
if status['status']:
raise ResultTransmission(status)
# vim:set et sts=4 ts=4 tw=80:

223
salt/states/zcbuildout.py Normal file
View File

@ -0,0 +1,223 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
Management of zc.buildout
===========================
This module is inspired from minitage's buildout maker
(https://github.com/minitage/minitage/blob/master/src/minitage/core/makers/buildout.py)
.. versionadded:: Boron
.. note::
This state module is beta; the API is subject to change and no promise
as to performance or functionality is yet present
Available Functions
-------------------
- built
.. code-block:: yaml
installed1
buildout.installed:
- name: /path/to/buildout
installed2
buildout.installed:
- name: /path/to/buildout
- parts:
- a
- b
- python: /path/to/pythonpath/bin/python
- unless: /bin/test_something_installed
- onlyif: /bin/test_else_installed
'''
# Import python libs
import re
import sys
# Import salt libs
from salt._compat import string_types
# Define the module's virtual name
__virtualname__ = 'buildout'
def __virtual__():
'''
Only load if zc.buildout libs available
'''
if True:
return __virtualname__
return False
INVALID_RESPONSE = 'We did not get any expectable answer from docker'
VALID_RESPONSE = ''
NOTSET = object()
MAPPING_CACHE = {}
FN_CACHE = {}
def __salt(fn):
if not fn in FN_CACHE:
FN_CACHE[fn] = __salt__[fn]
return FN_CACHE[fn]
def _ret_status(exec_status=None,
name='',
comment='',
result=None,
quiet=False,
changes=None):
if not changes:
changes = {}
if exec_status is None:
exec_status = {}
if exec_status:
if result is None:
result = exec_status['status']
scomment = exec_status.get('comment', None)
if scomment:
comment += '\n' + scomment
out = exec_status.get('out', '')
if not quiet:
if out:
if isinstance(out, string_types):
comment += '\n' + out
outlog = exec_status.get('outlog', None)
if outlog:
if isinstance(outlog, string_types):
comment += '\n' + outlog
return {
'changes': changes,
'result': result,
'name': name,
'comment': comment,
}
def _valid(exec_status=None, name='', comment='', changes=None):
return _ret_status(exec_status=exec_status,
comment=comment,
name=name,
changes=changes,
result=True)
def _invalid(exec_status=None, name='', comment='', changes=None):
return _ret_status(exec_status=exec_status,
comment=comment,
name=name,
changes=changes,
result=False)
def installed(name,
config='buildout.cfg',
quiet=False,
parts=None,
runas=None,
env=(),
buildout_ver=None,
test_release=False,
distribute=None,
new_st=None,
offline=False,
newest=False,
python=sys.executable,
debug=False,
verbose=False,
unless=None,
onlyif=None):
'''
Install buildout in a specific directory
It is a thin wrapper to modules.buildout.buildout
name
directory to execute in
quiet
do not output console & logs
config
buildout config to use (default: buildout.cfg)
parts
specific buildout parts to run
runas
user used to run buildout as
env
environment variables to set when running
buildout_ver
force a specific buildout version (1 | 2)
test_release
buildout accept test release
new_st
Forcing use of setuptools >= 0.7
distribute
use distribute over setuptools if possible
offline
does buildout run offline
python
python to use
debug
run buildout with -D debug flag
onlyif
Only execute cmd if statement on the host return 0
unless
Do not execute cmd if statement on the host return 0
newest
run buildout in newest mode
verbose
run buildout in verbose mode (-vvvvv)
'''
try:
test_release = int(test_release)
except:
test_release = None
func = __salt('buildout.buildout')
a, kw = [], dict(
directory=name,
config=config,
parts=parts,
runas=runas,
env=env,
buildout_ver=buildout_ver,
test_release=test_release,
distribute=distribute,
new_st=new_st,
offline=offline,
newest=newest,
python=python,
debug=debug,
verbose=verbose,
onlyif=onlyif,
unless=unless,
)
status = _ret_status(func(*a, **kw), name, quiet=quiet)
return status
# vim:set et sts=4 ts=4 tw=80:

View File

@ -0,0 +1,6 @@
[buildout]
parts=a
[a]
recipe=zc.recipe.egg
eggs=zc.buildout
[versions]

View File

@ -0,0 +1,8 @@
[buildout]
parts=a
versions=versions
[a]
recipe=zc.recipe.egg
eggs=zc.buildout
[versions]
distribute=0.6.43

View File

@ -0,0 +1,12 @@
[buildout]
parts=a
[c]
recipe=zc.recipe.egg
eggs=zc.buildout
[b]
recipe=zc.recipe.egg
eggs=zc.buildout
[a]
recipe=zc.recipe.egg
eggs=zc.buildout
[versions]

View File

@ -0,0 +1,12 @@
[buildout]
parts=a
[c]
recipe=zc.recipe.egg
eggs=zc.buildout
[b]
recipe=zc.recipe.egg
eggs=zc.buildout
[a]
recipe=zc.recipe.egg
eggs=zc.buildout
[versions]

View File

@ -0,0 +1,4 @@
[buildout]
parts=a
[a]
recipe=AN ERROR !!!

View File

@ -0,0 +1 @@
foo

View File

@ -0,0 +1,2 @@
[buildout]
[versions]

View File

@ -0,0 +1,2 @@
[buildout]
extensions = buildout.dumppickedversions

View File

@ -0,0 +1,3 @@
[buildout]
[versions]
zc.buildout=1.7

View File

@ -0,0 +1,2 @@
[buildout]
[versions]

View File

@ -0,0 +1,2 @@
[buildout]
[versions]

View File

@ -0,0 +1,3 @@
[buildout]
[versions]
zc.buildout=2

View File

@ -0,0 +1,387 @@
# -*- coding: utf-8 -*-
# Import python libs
import os
import tempfile
import urllib2
import textwrap
from distutils.dir_util import copy_tree
# Import Salt Testing libs
from salttesting import TestCase
from salttesting.helpers import (
ensure_in_syspath,
requires_network,
)
from salttesting.mock import MagicMock
ensure_in_syspath('../../')
import integration
import shutil
# Import Salt libs
from salt.modules import zcbuildout as buildout
from salt.modules import cmdmod as cmd
from salt.exceptions import CommandExecutionError, SaltInvocationError
ROOT = os.path.join(os.path.dirname(integration.__file__),
'files/file/base/buildout')
buildout.__salt__ = {
'cmd.run_all': cmd.run_all,
'cmd.run': cmd.run,
'cmd.retcode': cmd.retcode,
}
boot_init = {
1: [
'var/ver/1/bootstrap/bootstrap.py',
],
2: [
'var/ver/2/bootstrap/bootstrap.py',
'b/bootstrap.py',
]}
class Base(TestCase):
@classmethod
def setUpClass(cls):
cls.rdir = tempfile.mkdtemp()
cls.tdir = os.path.join(cls.rdir, 'test')
for i in buildout._url_versions:
p = os.path.join(
cls.rdir, '{}_bootstrap.py'.format(i)
)
fic = open(p, 'w')
fic.write(
urllib2.urlopen(buildout._url_versions[i]).read())
fic.close()
@classmethod
def tearDownClass(cls):
if os.path.isdir(cls.rdir):
shutil.rmtree(cls.rdir)
def setUp(self):
super(Base, self).setUp()
self._remove_dir()
shutil.copytree(ROOT, self.tdir)
for i in boot_init:
p = os.path.join(
self.rdir, '{}_bootstrap.py'.format(i)
)
for f in boot_init[i]:
shutil.copy2(p, os.path.join(self.tdir, f))
def tearDown(self):
super(Base, self).tearDown()
self._remove_dir()
def _remove_dir(self):
if os.path.isdir(self.tdir):
shutil.rmtree(self.tdir)
class BuildoutTestCase(Base):
@requires_network()
def test_onlyif_unless(self):
b_dir = os.path.join(self.tdir, 'b')
ret = buildout.buildout(b_dir, onlyif='/bin/false')
self.assertTrue(ret['comment'] == 'onlyif execution failed')
self.assertTrue(ret['status'] is True)
ret = buildout.buildout(b_dir, unless='/bin/true')
self.assertTrue(ret['comment'] == 'unless execution succeeded')
self.assertTrue(ret['status'] is True)
@requires_network()
def test_salt_callback(self):
@buildout._salt_callback
def callback1(a, b=1):
for i in buildout.LOG.levels:
getattr(buildout.LOG, i)('{0}bar'.format(i[0]))
return 'foo'
@buildout._salt_callback
def callback2(a, b=1):
raise Exception('foo')
ret1 = callback1(1, b=3)
self.assertEqual(ret1['status'], True)
self.assertEqual(ret1['logs_by_level']['warn'], ['wbar'])
self.assertEqual(ret1['comment'], '')
self.assertTrue(
u''
u'OUTPUT:\n'
u'foo\n'
u''
in ret1['outlog']
)
self.assertTrue(u'Log summary:\n' in ret1['outlog'])
self.assertTrue(
u'\n'
u'INFO: ibar\n'
u'\n'
u'WARN: wbar\n'
u'\n'
u'DEBUG: dbar\n'
u'\n'
u'ERROR: ebar\n'
in ret1['outlog']
)
self.assertTrue('by level' in ret1['outlog_by_level'])
self.assertEqual(ret1['out'], 'foo')
ret2 = callback2(2, b=6)
self.assertEqual(ret2['status'], False)
self.assertTrue(
ret2['logs_by_level']['error'][0].startswith('Traceback'))
self.assertTrue(
'We did not get any '
'expectable answer '
'from buildout' in ret2['comment'])
self.assertEqual(ret2['out'], None)
for l in buildout.LOG.levels:
self.assertTrue(0 == len(buildout.LOG.by_level[l]))
@requires_network()
def test_get_bootstrap_url(self):
for p in [
os.path.join(self.tdir, 'var/ver/1/dumppicked'),
os.path.join(self.tdir, 'var/ver/1/bootstrap'),
os.path.join(self.tdir, 'var/ver/1/versions'),
]:
self.assertEqual(buildout._url_versions[1],
buildout._get_bootstrap_url(p),
"b1 url for {0}".format(p))
for p in [
os.path.join(self.tdir, '/non/existing'),
os.path.join(self.tdir, 'var/ver/2/versions'),
os.path.join(self.tdir, 'var/ver/2/bootstrap'),
os.path.join(self.tdir, 'var/ver/2/default'),
]:
self.assertEqual(buildout._url_versions[2],
buildout._get_bootstrap_url(p),
"b2 url for {0}".format(p))
@requires_network()
def test_get_buildout_ver(self):
for p in [
os.path.join(self.tdir, 'var/ver/1/dumppicked'),
os.path.join(self.tdir, 'var/ver/1/bootstrap'),
os.path.join(self.tdir, 'var/ver/1/versions'),
]:
self.assertEqual(1,
buildout._get_buildout_ver(p),
"1 for {0}".format(p))
for p in [
os.path.join(self.tdir, '/non/existing'),
os.path.join(self.tdir, 'var/ver/2/versions'),
os.path.join(self.tdir, 'var/ver/2/bootstrap'),
os.path.join(self.tdir, 'var/ver/2/default'),
]:
self.assertEqual(2,
buildout._get_buildout_ver(p),
"2 for {0}".format(p))
@requires_network()
def test_get_bootstrap_content(self):
self.assertEqual(
'',
buildout._get_bootstrap_content(
os.path.join(self.tdir, '/non/existing'))
)
self.assertEqual(
'',
buildout._get_bootstrap_content(
os.path.join(self.tdir, 'var/tb/1')))
self.assertEqual(
'foo\n',
buildout._get_bootstrap_content(
os.path.join(self.tdir, 'var/tb/2')))
@requires_network()
def test_logger_clean(self):
buildout.LOG.clear()
# nothing in there
self.assertTrue(
True not in
[len(buildout.LOG.by_level[a]) > 0
for a in buildout.LOG.by_level])
buildout.LOG.info('foo')
self.assertTrue(
True in
[len(buildout.LOG.by_level[a]) > 0
for a in buildout.LOG.by_level])
buildout.LOG.clear()
self.assertTrue(
True not in
[len(buildout.LOG.by_level[a]) > 0
for a in buildout.LOG.by_level])
@requires_network()
def test_logger_loggers(self):
buildout.LOG.clear()
# nothing in there
for i in buildout.LOG.levels:
getattr(buildout.LOG, i)('foo')
getattr(buildout.LOG, i)('bar')
getattr(buildout.LOG, i)('moo')
self.assertTrue(len(buildout.LOG.by_level[i]) == 3)
self.assertEqual(buildout.LOG.by_level[i][0], 'foo')
self.assertEqual(buildout.LOG.by_level[i][-1], 'moo')
@requires_network()
def test__find_cfgs(self):
self.assertEqual(
[a.replace(ROOT, '')
for a in buildout._find_cfgs(ROOT)],
['/buildout.cfg',
'/c/buildout.cfg',
'/etc/buildout.cfg',
'/e/buildout.cfg',
'/b/buildout.cfg',
'/b/bdistribute/buildout.cfg',
'/b/b2/buildout.cfg',
'/foo/buildout.cfg'])
@requires_network()
def test_upgrade_bootstrap(self):
b_dir = os.path.join(self.tdir, 'b')
bpy = os.path.join(b_dir, 'bootstrap.py')
buildout.upgrade_bootstrap(b_dir)
time1 = os.stat(bpy).st_mtime
fic = open(bpy)
data = fic.read()
fic.close()
self.assertTrue('setdefaulttimeout(2)' in data)
flag = os.path.join(b_dir, '.buildout', '2.updated_bootstrap')
self.assertTrue(os.path.exists(flag))
buildout.upgrade_bootstrap(b_dir, buildout_ver=1)
time2 = os.stat(bpy).st_mtime
fic = open(bpy)
data = fic.read()
fic.close()
self.assertTrue('setdefaulttimeout(2)' in data)
flag = os.path.join(b_dir, '.buildout', '1.updated_bootstrap')
self.assertTrue(os.path.exists(flag))
buildout.upgrade_bootstrap(b_dir, buildout_ver=1)
time3 = os.stat(bpy).st_mtime
self.assertNotEqual(time2, time1)
self.assertEqual(time2, time3)
class BuildoutOnlineTestCase(Base):
@classmethod
def setUpClass(cls):
super(BuildoutOnlineTestCase, cls).setUpClass()
cls.ppy_dis = os.path.join(cls.rdir, 'pdistibute')
cls.ppy_st = os.path.join(cls.rdir, 'psetuptools')
cls.ppy_blank = os.path.join(cls.rdir, 'pblank')
cls.py_dis = os.path.join(cls.ppy_dis, 'bin', 'python')
cls.py_st = os.path.join(cls.ppy_st, 'bin', 'python')
cls.py_blank = os.path.join(cls.ppy_blank, 'bin', 'python')
# creating a new setuptools install
ret1 = buildout._Popen((
'virtualenv --no-site-packages {0};'
'{0}/bin/easy_install -U setuptools;'
'{0}/bin/easy_install -U distribute;'
).format(cls.ppy_st))
# creating a distribute based install
ret2 = buildout._Popen((
'virtualenv --no-site-packages {0};'
'{0}/bin/easy_install -U setuptools==0.6c9;'
'{0}/bin/easy_install -U distribute==0.6.43;'
).format(cls.ppy_dis))
# creating a blank based install
ret3 = buildout._Popen((
'virtualenv --no-site-packages --no-setuptools --no-pip {0}'
''.format(cls.ppy_blank)))
assert ret1['retcode'] == 0
assert ret2['retcode'] == 0
assert ret3['retcode'] == 0
@requires_network()
def test_buildout_bootstrap(self):
b_dir = os.path.join(self.tdir, 'b')
bd_dir = os.path.join(self.tdir, 'b', 'bdistribute')
b2_dir = os.path.join(self.tdir, 'b', 'b2')
self.assertTrue(buildout._has_old_distribute(self.py_dis))
self.assertFalse(buildout._has_old_distribute(self.py_blank))
self.assertFalse(buildout._has_old_distribute(self.py_st))
self.assertFalse(buildout._has_setuptools7(self.py_dis))
self.assertTrue(buildout._has_setuptools7(self.py_st))
self.assertFalse(buildout._has_setuptools7(self.py_blank))
ret = buildout.bootstrap(
bd_dir, buildout_ver=1, python=self.py_dis)
comment = ret['outlog']
self.assertTrue('--distribute' in comment)
self.assertTrue('Generated script' in comment)
ret = buildout.bootstrap(b_dir, buildout_ver=1, python=self.py_blank)
comment = ret['outlog']
self.assertTrue('Got setuptools' in comment)
self.assertTrue('Generated script' in comment)
ret = buildout.bootstrap(b_dir, buildout_ver=2, python=self.py_blank)
comment = ret['outlog']
self.assertTrue('setuptools' in comment)
self.assertTrue('Generated script' in comment)
ret = buildout.bootstrap(b_dir, buildout_ver=2, python=self.py_st)
comment = ret['outlog']
self.assertTrue('setuptools' in comment)
self.assertTrue('Generated script' in comment)
ret = buildout.bootstrap(b2_dir, buildout_ver=2, python=self.py_st)
comment = ret['outlog']
self.assertTrue('setuptools' in comment)
self.assertTrue('Creating directory' in comment)
@requires_network()
def test_run_buildout(self):
b_dir = os.path.join(self.tdir, 'b')
ret = buildout.bootstrap(b_dir, buildout_ver=2, python=self.py_st)
self.assertTrue(ret['status'])
ret = buildout.run_buildout(b_dir,
parts=['a', 'b'], python=self.py_st)
out = ret['out']
self.assertTrue('Installing a' in out)
self.assertTrue('Installing b' in out)
@requires_network()
def test_buildout(self):
b_dir = os.path.join(self.tdir, 'b')
ret = buildout.buildout(b_dir, buildout_ver=2, python=self.py_st)
self.assertTrue(ret['status'])
out = ret['out']
comment = ret['comment']
self.assertTrue(ret['status'])
self.assertTrue('Creating directory' in out)
self.assertTrue('Installing a.' in out)
self.assertTrue('psetuptools/bin/python bootstrap.py' in comment)
self.assertTrue('buildout -c buildout.cfg -n' in comment)
ret = buildout.buildout(b_dir,
parts=['a', 'b', 'c'],
buildout_ver=2,
python=self.py_st)
outlog = ret['outlog']
out = ret['out']
comment = ret['comment']
self.assertTrue('Installing single part: a' in outlog)
self.assertTrue('buildout -c buildout.cfg -n install a' in comment)
self.assertTrue('Installing b.' in out)
self.assertTrue('Installing c.' in out)
if __name__ == '__main__':
from integration import run_tests
run_tests(
BuildoutTestCase,
BuildoutOnlineTestCase,
needs_daemon=False)

View File

@ -0,0 +1,96 @@
# -*- coding: utf-8 -*-
import os
import tempfile
import textwrap
from distutils.dir_util import copy_tree
# Import third party libs
import yaml
# Import Salt Testing libs
from salttesting import TestCase
from salttesting.helpers import (
ensure_in_syspath,
requires_network,
)
from salttesting.mock import MagicMock
ensure_in_syspath('../../')
import integration
import shutil
# Import Salt libs
from unit.modules.zcbuildout_test import Base
from salt.modules import zcbuildout as modbuildout
from salt.states import zcbuildout as buildout
from salt.modules import cmdmod as cmd
from salt.exceptions import CommandExecutionError, SaltInvocationError
ROOT = os.path.join(os.path.dirname(integration.__file__),
'files/file/base/buildout')
modbuildout.__env__ = 'base'
modbuildout.__opts__ = {'test': False}
modbuildout.__salt__ = {
'cmd.run_all': cmd.run_all,
'cmd.run': cmd.run,
'cmd.retcode': cmd.retcode,
'buildout.buildout': modbuildout.buildout,
}
buildout.__env__ = 'base'
buildout.__opts__ = {'test': False}
buildout.__salt__ = {
'cmd.run_all': cmd.run_all,
'cmd.run': cmd.run,
'cmd.retcode': cmd.retcode,
'buildout.buildout': modbuildout.buildout,
}
class BuildoutTestCase(Base):
@requires_network()
def test_quiet(self):
c_dir = os.path.join(self.tdir, 'c')
cret = buildout.installed(c_dir, quiet=True)
self.assertTrue(cret['result'])
self.assertFalse('OUTPUT:' in cret['comment'])
self.assertFalse('Log summary:' in cret['comment'])
@requires_network()
def test_error(self):
b_dir = os.path.join(self.tdir, 'e')
ret = buildout.installed(b_dir)
self.assertTrue(
'We did not get any expectable '
'answer from buildout'
in ret['comment'])
self.assertTrue(
'An internal error occurred due to a bug in'
' either zc.buildout '
in ret['comment'])
self.assertFalse(ret['result'])
@requires_network()
def test_installed(self):
b_dir = os.path.join(self.tdir, 'b')
ret = buildout.installed(b_dir, onlyif='/bin/false')
self.assertEqual(ret['comment'], '\nonlyif execution failed')
self.assertEqual(ret['result'], True)
self.assertTrue('/b' in ret['name'])
b_dir = os.path.join(self.tdir, 'b')
ret = buildout.installed(b_dir, unless='/bin/true')
self.assertEqual(ret['comment'], '\nunless execution succeeded')
self.assertEqual(ret['result'], True)
self.assertTrue('/b' in ret['name'])
ret = buildout.installed(b_dir)
self.assertEqual(ret['result'], True)
self.assertTrue('OUTPUT:' in ret['comment'])
self.assertTrue('Log summary:' in ret['comment'])
if __name__ == '__main__':
from integration import run_tests
run_tests(BuildoutTestCase, needs_daemon=False)