diff options
Diffstat (limited to 'molecule/default/tests')
| -rw-r--r-- | molecule/default/tests/test_client_setup.py | 6 | ||||
| -rw-r--r-- | molecule/default/tests/test_installation.py | 2 | ||||
| -rw-r--r-- | molecule/default/tests/test_manual_backup.py | 21 | ||||
| -rw-r--r-- | molecule/default/tests/test_server_setup.py | 207 | ||||
| -rw-r--r-- | molecule/default/tests/test_server_setup_multi_instance.py | 134 | ||||
| -rw-r--r-- | molecule/default/tests/test_ssh_connectivity.py | 79 | ||||
| -rw-r--r-- | molecule/default/tests/test_systemd.py | 317 |
7 files changed, 383 insertions, 383 deletions
diff --git a/molecule/default/tests/test_client_setup.py b/molecule/default/tests/test_client_setup.py index a0e4f4a..6d11a93 100644 --- a/molecule/default/tests/test_client_setup.py +++ b/molecule/default/tests/test_client_setup.py @@ -223,10 +223,14 @@ class TestBackupScript: script = host.file('/usr/local/bin/run_borg_backup@borg-server') assert script.contains('borg@borg-server') assert script.contains('/opt/borg') - elif hostname in ('borg-client', 'borg-client-2'): + elif hostname == 'borg-client': script = host.file('/usr/local/bin/run_borg_backup') assert script.contains('borg@borg-server') assert script.contains('/opt/borg') + elif hostname == 'borg-client-2': + script = host.file('/usr/local/bin/run_borg_backup') + assert script.contains('backupserver@borg-server-2') + assert script.contains('/var/backups') else: pytest.fail(f"Unexpected hostname: {hostname}") diff --git a/molecule/default/tests/test_installation.py b/molecule/default/tests/test_installation.py index b1be621..bacc320 100644 --- a/molecule/default/tests/test_installation.py +++ b/molecule/default/tests/test_installation.py @@ -1,4 +1,4 @@ -testinfra_hosts = ['borg-client', 'borg-server'] +testinfra_hosts = ['borg-client', 'borg-server', 'borg-server-2'] def test_borg_installed(host): diff --git a/molecule/default/tests/test_manual_backup.py b/molecule/default/tests/test_manual_backup.py index 31a8693..b4068bc 100644 --- a/molecule/default/tests/test_manual_backup.py +++ b/molecule/default/tests/test_manual_backup.py @@ -1,7 +1,13 @@ import pytest from datetime import datetime -testinfra_hosts = ['borg-client'] +testinfra_hosts = ['borg-client', 'borg-client-2'] + + +def get_server_info(hostname): + if hostname == 'borg-client-2': + return ('backupserver', 'borg-server-2', '/var/backups') + return ('borg', 'borg-server', '/opt/borg') compression_types = [ @@ -20,8 +26,11 @@ host""" @pytest.mark.parametrize('compression', compression_types) def test_backup_push(host, compression): + hostname = host.backend.get_hostname() + server_user, server_host, server_path = get_server_info(hostname) + c = host.run( - f'borg create -C "{compression}" borg@borg-server:/opt/borg/borg-client::testinfra-{{now:%S.%f}} /etc' + f'borg create -C "{compression}" {server_user}@{server_host}:{server_path}/{hostname}::testinfra-{{now:%S.%f}} /etc' ) assert c.rc == 0 assert c.stdout == '' @@ -30,12 +39,14 @@ def test_backup_push(host, compression): @pytest.mark.parametrize('compression', compression_types) def test_backup_restore(host, compression): + hostname = host.backend.get_hostname() + server_user, server_host, server_path = get_server_info(hostname) timestamp = datetime.now().strftime('%Y%m%d-%H%M%S-%f') archive_name = f"testinfra-backup-restore-{compression}-{timestamp}" # Create backup c = host.run( - f'borg create -C "{compression}" borg@borg-server:/opt/borg/borg-client::{archive_name} /etc' + f'borg create -C "{compression}" {server_user}@{server_host}:{server_path}/{hostname}::{archive_name} /etc' ) assert c.rc == 0 assert c.stdout == '' @@ -43,7 +54,7 @@ def test_backup_restore(host, compression): # Restore Backup c = host.run( - f"cd /mnt && borg extract borg@borg-server:/opt/borg/borg-client::{archive_name}" + f"cd /mnt && borg extract {server_user}@{server_host}:{server_path}/{hostname}::{archive_name}" ) assert c.rc == 0 assert c.stdout == '' @@ -67,7 +78,7 @@ def test_backup_restore(host, compression): assert c.stderr == '' # Delete backup - c = host.run(f"borg delete borg@borg-server:/opt/borg/borg-client::{archive_name}") + c = host.run(f"borg delete {server_user}@{server_host}:{server_path}/{hostname}::{archive_name}") assert c.rc == 0 assert c.stdout == '' assert c.stderr == '' diff --git a/molecule/default/tests/test_server_setup.py b/molecule/default/tests/test_server_setup.py index 0a987b2..590a33c 100644 --- a/molecule/default/tests/test_server_setup.py +++ b/molecule/default/tests/test_server_setup.py @@ -2,80 +2,70 @@ import pytest import re -testinfra_hosts = ['borg-server'] +testinfra_hosts = ['borg-server', 'borg-server-2'] + +SERVER_CONFIGS = { + 'borg-server': { + 'user': 'borg', + 'home': '/opt/borg', + 'client_repo': 'borg-client', + }, + 'borg-server-2': { + 'user': 'backupserver', + 'home': '/var/backups', + 'client_repo': 'borg-client-2', + }, +} + + +@pytest.fixture +def config(host): + return SERVER_CONFIGS[host.backend.hostname] class TestBorgUser: - def test_user_exists(self, host): - user = host.user('borg') + def test_user_exists(self, host, config): + user = host.user(config['user']) assert user.exists - assert user.home == '/opt/borg' + assert user.home == config['home'] - def test_home_exists(self, host): - home = host.file('/opt/borg') + def test_home_exists(self, host, config): + home = host.file(config['home']) assert home.exists assert home.is_directory - assert home.user == 'borg' - assert home.group == 'borg' + assert home.user == config['user'] + assert home.group == config['user'] class TestBorgSSHSetup: - def test_ssh_directory_exists(self, host): - ssh_dir = host.file('/opt/borg/.ssh') + def test_ssh_directory_exists(self, host, config): + ssh_dir = host.file(f"{config['home']}/.ssh") assert ssh_dir.exists assert ssh_dir.is_directory - assert ssh_dir.user == 'borg' - assert ssh_dir.group == 'borg' + assert ssh_dir.user == config['user'] + assert ssh_dir.group == config['user'] assert ssh_dir.mode == 0o700 - def test_authorized_keys_exists(self, host): - auth_keys = host.file('/opt/borg/.ssh/authorized_keys') + def test_authorized_keys_exists(self, host, config): + auth_keys = host.file(f"{config['home']}/.ssh/authorized_keys") assert auth_keys.exists - assert auth_keys.user == 'borg' - assert auth_keys.group == 'borg' + assert auth_keys.user == config['user'] + assert auth_keys.group == config['user'] assert not auth_keys.mode & 0o002 - def test_authorized_keys_has_restrictions(self, host): - auth_keys = host.file('/opt/borg/.ssh/authorized_keys') + def test_authorized_keys_has_restrictions(self, host, config): + auth_keys = host.file(f"{config['home']}/.ssh/authorized_keys") content = auth_keys.content_string assert 'restrict' in content assert 'command="borg serve' in content - def test_authorized_keys_has_repository_restrictions(self, host): - auth_keys = host.file('/opt/borg/.ssh/authorized_keys') + def test_authorized_keys_has_repository_restrictions(self, host, config): + auth_keys = host.file(f"{config['home']}/.ssh/authorized_keys") content = auth_keys.content_string assert '--restrict-to-repository' in content - def test_authorized_keys_multi_instance_single_line(self, host): - auth_keys = host.file('/opt/borg/.ssh/authorized_keys') - content = auth_keys.content_string - lines_with_both_repos = [ - line for line in content.split('\n') - if line - and '/opt/borg/configs' in line - and '/opt/borg/home-data' in line - ] - assert len(lines_with_both_repos) == 1, ( - 'Expected exactly one authorized_keys line containing both configs and home-data repos, ' - f"found {len(lines_with_both_repos)}" - ) - - def test_authorized_keys_multi_instance_repo_count(self, host): - auth_keys = host.file('/opt/borg/.ssh/authorized_keys') - content = auth_keys.content_string - - restricted_repos = re.findall( - r'--restrict-to-repository ([^\s"]+)', - content, - ) - configs_count = restricted_repos.count('/opt/borg/configs') - home_data_count = restricted_repos.count('/opt/borg/home-data') - - assert configs_count == 1, f"configs repo should appear once in authorized_keys, found {configs_count}" - assert home_data_count == 1, f"home-data repo should appear once in authorized_keys, found {home_data_count}" - - def test_authorized_keys_format_valid(self, host): - auth_keys = host.file('/opt/borg/.ssh/authorized_keys') + def test_authorized_keys_format_valid(self, host, config): + auth_keys = host.file(f"{config['home']}/.ssh/authorized_keys") content = auth_keys.content_string valid_hosts = ( 'borg-client', @@ -91,113 +81,34 @@ class TestBorgSSHSetup: assert '@' in line and line.rstrip().endswith(valid_hosts), f"Line should contain user@hostname marker: {line[-40:]}" assert '--restrict-to-repository' in line, f"Line should have repo restriction: {line[:80]}" - def test_authorized_keys_multi_instance_no_cross_host_repos(self, host): - """Verify multi-instance host doesn't have repos from other hosts in authorized_keys""" - auth_keys = host.file('/opt/borg/.ssh/authorized_keys') - content = auth_keys.content_string - - multi_lines = [ - line for line in content.split('\n') - if line.rstrip().endswith('root@borg-client-multi') - ] - assert len(multi_lines) == 1, ( - f"Should have exactly one entry for borg-client-multi, found {len(multi_lines)}" - ) - - multi_line = multi_lines[0] - - assert '/opt/borg/configs' in multi_line, ( - 'borg-client-multi line should contain configs repo' - ) - assert '/opt/borg/home-data' in multi_line, ( - 'borg-client-multi line should contain home-data repo' - ) - - assert '/opt/borg/borg-client' not in multi_line, ( - 'borg-client-multi should NOT have access to borg-client repo' - ) - assert '/opt/borg/borg-client-2' not in multi_line, ( - 'borg-client-multi should NOT have access to borg-client-2 repo' - ) - - def test_authorized_keys_has_storage_quota(self, host): - """Verify storage quota is set in authorized_keys for configured repos""" - auth_keys = host.file('/opt/borg/.ssh/authorized_keys') - content = auth_keys.content_string - - quotas = re.findall(r'--storage-quota (\S+)', content) - assert '10G' in quotas, '10G quota should be set for configs-keys repo' - assert '50G' in quotas, '50G quota should be set for home-data-keys repo' - - def test_authorized_keys_multi_keys_different_quotas(self, host): - """Verify per-repo keys can have different storage quotas""" - auth_keys = host.file('/opt/borg/.ssh/authorized_keys') - content = auth_keys.content_string - - multi_keys_lines = [ - line for line in content.split('\n') - if line and 'root@borg-client-multi-keys' in line - ] - - assert len(multi_keys_lines) == 2, ( - f"Should have two entries for borg-client-multi-keys, found {len(multi_keys_lines)}" - ) - - configs_line = [l for l in multi_keys_lines if 'configs-keys' in l][0] - home_data_line = [l for l in multi_keys_lines if 'home-data-keys' in l][0] - - assert '--storage-quota 10G' in configs_line, ( - 'configs-keys should have 10G quota' - ) - assert '--storage-quota 50G' in home_data_line, ( - 'home-data-keys should have 50G quota' - ) - class TestBorgRepository: - def test_repo_directory_exists(self, host): - repo = host.file('/opt/borg/borg-client') + def test_repo_directory_exists(self, host, config): + repo = host.file(f"{config['home']}/{config['client_repo']}") assert repo.exists assert repo.is_directory - assert repo.user == 'borg' - assert repo.group == 'borg' - - def test_multi_instance_repos_exist(self, host): - configs_repo = host.file('/opt/borg/configs') - home_data_repo = host.file('/opt/borg/home-data') - configs_exists = configs_repo.exists - home_data_exists = home_data_repo.exists - assert configs_exists or 'skip' or home_data_exists or True - - def test_repo_initialized(self, host): - c = host.run('borg list borg@localhost:/opt/borg/borg-client') - assert c.rc == 0 or 'does not exist' not in c.stderr + assert repo.user == config['user'] + assert repo.group == config['user'] - def test_configs_repo_accessible(self, host): - c = host.run('borg list borg@localhost:/opt/borg/configs') - if c.rc != 0 and 'does not exist' in c.stderr: - pytest.skip('configs repo not created in this test run') - - def test_home_data_repo_accessible(self, host): - c = host.run('borg list borg@localhost:/opt/borg/home-data') - if c.rc != 0 and 'does not exist' in c.stderr: - pytest.skip('home-data repo not created in this test run') + def test_repo_initialized(self, host, config): + c = host.run(f"borg list {config['user']}@localhost:{config['home']}/{config['client_repo']}") + assert c.rc == 0 or 'does not exist' not in c.stderr - def test_repo_has_encryption(self, host): - config = host.file('/opt/borg/borg-client/config') - assert config.exists - content = config.content_string + def test_repo_has_encryption(self, host, config): + repo_config = host.file(f"{config['home']}/{config['client_repo']}/config") + assert repo_config.exists + content = repo_config.content_string assert len(content) > 0 - def test_repo_config_and_data_exist(self, host): - config = host.file('/opt/borg/borg-client/config') - assert config.exists + def test_repo_config_and_data_exist(self, host, config): + repo_config = host.file(f"{config['home']}/{config['client_repo']}/config") + assert repo_config.exists - data = host.file('/opt/borg/borg-client/data') + data = host.file(f"{config['home']}/{config['client_repo']}/data") assert data.exists assert data.is_directory - def test_repo_permissions(self, host): - repo = host.file('/opt/borg/borg-client') - assert repo.user == 'borg' - assert repo.group == 'borg' + def test_repo_permissions(self, host, config): + repo = host.file(f"{config['home']}/{config['client_repo']}") + assert repo.user == config['user'] + assert repo.group == config['user'] diff --git a/molecule/default/tests/test_server_setup_multi_instance.py b/molecule/default/tests/test_server_setup_multi_instance.py new file mode 100644 index 0000000..a235e2b --- /dev/null +++ b/molecule/default/tests/test_server_setup_multi_instance.py @@ -0,0 +1,134 @@ +"""Tests for borg server multi-instance repos and storage quota features""" +import pytest +import re + +testinfra_hosts = ['borg-server'] + +SERVER_CONFIG = { + 'user': 'borg', + 'home': '/opt/borg', +} + + +@pytest.fixture +def config(host): + return SERVER_CONFIG + + +class TestBorgSSHSetupMultiInstance: + """Tests for multi-instance repo configuration""" + + def test_authorized_keys_multi_instance_single_line(self, host, config): + auth_keys = host.file(f"{config['home']}/.ssh/authorized_keys") + content = auth_keys.content_string + lines_with_both_repos = [ + line for line in content.split('\n') + if line + and f"{config['home']}/configs" in line + and f"{config['home']}/home-data" in line + ] + assert len(lines_with_both_repos) == 1, ( + 'Expected exactly one authorized_keys line containing both configs and home-data repos, ' + f"found {len(lines_with_both_repos)}" + ) + + def test_authorized_keys_multi_instance_repo_count(self, host, config): + auth_keys = host.file(f"{config['home']}/.ssh/authorized_keys") + content = auth_keys.content_string + + restricted_repos = re.findall( + r'--restrict-to-repository ([^\s"]+)', + content, + ) + configs_count = restricted_repos.count(f"{config['home']}/configs") + home_data_count = restricted_repos.count(f"{config['home']}/home-data") + + assert configs_count == 1, f"configs repo should appear once in authorized_keys, found {configs_count}" + assert home_data_count == 1, f"home-data repo should appear once in authorized_keys, found {home_data_count}" + + def test_authorized_keys_multi_instance_no_cross_host_repos(self, host, config): + """Verify multi-instance host doesn't have repos from other hosts in authorized_keys""" + auth_keys = host.file(f"{config['home']}/.ssh/authorized_keys") + content = auth_keys.content_string + + multi_lines = [ + line for line in content.split('\n') + if line.rstrip().endswith('root@borg-client-multi') + ] + assert len(multi_lines) == 1, ( + f"Should have exactly one entry for borg-client-multi, found {len(multi_lines)}" + ) + + multi_line = multi_lines[0] + + assert f"{config['home']}/configs" in multi_line, ( + 'borg-client-multi line should contain configs repo' + ) + assert f"{config['home']}/home-data" in multi_line, ( + 'borg-client-multi line should contain home-data repo' + ) + + assert f"{config['home']}/borg-client" not in multi_line, ( + 'borg-client-multi should NOT have access to borg-client repo' + ) + assert f"{config['home']}/borg-client-2" not in multi_line, ( + 'borg-client-multi should NOT have access to borg-client-2 repo' + ) + + +class TestBorgSSHSetupStorageQuota: + """Tests for storage quota configuration""" + + def test_authorized_keys_has_storage_quota(self, host, config): + """Verify storage quota is set in authorized_keys for configured repos""" + auth_keys = host.file(f"{config['home']}/.ssh/authorized_keys") + content = auth_keys.content_string + + quotas = re.findall(r'--storage-quota (\S+)', content) + assert '10G' in quotas, '10G quota should be set for configs-keys repo' + assert '50G' in quotas, '50G quota should be set for home-data-keys repo' + + def test_authorized_keys_multi_keys_different_quotas(self, host, config): + """Verify per-repo keys can have different storage quotas""" + auth_keys = host.file(f"{config['home']}/.ssh/authorized_keys") + content = auth_keys.content_string + + multi_keys_lines = [ + line for line in content.split('\n') + if line and 'root@borg-client-multi-keys' in line + ] + + assert len(multi_keys_lines) == 2, ( + f"Should have two entries for borg-client-multi-keys, found {len(multi_keys_lines)}" + ) + + configs_line = [l for l in multi_keys_lines if 'configs-keys' in l][0] + home_data_line = [l for l in multi_keys_lines if 'home-data-keys' in l][0] + + assert '--storage-quota 10G' in configs_line, ( + 'configs-keys should have 10G quota' + ) + assert '--storage-quota 50G' in home_data_line, ( + 'home-data-keys should have 50G quota' + ) + + +class TestBorgRepositoryMultiInstance: + """Tests for multi-instance repo accessibility""" + + def test_multi_instance_repos_exist(self, host, config): + configs_repo = host.file(f"{config['home']}/configs") + home_data_repo = host.file(f"{config['home']}/home-data") + configs_exists = configs_repo.exists + home_data_exists = home_data_repo.exists + assert configs_exists or 'skip' or home_data_exists or True + + def test_configs_repo_accessible(self, host, config): + c = host.run(f"borg list {config['user']}@localhost:{config['home']}/configs") + if c.rc != 0 and 'does not exist' in c.stderr: + pytest.skip('configs repo not created in this test run') + + def test_home_data_repo_accessible(self, host, config): + c = host.run(f"borg list {config['user']}@localhost:{config['home']}/home-data") + if c.rc != 0 and 'does not exist' in c.stderr: + pytest.skip('home-data repo not created in this test run') diff --git a/molecule/default/tests/test_ssh_connectivity.py b/molecule/default/tests/test_ssh_connectivity.py index 0cc1897..f57b7b6 100644 --- a/molecule/default/tests/test_ssh_connectivity.py +++ b/molecule/default/tests/test_ssh_connectivity.py @@ -16,52 +16,73 @@ def get_borg_rsh(host, repo_name): ) +def get_server_user(hostname): + if hostname == 'borg-client-2': + return 'backupserver' + return 'borg' + + +def get_server_host(hostname): + if hostname == 'borg-client-2': + return 'borg-server-2' + return 'borg-server' + + def test_ssh_connection_to_server(host): - c = host.run('ssh -o BatchMode=yes -o ConnectTimeout=5 borg@borg-server echo test') + hostname = host.backend.get_hostname() + server_user = get_server_user(hostname) + server_host = get_server_host(hostname) + c = host.run(f'ssh -o BatchMode=yes -o ConnectTimeout=5 {server_user}@{server_host} echo test') assert c.rc == 0 or 'Connection refused' not in c.stderr def test_no_password_prompt_on_connect(host): hostname = host.backend.get_hostname() + server_user = get_server_user(hostname) + server_host = get_server_host(hostname) if hostname == 'borg-client-multi-keys': c = host.run( 'ssh -i /root/.ssh/id_ed25519_borgbackup_borg_server_configs_keys ' '-o BatchMode=yes -o PreferredAuthentications=publickey ' - 'borg@borg-server exit' + f'{server_user}@{server_host} exit' ) else: c = host.run( 'ssh -o BatchMode=yes -o PreferredAuthentications=publickey ' - 'borg@borg-server exit' + f'{server_user}@{server_host} exit' ) assert c.rc == 0 def test_known_hosts_has_correct_entry(host): + hostname = host.backend.get_hostname() + server_host = get_server_host(hostname) known_hosts = host.file('/root/.ssh/known_hosts') content = known_hosts.content_string - assert 'borg-server' in content + assert server_host in content assert 'ssh-' in content def test_borg_can_connect_to_server(host): hostname = host.backend.get_hostname() + server_user = get_server_user(hostname) + server_host = get_server_host(hostname) if hostname == 'borg-client-multi': - c = host.run('borg list borg@borg-server:/opt/borg/configs') + c = host.run(f'borg list {server_user}@{server_host}:/opt/borg/configs') assert c.rc == 0, 'Should be able to connect to configs repo' elif hostname == 'borg-client-multi-keys': c = host.run( f'{get_borg_rsh(host, "configs-keys")} ' - 'borg list borg@borg-server:/opt/borg/configs-keys' + f'borg list {server_user}@{server_host}:/opt/borg/configs-keys' ) assert c.rc == 0, 'Should be able to connect to configs-keys repo' elif hostname == 'borg-client': - c = host.run('borg list borg@borg-server:/opt/borg/borg-client') + c = host.run(f'borg list {server_user}@{server_host}:/opt/borg/borg-client') assert c.rc == 0 elif hostname == 'borg-client-2': - c = host.run('borg list borg@borg-server:/opt/borg/borg-client-2') + c = host.run(f'borg list {server_user}@{server_host}:/var/backups/borg-client-2') assert c.rc == 0, 'Should be able to connect to borg-client-2 repo' else: pytest.fail(f"Unexpected hostname: {hostname}") @@ -69,28 +90,30 @@ def test_borg_can_connect_to_server(host): def test_borg_info_works(host): hostname = host.backend.get_hostname() + server_user = get_server_user(hostname) + server_host = get_server_host(hostname) if hostname == 'borg-client-multi': - c = host.run('borg info borg@borg-server:/opt/borg/configs') + c = host.run(f'borg info {server_user}@{server_host}:/opt/borg/configs') assert c.rc == 0, 'Should be able to get info for configs repo' - c2 = host.run('borg info borg@borg-server:/opt/borg/home-data') + c2 = host.run(f'borg info {server_user}@{server_host}:/opt/borg/home-data') assert c2.rc == 0, 'Should be able to get info for home-data repo' elif hostname == 'borg-client-multi-keys': c = host.run( f'{get_borg_rsh(host, "configs-keys")} ' - 'borg info borg@borg-server:/opt/borg/configs-keys' + f'borg info {server_user}@{server_host}:/opt/borg/configs-keys' ) assert c.rc == 0, 'Should be able to get info for configs-keys repo' c2 = host.run( f'{get_borg_rsh(host, "home-data-keys")} ' - 'borg info borg@borg-server:/opt/borg/home-data-keys' + f'borg info {server_user}@{server_host}:/opt/borg/home-data-keys' ) assert c2.rc == 0, 'Should be able to get info for home-data-keys repo' elif hostname == 'borg-client': - c = host.run('borg info borg@borg-server:/opt/borg/borg-client') + c = host.run(f'borg info {server_user}@{server_host}:/opt/borg/borg-client') assert c.rc == 0 elif hostname == 'borg-client-2': - c = host.run('borg info borg@borg-server:/opt/borg/borg-client-2') + c = host.run(f'borg info {server_user}@{server_host}:/var/backups/borg-client-2') assert c.rc == 0, 'Should be able to get info for borg-client-2 repo' else: pytest.fail(f"Unexpected hostname: {hostname}") @@ -101,40 +124,48 @@ def test_multi_instance_can_access_both_repos(host): if hostname != 'borg-client-multi': return - c1 = host.run('borg list borg@borg-server:/opt/borg/configs') + server_user = get_server_user(hostname) + server_host = get_server_host(hostname) + + c1 = host.run(f'borg list {server_user}@{server_host}:/opt/borg/configs') assert c1.rc == 0, 'borg-client-multi should access configs repo' - c2 = host.run('borg list borg@borg-server:/opt/borg/home-data') + c2 = host.run(f'borg list {server_user}@{server_host}:/opt/borg/home-data') assert c2.rc == 0, 'borg-client-multi should access home-data repo' def test_multi_instance_cannot_access_other_hosts_repos(host): - """Verify borg-client-multi cannot access repos from other hosts""" hostname = host.backend.get_hostname() if hostname != 'borg-client-multi': return - c1 = host.run('borg list borg@borg-server:/opt/borg/borg-client') + server_user = get_server_user(hostname) + server_host = get_server_host(hostname) + + c1 = host.run(f'borg list {server_user}@{server_host}:/opt/borg/borg-client') assert c1.rc != 0, ( 'borg-client-multi should NOT access borg-client repo' ) - c2 = host.run('borg list borg@borg-server:/opt/borg/borg-client-2') + c2 = host.run(f'borg list {server_user}@{server_host}:/opt/borg/borg-client-2') assert c2.rc != 0, ( 'borg-client-multi should NOT access borg-client-2 repo' ) def test_single_host_cannot_access_multi_repos(host): - """Verify single-instance hosts cannot access multi-instance repos""" hostname = host.backend.get_hostname() if hostname not in ['borg-client', 'borg-client-2']: return - c1 = host.run('borg list borg@borg-server:/opt/borg/configs') - assert c1.rc != 0, ( - f'{hostname} should NOT access configs repo (belongs to borg-client-multi)' - ) + server_user = get_server_user(hostname) + server_host = get_server_host(hostname) + + if hostname == 'borg-client': + c1 = host.run(f'borg list {server_user}@{server_host}:/opt/borg/configs') + assert c1.rc != 0, ( + f'{hostname} should NOT access configs repo (belongs to borg-client-multi)' + ) c2 = host.run('borg list borg@borg-server:/opt/borg/home-data') assert c2.rc != 0, ( diff --git a/molecule/default/tests/test_systemd.py b/molecule/default/tests/test_systemd.py index b713c27..94b82c4 100644 --- a/molecule/default/tests/test_systemd.py +++ b/molecule/default/tests/test_systemd.py @@ -4,263 +4,172 @@ import pytest testinfra_hosts = ['borg-client', 'borg-client-2', 'borg-client-multi', 'borg-client-nonroot', 'borg-client-multi-keys'] +CLIENT_CONFIGS = { + 'borg-client': { + 'user': 'root', + 'server': 'borg-server', + 'schedule': '*-*-* 02:00:00', + 'success_exit_status': False, + }, + 'borg-client-2': { + 'user': 'root', + 'server': 'borg-server-2', + 'schedule': '*-*-* 03:00:00', + 'success_exit_status': True, + }, + 'borg-client-nonroot': { + 'user': 'backupuser', + 'server': 'borg-server', + 'schedule': '*-*-* 02:00:00', + 'success_exit_status': False, + }, +} -CLIENT_USER_MAP = { - 'borg-client': 'root', - 'borg-client-2': 'root', - 'borg-client-multi': 'root', - 'borg-client-nonroot': 'backupuser', - 'borg-client-multi-keys': 'root', +MULTI_INSTANCE_CONFIGS = { + 'borg-client-multi': { + 'user': 'root', + 'repos': ['configs', 'home-data'], + 'schedules': { + 'configs': '*-*-* 02:00:00', + 'home-data': '*-*-* 04:00:00', + }, + }, + 'borg-client-multi-keys': { + 'user': 'root', + 'repos': ['configs-keys', 'home-data-keys'], + 'schedules': { + 'configs-keys': '*-*-* 02:00:00', + 'home-data-keys': '*-*-* 04:00:00', + }, + }, } -def get_client_user(host): - hostname = host.backend.get_hostname() - return CLIENT_USER_MAP.get(hostname, 'root') +@pytest.fixture +def config(host): + hostname = host.backend.hostname + if hostname in CLIENT_CONFIGS: + return {'type': 'single', **CLIENT_CONFIGS[hostname]} + return {'type': 'multi', **MULTI_INSTANCE_CONFIGS[hostname]} class TestSystemdServiceFile: - def test_service_file_exists(self, host): - hostname = host.backend.get_hostname() - - if hostname == 'borg-client-multi': - service1 = host.file('/etc/systemd/system/borg_backup@configs.service') - service2 = host.file('/etc/systemd/system/borg_backup@home-data.service') - assert service1.exists - assert service1.user == 'root' - assert service1.group == 'root' - assert service2.exists - assert service2.user == 'root' - assert service2.group == 'root' - elif hostname == 'borg-client-multi-keys': - service1 = host.file('/etc/systemd/system/borg_backup@configs-keys.service') - service2 = host.file('/etc/systemd/system/borg_backup@home-data-keys.service') - assert service1.exists - assert service1.user == 'root' - assert service1.group == 'root' - assert service2.exists - assert service2.user == 'root' - assert service2.group == 'root' - elif hostname == 'borg-client-nonroot': - service = host.file('/etc/systemd/system/borg_backup@borg-server.service') - assert service.exists - assert service.user == 'root' - assert service.group == 'root' - assert service.mode == 0o644 - elif hostname in ('borg-client', 'borg-client-2'): - service = host.file('/etc/systemd/system/borg_backup@borg-server.service') + def test_service_file_exists(self, host, config): + if config['type'] == 'single': + service = host.file(f"/etc/systemd/system/borg_backup@{config['server']}.service") assert service.exists assert service.user == 'root' assert service.group == 'root' assert service.mode == 0o644 else: - pytest.fail(f"Unexpected hostname: {hostname}") - - def test_service_content(self, host): - hostname = host.backend.get_hostname() - - if hostname == 'borg-client-multi': - service1 = host.file('/etc/systemd/system/borg_backup@configs.service') - service2 = host.file('/etc/systemd/system/borg_backup@home-data.service') - for service in [service1, service2]: - assert service.contains('[Unit]') - assert service.contains('[Service]') - assert service.contains('[Install]') - assert service.contains('Type=oneshot') - elif hostname == 'borg-client-multi-keys': - service1 = host.file('/etc/systemd/system/borg_backup@configs-keys.service') - service2 = host.file('/etc/systemd/system/borg_backup@home-data-keys.service') - for service in [service1, service2]: - assert service.contains('[Unit]') - assert service.contains('[Service]') - assert service.contains('[Install]') - assert service.contains('Type=oneshot') - elif hostname == 'borg-client-nonroot': - service = host.file('/etc/systemd/system/borg_backup@borg-server.service') - assert service.contains('[Unit]') - assert service.contains('[Service]') - assert service.contains('[Install]') - assert service.contains('Type=oneshot') - assert service.contains('ExecStart=/usr/local/bin/run_borg_backup') - elif hostname in ('borg-client', 'borg-client-2'): - service = host.file('/etc/systemd/system/borg_backup@borg-server.service') + for repo in config['repos']: + service = host.file(f"/etc/systemd/system/borg_backup@{repo}.service") + assert service.exists + assert service.user == 'root' + assert service.group == 'root' + + def test_service_content(self, host, config): + if config['type'] == 'single': + service = host.file(f"/etc/systemd/system/borg_backup@{config['server']}.service") assert service.contains('[Unit]') assert service.contains('[Service]') assert service.contains('[Install]') assert service.contains('Type=oneshot') assert service.contains('ExecStart=/usr/local/bin/run_borg_backup') else: - pytest.fail(f"Unexpected hostname: {hostname}") - - def test_service_user(self, host): - hostname = host.backend.get_hostname() - client_user = get_client_user(host) + for repo in config['repos']: + service = host.file(f"/etc/systemd/system/borg_backup@{repo}.service") + assert service.contains('[Unit]') + assert service.contains('[Service]') + assert service.contains('[Install]') + assert service.contains('Type=oneshot') - if hostname == 'borg-client-multi': - service1 = host.file('/etc/systemd/system/borg_backup@configs.service') - service2 = host.file('/etc/systemd/system/borg_backup@home-data.service') - for service in [service1, service2]: - assert service.contains(f'User={client_user}') - assert service.contains(f'Group={client_user}') - elif hostname == 'borg-client-multi-keys': - service1 = host.file('/etc/systemd/system/borg_backup@configs-keys.service') - service2 = host.file('/etc/systemd/system/borg_backup@home-data-keys.service') - for service in [service1, service2]: - assert service.contains(f'User={client_user}') - assert service.contains(f'Group={client_user}') - elif hostname in ('borg-client', 'borg-client-2', 'borg-client-nonroot'): - service = host.file('/etc/systemd/system/borg_backup@borg-server.service') - assert service.contains(f'User={client_user}') - assert service.contains(f'Group={client_user}') + def test_service_user(self, host, config): + if config['type'] == 'single': + service = host.file(f"/etc/systemd/system/borg_backup@{config['server']}.service") + assert service.contains(f"User={config['user']}") + assert service.contains(f"Group={config['user']}") else: - pytest.fail(f"Unexpected hostname: {hostname}") + for repo in config['repos']: + service = host.file(f"/etc/systemd/system/borg_backup@{repo}.service") + assert service.contains(f"User={config['user']}") + assert service.contains(f"Group={config['user']}") - def test_success_exit_status(self, host): - hostname = host.backend.get_hostname() - if hostname in ('borg-client-multi', 'borg-client-nonroot', 'borg-client-multi-keys'): + def test_success_exit_status(self, host, config): + if config['type'] != 'single': return - - service = host.file('/etc/systemd/system/borg_backup@borg-server.service') + service = host.file(f"/etc/systemd/system/borg_backup@{config['server']}.service") assert service.exists - if hostname == 'borg-client-2': + if config['success_exit_status']: assert service.contains('SuccessExitStatus=1 TEMPFAIL') - elif hostname == 'borg-client': - assert not service.contains('SuccessExitStatus=') else: - pytest.fail(f"Unexpected hostname: {hostname}") - - @pytest.mark.parametrize('exit_status', [1, 'TEMPFAIL']) - def test_success_exit_status_values(self, host, exit_status): - hostname = host.backend.get_hostname() - if hostname == 'borg-client-2': - service = host.file('/etc/systemd/system/borg_backup@borg-server.service') - assert service.contains(str(exit_status)) + assert not service.contains('SuccessExitStatus=') class TestSystemdTimerFile: - def test_timer_file_exists(self, host): - hostname = host.backend.get_hostname() - - if hostname == 'borg-client-multi': - timer1 = host.file('/etc/systemd/system/borg_backup@configs.timer') - timer2 = host.file('/etc/systemd/system/borg_backup@home-data.timer') - assert timer1.exists - assert timer2.exists - elif hostname == 'borg-client-multi-keys': - timer1 = host.file('/etc/systemd/system/borg_backup@configs-keys.timer') - timer2 = host.file('/etc/systemd/system/borg_backup@home-data-keys.timer') - assert timer1.exists - assert timer2.exists - elif hostname in ('borg-client', 'borg-client-2', 'borg-client-nonroot'): - timer = host.file('/etc/systemd/system/borg_backup@borg-server.timer') + def test_timer_file_exists(self, host, config): + if config['type'] == 'single': + timer = host.file(f"/etc/systemd/system/borg_backup@{config['server']}.timer") assert timer.exists assert timer.user == 'root' assert timer.group == 'root' assert timer.mode == 0o644 else: - pytest.fail(f"Unexpected hostname: {hostname}") - - def test_timer_content(self, host): - hostname = host.backend.get_hostname() + for repo in config['repos']: + timer = host.file(f"/etc/systemd/system/borg_backup@{repo}.timer") + assert timer.exists - if hostname == 'borg-client-multi': - timer1 = host.file('/etc/systemd/system/borg_backup@configs.timer') - timer2 = host.file('/etc/systemd/system/borg_backup@home-data.timer') - for timer in [timer1, timer2]: - assert timer.contains('[Unit]') - assert timer.contains('[Timer]') - assert timer.contains('[Install]') - assert timer.contains('OnCalendar=') - assert timer.contains('AccuracySec=') - elif hostname == 'borg-client-multi-keys': - timer1 = host.file('/etc/systemd/system/borg_backup@configs-keys.timer') - timer2 = host.file('/etc/systemd/system/borg_backup@home-data-keys.timer') - for timer in [timer1, timer2]: - assert timer.contains('[Unit]') - assert timer.contains('[Timer]') - assert timer.contains('[Install]') - assert timer.contains('OnCalendar=') - assert timer.contains('AccuracySec=') - elif hostname in ('borg-client', 'borg-client-2', 'borg-client-nonroot'): - timer = host.file('/etc/systemd/system/borg_backup@borg-server.timer') + def test_timer_content(self, host, config): + if config['type'] == 'single': + timer = host.file(f"/etc/systemd/system/borg_backup@{config['server']}.timer") assert timer.contains('[Unit]') assert timer.contains('[Timer]') assert timer.contains('[Install]') assert timer.contains('OnCalendar=') assert timer.contains('AccuracySec=') else: - pytest.fail(f"Unexpected hostname: {hostname}") - - def test_timer_schedule(self, host): - hostname = host.backend.get_hostname() + for repo in config['repos']: + timer = host.file(f"/etc/systemd/system/borg_backup@{repo}.timer") + assert timer.contains('[Unit]') + assert timer.contains('[Timer]') + assert timer.contains('[Install]') + assert timer.contains('OnCalendar=') + assert timer.contains('AccuracySec=') - if hostname == 'borg-client-multi': - timer1 = host.file('/etc/systemd/system/borg_backup@configs.timer') - timer2 = host.file('/etc/systemd/system/borg_backup@home-data.timer') - assert 'OnCalendar=*-*-* 02:00:00' in timer1.content_string - assert 'OnCalendar=*-*-* 04:00:00' in timer2.content_string - elif hostname == 'borg-client-multi-keys': - timer1 = host.file('/etc/systemd/system/borg_backup@configs-keys.timer') - timer2 = host.file('/etc/systemd/system/borg_backup@home-data-keys.timer') - assert 'OnCalendar=*-*-* 02:00:00' in timer1.content_string - assert 'OnCalendar=*-*-* 04:00:00' in timer2.content_string - elif hostname == 'borg-client-2': - timer = host.file('/etc/systemd/system/borg_backup@borg-server.timer') - assert 'OnCalendar=*-*-* 03:00:00' in timer.content_string - elif hostname in ('borg-client', 'borg-client-nonroot'): - timer = host.file('/etc/systemd/system/borg_backup@borg-server.timer') - assert 'OnCalendar=*-*-* 02:00:00' in timer.content_string + def test_timer_schedule(self, host, config): + if config['type'] == 'single': + timer = host.file(f"/etc/systemd/system/borg_backup@{config['server']}.timer") + assert f"OnCalendar={config['schedule']}" in timer.content_string else: - pytest.fail(f"Unexpected hostname: {hostname}") + for repo in config['repos']: + timer = host.file(f"/etc/systemd/system/borg_backup@{repo}.timer") + assert f"OnCalendar={config['schedules'][repo]}" in timer.content_string class TestSystemdState: - def test_timer_enabled(self, host): - hostname = host.backend.get_hostname() - - if hostname == 'borg-client-multi': - c1 = host.run('systemctl is-enabled borg_backup@configs.timer') - c2 = host.run('systemctl is-enabled borg_backup@home-data.timer') - assert c1.rc == 0 - assert c1.stdout.strip() == 'enabled' - assert c2.rc == 0 - assert c2.stdout.strip() == 'enabled' - elif hostname == 'borg-client-multi-keys': - c1 = host.run('systemctl is-enabled borg_backup@configs-keys.timer') - c2 = host.run('systemctl is-enabled borg_backup@home-data-keys.timer') - assert c1.rc == 0 - assert c1.stdout.strip() == 'enabled' - assert c2.rc == 0 - assert c2.stdout.strip() == 'enabled' - elif hostname in ('borg-client', 'borg-client-2', 'borg-client-nonroot'): - timer_name = 'borg_backup@borg-server.timer' + def test_timer_enabled(self, host, config): + if config['type'] == 'single': + timer_name = f"borg_backup@{config['server']}.timer" c = host.run(f"systemctl is-enabled {timer_name}") assert c.rc == 0 assert c.stdout.strip() == 'enabled' else: - pytest.fail(f"Unexpected hostname: {hostname}") - - def test_timer_active(self, host): - hostname = host.backend.get_hostname() - - if hostname == 'borg-client-multi': - c1 = host.run('systemctl is-active borg_backup@configs.timer') - c2 = host.run('systemctl is-active borg_backup@home-data.timer') - assert c1.rc == 0 - assert c2.rc == 0 - elif hostname == 'borg-client-multi-keys': - c1 = host.run('systemctl is-active borg_backup@configs-keys.timer') - c2 = host.run('systemctl is-active borg_backup@home-data-keys.timer') - assert c1.rc == 0 - assert c2.rc == 0 - elif hostname in ('borg-client', 'borg-client-2', 'borg-client-nonroot'): - timer_name = 'borg_backup@borg-server.timer' + for repo in config['repos']: + c = host.run(f"systemctl is-enabled borg_backup@{repo}.timer") + assert c.rc == 0 + assert c.stdout.strip() == 'enabled' + + def test_timer_active(self, host, config): + if config['type'] == 'single': + timer_name = f"borg_backup@{config['server']}.timer" c = host.run(f"systemctl is-active {timer_name}") assert c.rc == 0 else: - pytest.fail(f"Unexpected hostname: {hostname}") + for repo in config['repos']: + c = host.run(f"systemctl is-active borg_backup@{repo}.timer") + assert c.rc == 0 - def test_daemon_reload_ok(self, host): + def test_daemon_reload_ok(self, host, config): c = host.run('systemctl daemon-reload') assert c.rc == 0 |