|
@@ -2,7 +2,6 @@
|
|
|
from __future__ import absolute_import, unicode_literals, print_function
|
|
|
|
|
|
# Import python libraries
|
|
|
-import distutils.spawn
|
|
|
import logging
|
|
|
import os.path
|
|
|
import shutil
|
|
@@ -11,48 +10,86 @@ 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
|
|
|
+from pytestskipmarkers.utils import ports
|
|
|
+from saltfactories.utils import random_string, running_username
|
|
|
|
|
|
# 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')
|
|
|
+def sshd_config_dir(salt_factories):
|
|
|
+ config_dir = salt_factories.get_root_dir_for_daemon("sshd")
|
|
|
+ yield config_dir
|
|
|
+ shutil.rmtree(str(config_dir), ignore_errors=True)
|
|
|
|
|
|
|
|
|
-class SaltApi(SaltDaemonScriptBase):
|
|
|
- '''
|
|
|
- Class which runs the salt-api daemon
|
|
|
- '''
|
|
|
-
|
|
|
- def get_script_args(self):
|
|
|
- return ['-l', 'quiet']
|
|
|
+@pytest.fixture(scope='session')
|
|
|
+def session_sshd_server(salt_factories, sshd_config_dir, session_master):
|
|
|
+ sshd_config_dict = {
|
|
|
+ "Protocol": "2",
|
|
|
+ # Turn strict modes off so that we can operate in /tmp
|
|
|
+ "StrictModes": "no",
|
|
|
+ # Logging
|
|
|
+ "SyslogFacility": "AUTH",
|
|
|
+ "LogLevel": "INFO",
|
|
|
+ # Authentication:
|
|
|
+ "LoginGraceTime": "120",
|
|
|
+ "PermitRootLogin": "without-password",
|
|
|
+ "PubkeyAuthentication": "yes",
|
|
|
+ # Don't read the user's ~/.rhosts and ~/.shosts files
|
|
|
+ "IgnoreRhosts": "yes",
|
|
|
+ "HostbasedAuthentication": "no",
|
|
|
+ # 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",
|
|
|
+ "AcceptEnv": "LANG LC_*",
|
|
|
+ "UsePAM": "yes",
|
|
|
+ }
|
|
|
+ factory = salt_factories.get_sshd_daemon(
|
|
|
+ sshd_config_dict=sshd_config_dict,
|
|
|
+ config_dir=sshd_config_dir,
|
|
|
+ )
|
|
|
+ with factory.started():
|
|
|
+ yield factory
|
|
|
|
|
|
- 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 session_ssh_roster_config(session_sshd_server, session_master):
|
|
|
+ roster_contents = """
|
|
|
+ localhost:
|
|
|
+ host: 127.0.0.1
|
|
|
+ port: {}
|
|
|
+ user: {}
|
|
|
+ priv: {}
|
|
|
+ mine_functions:
|
|
|
+ test.arg: ['itworked']
|
|
|
+ """.format(
|
|
|
+ session_sshd_server.listen_port,
|
|
|
+ running_username(),
|
|
|
+ session_sshd_server.client_key
|
|
|
+ )
|
|
|
+ with pytest.helpers.temp_file(
|
|
|
+ "roster", roster_contents, session_master.config_dir
|
|
|
+ ) as roster_file:
|
|
|
+ yield roster_file
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope='session')
|
|
@@ -60,7 +97,7 @@ def salt_api_port():
|
|
|
'''
|
|
|
Returns an unused localhost port for the api port
|
|
|
'''
|
|
|
- return get_unused_localhost_port()
|
|
|
+ return ports.get_unused_localhost_port()
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope='session')
|
|
@@ -121,7 +158,7 @@ def output_file():
|
|
|
|
|
|
|
|
|
@pytest.fixture(params=['/run', '/login'])
|
|
|
-def pepper_cli(request, session_salt_api, salt_api_port, output_file, install_sshd_server, session_sshd_server):
|
|
|
+def pepper_cli(request, session_salt_api, salt_api_port, output_file, session_sshd_server):
|
|
|
'''
|
|
|
Wrapper to invoke Pepper with common params and inside an empty env
|
|
|
'''
|
|
@@ -154,6 +191,20 @@ def pepper_cli(request, session_salt_api, salt_api_port, output_file, install_ss
|
|
|
return _run_pepper_cli
|
|
|
|
|
|
|
|
|
+@pytest.fixture(scope='session')
|
|
|
+def session_master_factory(request, salt_factories, session_master_config_overrides):
|
|
|
+ return salt_factories.salt_master_daemon(
|
|
|
+ random_string("master-"),
|
|
|
+ overrides=session_master_config_overrides
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+@pytest.fixture(scope='session')
|
|
|
+def session_master(session_master_factory):
|
|
|
+ with session_master_factory.started():
|
|
|
+ yield session_master_factory
|
|
|
+
|
|
|
+
|
|
|
@pytest.fixture(scope='session')
|
|
|
def session_master_config_overrides(request, salt_api_port, salt_api_backend):
|
|
|
return {
|
|
@@ -175,69 +226,40 @@ def session_master_config_overrides(request, salt_api_port, salt_api_backend):
|
|
|
'token_expire': 94670856,
|
|
|
'ignore_host_keys': True,
|
|
|
'ssh_wipe': True,
|
|
|
+ 'netapi_enable_clients': [
|
|
|
+ 'local',
|
|
|
+ 'local_async',
|
|
|
+ 'local_subset',
|
|
|
+ 'ssh',
|
|
|
+ 'runner',
|
|
|
+ 'runner_async',
|
|
|
+ 'wheel',
|
|
|
+ 'wheel_async',
|
|
|
+ 'run'
|
|
|
+ ]
|
|
|
}
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope='session')
|
|
|
-def session_api_log_prefix(master_id):
|
|
|
- return 'salt-api/{0}'.format(master_id)
|
|
|
+def session_minion_factory(session_master_factory):
|
|
|
+ """Return a factory for a randomly named minion connected to master."""
|
|
|
+ minion_factory = session_master_factory.salt_minion_daemon(random_string("minion-"))
|
|
|
+ minion_factory.after_terminate(
|
|
|
+ pytest.helpers.remove_stale_minion_key, session_master_factory, minion_factory.id
|
|
|
+ )
|
|
|
+ return minion_factory
|
|
|
|
|
|
|
|
|
@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
|
|
|
+def session_minion(session_master, session_minion_factory): # noqa
|
|
|
+ assert session_master.is_running()
|
|
|
+ with session_minion_factory.started():
|
|
|
+ yield session_minion_factory
|
|
|
|
|
|
|
|
|
@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
|
|
|
+def session_minion_id(session_minion):
|
|
|
+ return session_minion.id
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope='session')
|
|
@@ -257,91 +279,15 @@ def salt_api_backend(request):
|
|
|
|
|
|
|
|
|
@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())
|
|
|
+def session_salt_api_factory(session_master_factory):
|
|
|
+ return session_master_factory.salt_api_daemon()
|
|
|
|
|
|
|
|
|
@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 session_salt_api(session_master, session_salt_api_factory):
|
|
|
+ assert session_master.is_running()
|
|
|
+ with session_salt_api_factory.started():
|
|
|
+ yield session_salt_api_factory
|
|
|
|
|
|
|
|
|
def pytest_addoption(parser):
|