123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353 |
- # -*- coding: utf-8 -*-
- from __future__ import absolute_import, unicode_literals, print_function
- # Import python libraries
- import distutils.spawn
- import logging
- import os.path
- import shutil
- import sys
- import tempfile
- import textwrap
- # Import Salt Libraries
- import salt.client
- import salt.config
- import salt.utils.yaml as yaml
- # Import pytest libraries
- import pytest
- from pytestsalt.utils import SaltDaemonScriptBase, start_daemon, get_unused_localhost_port
- # Import Pepper libraries
- import pepper
- import pepper.script
- DEFAULT_MASTER_ID = 'pytest-salt-master'
- DEFAULT_MINION_ID = 'pytest-salt-minion'
- log = logging.getLogger(__name__)
- @pytest.fixture(scope='session')
- def install_sshd_server():
- if distutils.spawn.find_executable('sshd'):
- return
- __opts__ = salt.config.minion_config('tests/minion.conf')
- __opts__['file_client'] = 'local'
- caller = salt.client.Caller(mopts=__opts__)
- caller.cmd('pkg.install', 'openssh-server')
- class SaltApi(SaltDaemonScriptBase):
- '''
- Class which runs the salt-api daemon
- '''
- def get_script_args(self):
- return ['-l', 'quiet']
- def get_check_ports(self):
- if 'rest_cherrypy' in self.config:
- return [self.config['rest_cherrypy']['port']]
- if 'rest_tornado' in self.config:
- return [self.config['rest_tornado']['port']]
- @pytest.fixture(scope='session')
- def salt_api_port():
- '''
- Returns an unused localhost port for the api port
- '''
- return get_unused_localhost_port()
- @pytest.fixture(scope='session')
- def pepperconfig(salt_api_port):
- config = textwrap.dedent('''
- [main]
- SALTAPI_URL=http://localhost:{0}/
- SALTAPI_USER=pepper
- SALTAPI_PASS=pepper
- SALTAPI_EAUTH=sharedsecret
- [pepper]
- SALTAPI_URL=http://localhost:{0}/
- SALTAPI_USER=pepper
- SALTAPI_PASS=pepper
- SALTAPI_EAUTH=sharedsecret
- [baduser]
- SALTAPI_URL=http://localhost:{0}/
- SALTAPI_USER=saltdev
- SALTAPI_PASS=saltdev
- SALTAPI_EAUTH=pam
- [badapi]
- SALTAPI_URL=git://localhost:{0}/
- [noapi]
- SALTAPI_USER=pepper
- SALTAPI_PASS=pepper
- SALTAPI_EAUTH=sharedsecret
- [noopts]
- SALTAPI_URL=http://localhost:{0}/
- '''.format(salt_api_port))
- with open('tests/.pepperrc', 'w') as pepper_file:
- print(config, file=pepper_file)
- yield
- os.remove('tests/.pepperrc')
- @pytest.fixture
- def pepper_client(session_salt_api, salt_api_port):
- client = pepper.Pepper('http://localhost:{0}'.format(salt_api_port))
- client.login('pepper', 'pepper', 'sharedsecret')
- return client
- @pytest.fixture
- def tokfile():
- tokdir = tempfile.mkdtemp()
- yield os.path.join(tokdir, 'peppertok.json')
- shutil.rmtree(tokdir)
- @pytest.fixture
- def output_file():
- '''
- Returns the path to the salt master configuration file
- '''
- out_dir = tempfile.mkdtemp()
- yield os.path.join(out_dir, 'output')
- shutil.rmtree(out_dir)
- @pytest.fixture(params=['/run', '/login'])
- def pepper_cli(request, session_salt_api, salt_api_port, output_file, install_sshd_server, session_sshd_server):
- '''
- Wrapper to invoke Pepper with common params and inside an empty env
- '''
- if request.config.getoption('--salt-api-backend') == 'rest_tornado' and request.param == '/run':
- pytest.xfail("rest_tornado does not support /run endpoint until next release")
- def_args = [
- '--out=json',
- '--output-file={0}'.format(output_file),
- '-c', 'tests/.pepperrc',
- ]
- if request.param == '/run':
- def_args = ['--run-uri'] + def_args
- def _run_pepper_cli(*args, **kwargs):
- sys.argv = ['pepper', '-p', kwargs.pop('profile', 'main')] + def_args + list(args)
- exitcode = pepper.script.Pepper()()
- try:
- with open(output_file, 'r') as result:
- try:
- return yaml.load(result)
- except yaml.parser.ParserError:
- result.seek(0)
- return [yaml.load('{0}}}'.format(ret).strip('"')) for ret in result.read().split('}"\n') if ret]
- except Exception as exc:
- log.error('ExitCode %s: %s', exitcode, exc)
- return exitcode
- return _run_pepper_cli
- @pytest.fixture(scope='session')
- def session_master_config_overrides(request, salt_api_port, salt_api_backend):
- return {
- salt_api_backend: {
- 'port': salt_api_port,
- 'disable_ssl': True,
- },
- 'external_auth': {
- 'sharedsecret': {
- 'pepper': [
- '.*',
- '@jobs',
- '@wheel',
- '@runner',
- ],
- },
- },
- 'sharedsecret': 'pepper',
- 'token_expire': 94670856,
- 'ignore_host_keys': True,
- 'ssh_wipe': True,
- }
- @pytest.fixture(scope='session')
- def session_api_log_prefix(master_id):
- return 'salt-api/{0}'.format(master_id)
- @pytest.fixture(scope='session')
- def cli_api_script_name():
- '''
- Return the CLI script basename
- '''
- return 'salt-api'
- @pytest.yield_fixture(scope='session')
- def session_salt_api_before_start():
- '''
- This fixture should be overridden if you need to do
- some preparation and clean up work before starting
- the salt-api and after ending it.
- '''
- # Prep routines go here
- # Start the salt-api
- yield
- # Clean routines go here
- @pytest.yield_fixture(scope='session')
- def session_salt_api_after_start(session_salt_api):
- '''
- This fixture should be overridden if you need to do
- some preparation and clean up work after starting
- the salt-api and before ending it.
- '''
- # Prep routines go here
- # Resume test execution
- yield
- # Clean routines go here
- @pytest.fixture(scope='session')
- def _salt_fail_hard(request, salt_fail_hard):
- '''
- Return the salt fail hard value
- '''
- fail_hard = request.config.getoption('salt_fail_hard')
- if fail_hard is not None:
- # We were passed --salt-fail-hard as a CLI option
- return fail_hard
- # The salt fail hard was not passed as a CLI option
- fail_hard = request.config.getini('salt_fail_hard')
- if fail_hard != []:
- # We were passed salt_fail_hard as a INI option
- return fail_hard
- return salt_fail_hard
- @pytest.fixture(scope='session')
- def salt_api_backend(request):
- '''
- Return the salt-api backend (cherrypy or tornado)
- '''
- backend = request.config.getoption('--salt-api-backend')
- if backend is not None:
- return backend
- backend = request.config.getini('salt_api_backend')
- if backend is not None:
- return backend
- return 'rest_cherrypy'
- @pytest.fixture(scope='session')
- def master_id(salt_master_id_counter):
- '''
- Returns the master id
- '''
- return DEFAULT_MASTER_ID + '-{0}'.format(salt_master_id_counter())
- @pytest.fixture(scope='session')
- def minion_id(salt_minion_id_counter):
- '''
- Returns the minion id
- '''
- return DEFAULT_MINION_ID + '-{0}'.format(salt_minion_id_counter())
- @pytest.fixture(scope='session')
- def session_salt_api(request,
- session_salt_minion,
- session_master_id,
- session_master_config,
- session_salt_api_before_start, # pylint: disable=unused-argument
- session_api_log_prefix,
- cli_api_script_name,
- log_server,
- _cli_bin_dir,
- session_conf_dir):
- '''
- Returns a running salt-api
- '''
- return start_daemon(request,
- daemon_name='salt-api',
- daemon_id=session_master_id,
- daemon_log_prefix=session_api_log_prefix,
- daemon_cli_script_name=cli_api_script_name,
- daemon_config=session_master_config,
- daemon_config_dir=session_conf_dir,
- daemon_class=SaltApi,
- bin_dir_path=_cli_bin_dir,
- start_timeout=30)
- @pytest.fixture(scope='session')
- def session_sshd_config_lines(session_sshd_port):
- '''
- Return a list of lines which will make the sshd_config file
- '''
- return [
- 'Port {0}'.format(session_sshd_port),
- 'ListenAddress 127.0.0.1',
- 'Protocol 2',
- 'UsePrivilegeSeparation yes',
- '# Turn strict modes off so that we can operate in /tmp',
- 'StrictModes no',
- '# Logging',
- 'SyslogFacility AUTH',
- 'LogLevel INFO',
- '# Authentication:',
- 'LoginGraceTime 120',
- 'PermitRootLogin without-password',
- 'StrictModes yes',
- 'PubkeyAuthentication yes',
- '#AuthorizedKeysFile %h/.ssh/authorized_keys',
- '#AuthorizedKeysFile key_test.pub',
- '# Don\'t read the user\'s ~/.rhosts and ~/.shosts files',
- 'IgnoreRhosts yes',
- '# similar for protocol version 2',
- 'HostbasedAuthentication no',
- '#IgnoreUserKnownHosts yes',
- '# To enable empty passwords, change to yes (NOT RECOMMENDED)',
- 'PermitEmptyPasswords no',
- '# Change to yes to enable challenge-response passwords (beware issues with',
- '# some PAM modules and threads)',
- 'ChallengeResponseAuthentication no',
- '# Change to no to disable tunnelled clear text passwords',
- 'PasswordAuthentication no',
- 'X11Forwarding no',
- 'X11DisplayOffset 10',
- 'PrintMotd no',
- 'PrintLastLog yes',
- 'TCPKeepAlive yes',
- '#UseLogin no',
- 'AcceptEnv LANG LC_*',
- 'Subsystem sftp /usr/lib/openssh/sftp-server',
- '#UsePAM yes',
- ]
- def pytest_addoption(parser):
- parser.addoption(
- '--salt-api-backend',
- action='store',
- default='rest_cherrypy',
- help='which backend to use for salt-api, must be one of rest_cherrypy or rest_tornado',
- )
|