1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
"""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 = [line for line in multi_keys_lines if "configs-keys" in line][0]
home_data_line = [
line for line in multi_keys_lines if "home-data-keys" in line
][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")
|