Merge pull request #54252 from s0undt3ch/hotfix/git-pillar-2019.2.1

[2019.2.1] More control on spun test deamons on git pillar tests
This commit is contained in:
Daniel Wozniak 2019-08-22 15:13:07 -07:00 committed by GitHub
commit c181f5a633
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 477 additions and 283 deletions

View file

@ -1,3 +1,4 @@
daemon off;
worker_processes 1;
error_log {{ pillar['git_pillar']['config_dir'] }}/error.log;
pid {{ pillar['git_pillar']['config_dir'] }}/nginx.pid;

View file

@ -2,7 +2,6 @@ uwsgi:
socket: 127.0.0.1:{{ pillar['git_pillar']['uwsgi_port'] }}
cgi: {{ pillar['git_pillar']['git-http-backend'] }}
chdir: %d
daemonize: {{ pillar['git_pillar']['config_dir'] }}/uwsgi.log
pidfile: {{ pillar['git_pillar']['config_dir'] }}/uwsgi.pid
# This is required to work around a bug in git-http-backend, introduced in
# git 2.4.4 and worked around with cgi-close-stdin-on-eof in uwsgi >= 2.0.13.

View file

@ -43,22 +43,10 @@
uwsgi:
pip.installed:
- name: 'uwsgi >= 2.0.13'
- name: 'uwsgi == 2.0.18'
- bin_env: {{ venv_dir }}
{#- The env var bellow is EXTREMELY important #}
- env_vars:
UWSGI_PROFILE: cgi
- require:
- virtualenv: {{ venv_dir }}
start_uwsgi:
cmd.run:
- name: '{{ venv_dir }}/bin/uwsgi --yaml {{ config_dir }}/uwsgi.yml'
- require:
- pip: uwsgi
- file: {{ config_dir }}/uwsgi.yml
start_nginx:
cmd.run:
- name: 'nginx -c {{ config_dir }}/nginx.conf'
- require:
- file: {{ config_dir }}/nginx.conf

View file

@ -31,14 +31,3 @@
- group: root
- mode: 755
{%- endif %}
start_sshd:
cmd.run:
- name: '{{ pillar['git_pillar']['sshd_bin'] }} -f {{ sshd_config_dir }}/sshd_config'
- require:
- file: {{ sshd_config_dir }}/sshd_config
- file: {{ sshd_config_dir }}/ssh_host_rsa_key
- file: {{ sshd_config_dir }}/ssh_host_rsa_key.pub
{%- if grains['os_family'] == 'Debian' %}
- file: /var/run/sshd
{%- endif %}

View file

@ -5,34 +5,36 @@ Base classes for gitfs/git_pillar integration tests
# Import python libs
from __future__ import absolute_import, print_function, unicode_literals
import sys
import copy
import errno
import logging
import os
import psutil
import pprint
import shutil
import signal
import tempfile
import textwrap
import time
# Import 3rd-party libs
import psutil
# Import Salt libs
import salt.utils.files
import salt.utils.path
import salt.utils.yaml
import salt.ext.six as six
from salt.fileserver import gitfs
from salt.pillar import git_pillar
from salt.ext.six.moves import range # pylint: disable=redefined-builtin
# Import Salt Testing libs
from tests.support.case import ModuleCase
from tests.support.mixins import LoaderModuleMockMixin, SaltReturnAssertsMixin
from tests.support.paths import TMP
from tests.support.helpers import (
get_unused_localhost_port,
requires_system_grains,
)
from tests.support.unit import SkipTest
from tests.support.mixins import AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin, SaltReturnAssertsMixin
from tests.support.helpers import get_unused_localhost_port, requires_system_grains
from tests.support.runtests import RUNTIME_VARS
from tests.support.mock import patch
from pytestsalt.utils import SaltDaemonScriptBase, terminate_process
log = logging.getLogger(__name__)
@ -72,122 +74,374 @@ _OPTS = {
PROC_TIMEOUT = 10
class ProcessManager(object):
def start_daemon(daemon_cli_script_name,
daemon_config_dir,
daemon_check_port,
daemon_class,
fail_hard=False,
start_timeout=10,
slow_stop=True,
environ=None,
cwd=None,
max_attempts=3,
**kwargs):
'''
Functions used both to set up self-contained SSH/HTTP servers for testing
Returns a running process daemon
'''
wait = 10
def find_proc(self, name=None, search=None):
def _search(proc):
return any([search in x for x in proc.cmdline()])
if name is None and search is None:
raise ValueError('one of name or search is required')
for proc in psutil.process_iter():
if name is not None:
try:
if search is None:
if name in proc.name():
return proc
elif name in proc.name() and _search(proc):
return proc
except psutil.NoSuchProcess:
# Whichever process we are interrogating is no longer alive.
# Skip it and keep searching.
continue
else:
if _search(proc):
return proc
return None
def wait_proc(self, name=None, search=None, timeout=PROC_TIMEOUT):
for idx in range(1, self.wait + 1):
proc = self.find_proc(name=name, search=search)
if proc is not None:
return proc
else:
if idx != self.wait:
log.debug(
'Waiting for %s process (%d of %d)',
name, idx, self.wait
)
time.sleep(1)
else:
log.debug(
'Failed fo find %s process after %d seconds',
name, self.wait
)
raise Exception(
'Unable to find {0} process running from temp config file {1} '
'using psutil'.format(name, search)
log.info('[%s] Starting %s', daemon_class.log_prefix, daemon_class.__name__)
attempts = 0
process = None
while attempts <= max_attempts: # pylint: disable=too-many-nested-blocks
attempts += 1
process = daemon_class(str(daemon_config_dir),
daemon_check_port,
cli_script_name=daemon_cli_script_name,
slow_stop=slow_stop,
environ=environ,
cwd=cwd,
**kwargs)
process.start()
if process.is_alive():
try:
connectable = process.wait_until_running(timeout=start_timeout)
if connectable is False:
connectable = process.wait_until_running(timeout=start_timeout/2)
if connectable is False:
process.terminate()
if attempts >= max_attempts:
raise AssertionError(
'The {} has failed to confirm running status '
'after {} attempts'.format(daemon_class.__name__, attempts))
continue
except Exception as exc: # pylint: disable=broad-except
log.exception('[%s] %s', daemon_class.log_prefix, exc, exc_info=True)
terminate_process(process.pid, kill_children=True, slow_stop=slow_stop)
if attempts >= max_attempts:
raise AssertionError(str(exc))
continue
# A little breathing before returning the process
time.sleep(0.5)
log.info(
'[%s] The %s is running after %d attempts',
daemon_class.log_prefix,
daemon_class.__name__,
attempts
)
return process
else:
terminate_process(process.pid, kill_children=True, slow_stop=slow_stop)
time.sleep(1)
continue
else: # pylint: disable=useless-else-on-loop
# Wrong, we have a return, its not useless
if process is not None:
terminate_process(process.pid, kill_children=True, slow_stop=slow_stop)
raise AssertionError(
'The {} has failed to start after {} attempts'.format(
daemon_class.__name__,
attempts-1
)
)
class SSHDMixin(ModuleCase, ProcessManager, SaltReturnAssertsMixin):
class UwsgiDaemon(SaltDaemonScriptBase):
log_prefix = 'uWSGI'
def __init__(self,
config_dir,
uwsgi_port,
cli_script_name='uwsgi',
**kwargs):
super(UwsgiDaemon, self).__init__(None, # request
{'check_port': uwsgi_port}, # config
config_dir, # config_dir
None, # bin_dir_path
self.__class__.log_prefix, # log_prefix
cli_script_name=cli_script_name,
**kwargs)
def get_script_path(self, script_name):
'''
Returns the path to the script to run
'''
return script_name
def get_base_script_args(self):
'''
Returns any additional arguments to pass to the CLI script
'''
return ['--yaml', os.path.join(self.config_dir, 'uwsgi.yml')]
def get_check_ports(self):
'''
Return a list of ports to check against to ensure the daemon is running
'''
return [self.config['check_port']]
def get_salt_run_event_listener(self):
# Remove this method once pytest-salt get's past 2019.7.20
# Just return a class with a terminate method
class EV(object):
def terminate(self):
pass
return EV()
class NginxDaemon(SaltDaemonScriptBase):
log_prefix = 'Nginx'
def __init__(self,
config_dir,
nginx_port,
cli_script_name='nginx',
**kwargs):
super(NginxDaemon, self).__init__(None, # request
{'check_port': nginx_port}, # config
config_dir, # config_dir
None, # bin_dir_path
self.__class__.log_prefix, # log_prefix
cli_script_name=cli_script_name,
**kwargs)
def get_script_path(self, script_name):
'''
Returns the path to the script to run
'''
return script_name
def get_base_script_args(self):
'''
Returns any additional arguments to pass to the CLI script
'''
return ['-c', os.path.join(self.config_dir, 'nginx.conf')]
def get_check_ports(self):
'''
Return a list of ports to check against to ensure the daemon is running
'''
return [self.config['check_port']]
def get_salt_run_event_listener(self):
# Remove this method once pytest-salt get's past 2019.7.20
# Just return a class with a terminate method
class EV(object):
def terminate(self):
pass
return EV()
class SshdDaemon(SaltDaemonScriptBase):
log_prefix = 'SSHD'
def __init__(self,
config_dir,
sshd_port,
cli_script_name='sshd',
**kwargs):
super(SshdDaemon, self).__init__(None, # request
{'check_port': sshd_port}, # config
config_dir, # config_dir
None, # bin_dir_path
self.__class__.log_prefix, # log_prefix
cli_script_name=cli_script_name,
**kwargs)
def get_script_path(self, script_name):
'''
Returns the path to the script to run
'''
return script_name
def get_base_script_args(self):
'''
Returns any additional arguments to pass to the CLI script
'''
return ['-D', '-e', '-f', os.path.join(self.config_dir, 'sshd_config')]
def get_check_ports(self):
'''
Return a list of ports to check against to ensure the daemon is running
'''
return [self.config['check_port']]
def get_salt_run_event_listener(self):
# Remove this method once pytest-salt get's past 2019.7.20
# Just return a class with a terminate method
class EV(object):
def terminate(self):
pass
return EV()
class SaltClientMixin(ModuleCase):
client = None
@classmethod
@requires_system_grains
def setUpClass(cls, grains=None):
# Cent OS 6 has too old a version of git to handle the make_repo code, as
# it lacks the -c option for git itself.
make_repo = getattr(cls, 'make_repo', None)
if callable(make_repo) and grains['os_family'] == 'RedHat' and grains['osmajorrelease'] < 7:
raise SkipTest('RHEL < 7 has too old a version of git to run these tests')
# Late import
import salt.client
mopts = AdaptedConfigurationTestCaseMixin.get_config('master', from_scratch=True)
cls.user = mopts['user']
cls.client = salt.client.get_local_client(mopts=mopts)
@classmethod
def tearDownClass(cls):
cls.client = None
@classmethod
def cls_run_function(cls, function, *args, **kwargs):
orig = cls.client.cmd('minion',
function,
arg=args,
timeout=300,
kwarg=kwargs)
return orig['minion']
class SSHDMixin(SaltClientMixin, SaltReturnAssertsMixin):
'''
Functions to stand up an SSHD server to serve up git repos for tests.
'''
sshd_proc = None
prep_states_ran = False
known_hosts_setup = False
@classmethod
def prep_server(cls):
cls.sshd_config_dir = tempfile.mkdtemp(dir=TMP)
cls.sshd_config = os.path.join(cls.sshd_config_dir, 'sshd_config')
cls.sshd_port = get_unused_localhost_port()
cls.url = 'ssh://{username}@127.0.0.1:{port}/~/repo.git'.format(
username=cls.username,
port=cls.sshd_port)
cls.url_extra_repo = 'ssh://{username}@127.0.0.1:{port}/~/extra_repo.git'.format(
username=cls.username,
port=cls.sshd_port)
home = '/root/.ssh'
cls.ext_opts = {
'url': cls.url,
'url_extra_repo': cls.url_extra_repo,
'privkey_nopass': os.path.join(home, cls.id_rsa_nopass),
'pubkey_nopass': os.path.join(home, cls.id_rsa_nopass + '.pub'),
'privkey_withpass': os.path.join(home, cls.id_rsa_withpass),
'pubkey_withpass': os.path.join(home, cls.id_rsa_withpass + '.pub'),
'passphrase': cls.passphrase}
def spawn_server(self):
ret = self.run_function(
'state.apply',
mods='git_pillar.ssh',
pillar={'git_pillar': {'git_ssh': self.git_ssh,
'id_rsa_nopass': self.id_rsa_nopass,
'id_rsa_withpass': self.id_rsa_withpass,
'sshd_bin': self.sshd_bin,
'sshd_port': self.sshd_port,
'sshd_config_dir': self.sshd_config_dir,
'master_user': self.master_opts['user'],
'user': self.username}}
)
def setUpClass(cls): # pylint: disable=arguments-differ
super(SSHDMixin, cls).setUpClass()
try:
self.sshd_proc = self.wait_proc(name='sshd',
search=self.sshd_config)
finally:
# Do the assert after we check for the PID so that we can track
# it regardless of whether or not something else in the SLS
# failed (but the SSH server still started).
self.assertSaltTrueReturn(ret)
log.info('%s: prep_server()', cls.__name__)
cls.sshd_bin = salt.utils.path.which('sshd')
cls.sshd_config_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
cls.sshd_config = os.path.join(cls.sshd_config_dir, 'sshd_config')
cls.sshd_port = get_unused_localhost_port()
cls.url = 'ssh://{username}@127.0.0.1:{port}/~/repo.git'.format(
username=cls.username,
port=cls.sshd_port)
cls.url_extra_repo = 'ssh://{username}@127.0.0.1:{port}/~/extra_repo.git'.format(
username=cls.username,
port=cls.sshd_port)
home = '/root/.ssh'
cls.ext_opts = {
'url': cls.url,
'url_extra_repo': cls.url_extra_repo,
'privkey_nopass': os.path.join(home, cls.id_rsa_nopass),
'pubkey_nopass': os.path.join(home, cls.id_rsa_nopass + '.pub'),
'privkey_withpass': os.path.join(home, cls.id_rsa_withpass),
'pubkey_withpass': os.path.join(home, cls.id_rsa_withpass + '.pub'),
'passphrase': cls.passphrase}
if cls.prep_states_ran is False:
ret = cls.cls_run_function(
'state.apply',
mods='git_pillar.ssh',
pillar={'git_pillar': {'git_ssh': cls.git_ssh,
'id_rsa_nopass': cls.id_rsa_nopass,
'id_rsa_withpass': cls.id_rsa_withpass,
'sshd_bin': cls.sshd_bin,
'sshd_port': cls.sshd_port,
'sshd_config_dir': cls.sshd_config_dir,
'master_user': cls.user,
'user': cls.username}}
)
assert next(six.itervalues(ret))['result'] is True
cls.prep_states_ran = True
log.info('%s: States applied', cls.__name__)
if cls.sshd_proc is not None:
if not psutil.pid_exists(cls.sshd_proc.pid):
log.info('%s: sshd started but appears to be dead now. Will try to restart it.',
cls.__name__)
cls.sshd_proc = None
if cls.sshd_proc is None:
cls.sshd_proc = start_daemon(cls.sshd_bin, cls.sshd_config_dir, cls.sshd_port, SshdDaemon)
log.info('\n\n%s: sshd started\n\n\n\n', cls.__name__)
except AssertionError:
cls.tearDownClass()
six.reraise(*sys.exc_info())
if cls.known_hosts_setup is False:
known_hosts_ret = cls.cls_run_function(
'ssh.set_known_host',
user=cls.user,
hostname='127.0.0.1',
port=cls.sshd_port,
enc='ssh-rsa',
fingerprint='fd:6f:7f:5d:06:6b:f2:06:0d:26:93:9e:5a:b5:19:46',
hash_known_hosts=False,
fingerprint_hash_type='md5',
)
if 'error' in known_hosts_ret:
cls.tearDownClass()
raise AssertionError(
'Failed to add key to {0} user\'s known_hosts '
'file: {1}'.format(
cls.master_opts['user'],
known_hosts_ret['error']
)
)
cls.known_hosts_setup = True
@classmethod
def tearDownClass(cls):
if cls.sshd_proc is not None:
log.info('[%s] Stopping %s', cls.sshd_proc.log_prefix, cls.sshd_proc.__class__.__name__)
terminate_process(cls.sshd_proc.pid, kill_children=True, slow_stop=True)
log.info('[%s] %s stopped', cls.sshd_proc.log_prefix, cls.sshd_proc.__class__.__name__)
cls.sshd_proc = None
if cls.prep_states_ran:
ret = cls.cls_run_function('state.single', 'user.absent', name=cls.username, purge=True)
try:
if ret and 'minion' in ret:
ret_data = next(six.itervalues(ret['minion']))
if not ret_data['result']:
log.warning('Failed to delete test account %s', cls.username)
except KeyError:
log.warning('Failed to delete test account. Salt return:\n%s',
pprint.pformat(ret))
shutil.rmtree(cls.sshd_config_dir, ignore_errors=True)
ssh_dir = os.path.expanduser('~/.ssh')
for filename in (cls.id_rsa_nopass,
cls.id_rsa_nopass + '.pub',
cls.id_rsa_withpass,
cls.id_rsa_withpass + '.pub',
cls.git_ssh):
try:
os.remove(os.path.join(ssh_dir, filename))
except OSError as exc:
if exc.errno != errno.ENOENT:
raise
super(SSHDMixin, cls).tearDownClass()
class WebserverMixin(ModuleCase, ProcessManager, SaltReturnAssertsMixin):
class WebserverMixin(SaltClientMixin, SaltReturnAssertsMixin):
'''
Functions to stand up an nginx + uWSGI + git-http-backend webserver to
serve up git repos for tests.
'''
nginx_proc = uwsgi_proc = None
prep_states_ran = False
@classmethod
def prep_server(cls):
def setUpClass(cls): # pylint: disable=arguments-differ
'''
Set up all the webserver paths. Designed to be run once in a
setUpClass function.
'''
cls.root_dir = tempfile.mkdtemp(dir=TMP)
super(WebserverMixin, cls).setUpClass()
cls.root_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
cls.config_dir = os.path.join(cls.root_dir, 'config')
cls.nginx_conf = os.path.join(cls.config_dir, 'nginx.conf')
cls.uwsgi_conf = os.path.join(cls.config_dir, 'uwsgi.yml')
@ -208,50 +462,68 @@ class WebserverMixin(ModuleCase, ProcessManager, SaltReturnAssertsMixin):
for credential_param in ('user', 'password'):
if hasattr(cls, credential_param):
cls.ext_opts[credential_param] = getattr(cls, credential_param)
@requires_system_grains
def spawn_server(self, grains):
auth_enabled = hasattr(self, 'username') and hasattr(self, 'password')
pillar = {'git_pillar': {'config_dir': self.config_dir,
'git_dir': self.git_dir,
'venv_dir': self.venv_dir,
'root_dir': self.root_dir,
'nginx_port': self.nginx_port,
'uwsgi_port': self.uwsgi_port,
auth_enabled = hasattr(cls, 'username') and hasattr(cls, 'password')
pillar = {'git_pillar': {'config_dir': cls.config_dir,
'git_dir': cls.git_dir,
'venv_dir': cls.venv_dir,
'root_dir': cls.root_dir,
'nginx_port': cls.nginx_port,
'uwsgi_port': cls.uwsgi_port,
'auth_enabled': auth_enabled}}
# Different libexec dir for git backend on Debian-based systems
git_core = '/usr/libexec/git-core' \
if grains['os_family'] in ('RedHat') \
else '/usr/lib/git-core'
git_core = '/usr/libexec/git-core'
if not os.path.exists(git_core):
git_core = '/usr/lib/git-core'
pillar['git_pillar']['git-http-backend'] = os.path.join(
git_core,
'git-http-backend')
ret = self.run_function(
'state.apply',
mods='git_pillar.http',
pillar=pillar)
if not os.path.exists(pillar['git_pillar']['git-http-backend']):
self.fail(
'{0} not found. Either git is not installed, or the test '
'class needs to be updated.'.format(
pillar['git_pillar']['git-http-backend']
)
if not os.path.exists(git_core):
cls.tearDownClass()
raise AssertionError(
'{} not found. Either git is not installed, or the test '
'class needs to be updated.'.format(git_core)
)
pillar['git_pillar']['git-http-backend'] = os.path.join(git_core, 'git-http-backend')
try:
self.nginx_proc = self.wait_proc(name='nginx',
search=self.nginx_conf)
self.uwsgi_proc = self.wait_proc(name='uwsgi',
search=self.uwsgi_conf)
finally:
# Do the assert after we check for the PID so that we can track
# it regardless of whether or not something else in the SLS
# failed (but the webserver still started).
self.assertSaltTrueReturn(ret)
if cls.prep_states_ran is False:
ret = cls.cls_run_function('state.apply', mods='git_pillar.http', pillar=pillar)
assert next(six.itervalues(ret))['result'] is True
cls.prep_states_ran = True
log.info('%s: States applied', cls.__name__)
if cls.uwsgi_proc is not None:
if not psutil.pid_exists(cls.uwsgi_proc.pid):
log.warning('%s: uWsgi started but appears to be dead now. Will try to restart it.',
cls.__name__)
cls.uwsgi_proc = None
if cls.uwsgi_proc is None:
cls.uwsgi_proc = start_daemon(cls.uwsgi_bin, cls.config_dir, cls.uwsgi_port, UwsgiDaemon)
log.info('\n\n\n%s: %s started\n\n\n', cls.__name__, cls.uwsgi_bin)
if cls.nginx_proc is not None:
if not psutil.pid_exists(cls.nginx_proc.pid):
log.warning('%s: nginx started but appears to be dead now. Will try to restart it.',
cls.__name__)
cls.nginx_proc = None
if cls.nginx_proc is None:
cls.nginx_proc = start_daemon('nginx', cls.config_dir, cls.nginx_port, NginxDaemon)
log.info('\n\n\n%s: nginx started\n\n\n', cls.__name__)
except AssertionError:
cls.tearDownClass()
six.reraise(*sys.exc_info())
@classmethod
def tearDownClass(cls):
if cls.nginx_proc is not None:
log.info('[%s] Stopping %s', cls.nginx_proc.log_prefix, cls.nginx_proc.__class__.__name__)
terminate_process(cls.nginx_proc.pid, kill_children=True, slow_stop=True)
log.info('[%s] %s stopped', cls.nginx_proc.log_prefix, cls.nginx_proc.__class__.__name__)
cls.nginx_proc = None
if cls.uwsgi_proc is not None:
log.info('[%s] Stopping %s', cls.uwsgi_proc.log_prefix, cls.uwsgi_proc.__class__.__name__)
terminate_process(cls.uwsgi_proc.pid, kill_children=True, slow_stop=True)
log.info('[%s] %s stopped', cls.uwsgi_proc.log_prefix, cls.uwsgi_proc.__class__.__name__)
cls.uwsgi_proc = None
shutil.rmtree(cls.root_dir, ignore_errors=True)
super(WebserverMixin, cls).tearDownClass()
class GitTestBase(ModuleCase):
@ -259,47 +531,10 @@ class GitTestBase(ModuleCase):
Base class for all gitfs/git_pillar tests. Must be subclassed and paired
with either SSHDMixin or WebserverMixin to provide the server.
'''
case = port = bare_repo = base_extra_repo = admin_repo = admin_extra_repo = None
maxDiff = None
git_opts = '-c user.name="Foo Bar" -c user.email=foo@bar.com'
ext_opts = {}
# We need to temporarily skip pygit2 tests on EL7 until the EPEL packager
# updates pygit2 to bring it up-to-date with libgit2.
@requires_system_grains
def is_el7(self, grains):
return grains['os_family'] == 'RedHat' and grains['osmajorrelease'] == 7
# Cent OS 6 has too old a version of git to handle the make_repo code, as
# it lacks the -c option for git itself.
@requires_system_grains
def is_pre_el7(self, grains):
return grains['os_family'] == 'RedHat' and grains['osmajorrelease'] < 7
@classmethod
def setUpClass(cls):
cls.prep_server()
def setUp(self):
# Make the test class available to the tearDownClass so we can clean up
# after ourselves. This (and the gated block below) prevent us from
# needing to spend the extra time creating an ssh server and user and
# then tear them down separately for each test.
self.update_class(self)
if self.is_pre_el7(): # pylint: disable=E1120
self.skipTest(
'RHEL < 7 has too old a version of git to run these tests')
@classmethod
def update_class(cls, case):
'''
Make the test class available to the tearDownClass. Note that this
cannot be defined in a parent class and inherited, as this will cause
the parent class to be modified.
'''
if getattr(cls, 'case') is None:
setattr(cls, 'case', case)
def make_repo(self, root_dir, user='root'):
raise NotImplementedError()
@ -325,6 +560,9 @@ class GitPillarTestBase(GitTestBase, LoaderModuleMockMixin):
'''
Base class for all git_pillar tests
'''
bare_repo = bare_repo_backup = bare_extra_repo = bare_extra_repo_backup = None
admin_repo = admin_repo_backup = admin_extra_repo = admin_extra_repo_backup = None
@requires_system_grains
def setup_loader_modules(self, grains): # pylint: disable=W0221
return {
@ -338,7 +576,7 @@ class GitPillarTestBase(GitTestBase, LoaderModuleMockMixin):
'''
Run git_pillar with the specified configuration
'''
cachedir = tempfile.mkdtemp(dir=TMP)
cachedir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
self.addCleanup(shutil.rmtree, cachedir, ignore_errors=True)
ext_pillar_opts = {'optimization_order': [0, 1, 2]}
ext_pillar_opts.update(
@ -358,12 +596,20 @@ class GitPillarTestBase(GitTestBase, LoaderModuleMockMixin):
)
def make_repo(self, root_dir, user='root'):
log.info('Creating test Git repo....')
self.bare_repo = os.path.join(root_dir, 'repo.git')
self.bare_repo_backup = '{}.backup'.format(self.bare_repo)
self.admin_repo = os.path.join(root_dir, 'admin')
self.admin_repo_backup = '{}.backup'.format(self.admin_repo)
for dirname in (self.bare_repo, self.admin_repo):
shutil.rmtree(dirname, ignore_errors=True)
if os.path.exists(self.bare_repo_backup) and os.path.exists(self.admin_repo_backup):
shutil.copytree(self.bare_repo_backup, self.bare_repo)
shutil.copytree(self.admin_repo_backup, self.admin_repo)
return
# Create bare repo
self.run_function(
'git.init',
@ -498,14 +744,25 @@ class GitPillarTestBase(GitTestBase, LoaderModuleMockMixin):
- mounted.bar
'''))
_push('top_mounted', 'add top_mounted branch')
shutil.copytree(self.bare_repo, self.bare_repo_backup)
shutil.copytree(self.admin_repo, self.admin_repo_backup)
log.info('Test Git repo created.')
def make_extra_repo(self, root_dir, user='root'):
log.info('Creating extra test Git repo....')
self.bare_extra_repo = os.path.join(root_dir, 'extra_repo.git')
self.bare_extra_repo_backup = '{}.backup'.format(self.bare_extra_repo)
self.admin_extra_repo = os.path.join(root_dir, 'admin_extra')
self.admin_extra_repo_backup = '{}.backup'.format(self.admin_extra_repo)
for dirname in (self.bare_extra_repo, self.admin_extra_repo):
shutil.rmtree(dirname, ignore_errors=True)
if os.path.exists(self.bare_extra_repo_backup) and os.path.exists(self.admin_extra_repo_backup):
shutil.copytree(self.bare_extra_repo_backup, self.bare_extra_repo)
shutil.copytree(self.admin_extra_repo_backup, self.admin_extra_repo)
return
# Create bare extra repo
self.run_function(
'git.init',
@ -553,6 +810,23 @@ class GitPillarTestBase(GitTestBase, LoaderModuleMockMixin):
motd: The force will be with you. Always.
'''))
_push('master', 'initial commit')
shutil.copytree(self.bare_extra_repo, self.bare_extra_repo_backup)
shutil.copytree(self.admin_extra_repo, self.admin_extra_repo_backup)
log.info('Extra test Git repo created.')
@classmethod
def tearDownClass(cls):
super(GitPillarTestBase, cls).tearDownClass()
for dirname in (cls.admin_repo,
cls.admin_repo_backup,
cls.admin_extra_repo,
cls.admin_extra_repo_backup,
cls.bare_repo,
cls.bare_repo_backup,
cls.bare_extra_repo,
cls.bare_extra_repo_backup):
if dirname is not None:
shutil.rmtree(dirname, ignore_errors=True)
class GitPillarSSHTestBase(GitPillarTestBase, SSHDMixin):
@ -562,69 +836,22 @@ class GitPillarSSHTestBase(GitPillarTestBase, SSHDMixin):
id_rsa_nopass = id_rsa_withpass = None
git_ssh = '/tmp/git_ssh'
@classmethod
def tearDownClass(cls):
if cls.case is None:
return
if cls.case.sshd_proc is not None:
cls.case.sshd_proc.send_signal(signal.SIGTERM)
cls.case.run_state('user.absent', name=cls.username, purge=True)
for dirname in (cls.sshd_config_dir, cls.case.admin_repo,
cls.case.bare_repo):
if dirname is not None:
shutil.rmtree(dirname, ignore_errors=True)
ssh_dir = os.path.expanduser('~/.ssh')
for filename in (cls.id_rsa_nopass,
cls.id_rsa_nopass + '.pub',
cls.id_rsa_withpass,
cls.id_rsa_withpass + '.pub',
cls.git_ssh):
try:
os.remove(os.path.join(ssh_dir, filename))
except OSError as exc:
if exc.errno != errno.ENOENT:
raise
def setUp(self):
'''
Create the SSH server and user, and create the git repo
'''
log.info('%s.setUp() started...', self.__class__.__name__)
super(GitPillarSSHTestBase, self).setUp()
self.sshd_proc = self.find_proc(name='sshd',
search=self.sshd_config)
self.sshd_bin = salt.utils.path.which('sshd')
if self.sshd_proc is None:
self.spawn_server()
known_hosts_ret = self.run_function(
'ssh.set_known_host',
user=self.master_opts['user'],
hostname='127.0.0.1',
port=self.sshd_port,
enc='ssh-rsa',
fingerprint='fd:6f:7f:5d:06:6b:f2:06:0d:26:93:9e:5a:b5:19:46',
hash_known_hosts=False,
fingerprint_hash_type='md5',
)
if 'error' in known_hosts_ret:
raise Exception(
'Failed to add key to {0} user\'s known_hosts '
'file: {1}'.format(
self.master_opts['user'],
known_hosts_ret['error']
)
)
root_dir = os.path.expanduser('~{0}'.format(self.username))
if root_dir.startswith('~'):
self.fail(
raise AssertionError(
'Unable to resolve homedir for user \'{0}\''.format(
self.username
)
)
self.make_repo(root_dir, user=self.username)
self.make_extra_repo(root_dir, user=self.username)
log.info('%s.setUp() complete.', self.__class__.__name__)
def get_pillar(self, ext_pillar_conf):
'''
@ -648,31 +875,13 @@ class GitPillarHTTPTestBase(GitPillarTestBase, WebserverMixin):
'''
Base class for GitPython and Pygit2 HTTP tests
'''
@classmethod
def tearDownClass(cls):
for proc in (cls.case.nginx_proc, cls.case.uwsgi_proc):
if proc is not None:
try:
proc.send_signal(signal.SIGTERM)
time.sleep(1)
if proc.is_running():
proc.send_signal(signal.SIGKILL)
except psutil.NoSuchProcess:
pass
shutil.rmtree(cls.root_dir, ignore_errors=True)
def setUp(self):
'''
Create and start the webserver, and create the git repo
'''
log.info('%s.setUp() started...', self.__class__.__name__)
super(GitPillarHTTPTestBase, self).setUp()
self.nginx_proc = self.find_proc(name='nginx',
search=self.nginx_conf)
self.uwsgi_proc = self.find_proc(name='uwsgi',
search=self.uwsgi_conf)
if self.nginx_proc is None and self.uwsgi_proc is None:
self.spawn_server() # pylint: disable=E1120
self.make_repo(self.repo_dir)
self.make_extra_repo(self.repo_dir)
log.info('%s.setUp() complete', self.__class__.__name__)

View file

@ -1064,17 +1064,25 @@ def requires_system_grains(func):
case.
'''
@functools.wraps(func)
def decorator(cls):
def decorator(*args, **kwargs):
if not hasattr(requires_system_grains, '__grains__'):
if not hasattr(cls, 'run_function'):
raise RuntimeError(
'{0} does not have the \'run_function\' method which is '
'necessary to collect the system grains'.format(
cls.__class__.__name__
)
)
requires_system_grains.__grains__ = func(cls, grains=cls.run_function('grains.items'))
return requires_system_grains.__grains__
import salt.config
root_dir = tempfile.mkdtemp(dir=TMP)
defaults = salt.config.DEFAULT_MINION_OPTS.copy()
defaults.pop('conf_file')
defaults.update({
'root_dir': root_dir,
'cachedir': 'cachedir',
'sock_dir': 'sock',
'pki_dir': 'pki',
'log_file': 'logs/minion',
'pidfile': 'pids/minion.pid'
})
opts = salt.config.minion_config(None, defaults=defaults)
requires_system_grains.__grains__ = salt.loader.grains(opts)
shutil.rmtree(root_dir, ignore_errors=True)
kwargs['grains'] = requires_system_grains.__grains__
return func(*args, **kwargs)
return decorator

View file

@ -66,7 +66,7 @@ if sys.version_info < (2, 7):
TestResult as _TestResult,
TextTestResult as __TextTestResult
)
from unittest2.case import _id
from unittest2.case import _id, SkipTest
# pylint: enable=import-error
class NewStyleClassMixin(object):
@ -110,7 +110,7 @@ else:
TestResult,
TextTestResult as _TextTestResult
)
from unittest.case import _id
from unittest.case import _id, SkipTest
class TestSuite(_TestSuite):