mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 08:58:59 +00:00
Merge pull request #8914 from makinacorpus/issue-8856-buildout-support
feat: buildout support
This commit is contained in:
commit
77d61d92a4
@ -27,6 +27,7 @@ Full list of builtin execution modules
|
||||
brew
|
||||
bridge
|
||||
bsd_shadow
|
||||
buildout
|
||||
cassandra
|
||||
chocolatey
|
||||
cmdmod
|
||||
|
6
doc/ref/modules/all/salt.modules.buildout.rst
Normal file
6
doc/ref/modules/all/salt.modules.buildout.rst
Normal file
@ -0,0 +1,6 @@
|
||||
=====================
|
||||
salt.modules.buildout
|
||||
=====================
|
||||
|
||||
.. automodule:: salt.modules.zcbuildout
|
||||
:members:
|
@ -15,6 +15,7 @@ Full list of builtin state modules
|
||||
apt
|
||||
augeas
|
||||
aws_sqs
|
||||
buildout
|
||||
cmd
|
||||
cron
|
||||
ddns
|
||||
|
6
doc/ref/states/all/salt.states.buildout.rst
Normal file
6
doc/ref/states/all/salt.states.buildout.rst
Normal file
@ -0,0 +1,6 @@
|
||||
====================
|
||||
salt.states.buildout
|
||||
====================
|
||||
|
||||
.. automodule:: salt.states.zcbuildout
|
||||
:members:
|
896
salt/modules/zcbuildout.py
Normal file
896
salt/modules/zcbuildout.py
Normal 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
223
salt/states/zcbuildout.py
Normal 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:
|
@ -0,0 +1,6 @@
|
||||
[buildout]
|
||||
parts=a
|
||||
[a]
|
||||
recipe=zc.recipe.egg
|
||||
eggs=zc.buildout
|
||||
[versions]
|
@ -0,0 +1,8 @@
|
||||
[buildout]
|
||||
parts=a
|
||||
versions=versions
|
||||
[a]
|
||||
recipe=zc.recipe.egg
|
||||
eggs=zc.buildout
|
||||
[versions]
|
||||
distribute=0.6.43
|
12
tests/integration/files/file/base/buildout/b/buildout.cfg
Normal file
12
tests/integration/files/file/base/buildout/b/buildout.cfg
Normal 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]
|
12
tests/integration/files/file/base/buildout/c/buildout.cfg
Normal file
12
tests/integration/files/file/base/buildout/c/buildout.cfg
Normal 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]
|
@ -0,0 +1,4 @@
|
||||
[buildout]
|
||||
parts=a
|
||||
[a]
|
||||
recipe=AN ERROR !!!
|
@ -0,0 +1 @@
|
||||
foo
|
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1,2 @@
|
||||
[buildout]
|
||||
[versions]
|
@ -0,0 +1,2 @@
|
||||
[buildout]
|
||||
extensions = buildout.dumppickedversions
|
@ -0,0 +1,3 @@
|
||||
[buildout]
|
||||
[versions]
|
||||
zc.buildout=1.7
|
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1,2 @@
|
||||
[buildout]
|
||||
[versions]
|
@ -0,0 +1,2 @@
|
||||
[buildout]
|
||||
[versions]
|
@ -0,0 +1,3 @@
|
||||
[buildout]
|
||||
[versions]
|
||||
zc.buildout=2
|
387
tests/unit/modules/zcbuildout_test.py
Normal file
387
tests/unit/modules/zcbuildout_test.py
Normal 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)
|
||||
|
96
tests/unit/states/zcbuildout_test.py
Normal file
96
tests/unit/states/zcbuildout_test.py
Normal 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)
|
Loading…
Reference in New Issue
Block a user