]>
Commit | Line | Data |
---|---|---|
e306af50 | 1 | # type: ignore |
9f95a23c | 2 | import mock |
ec96510d | 3 | from mock import patch, call |
9f95a23c | 4 | import unittest |
ec96510d FG |
5 | import errno |
6 | import socket | |
9f95a23c TL |
7 | |
8 | import pytest | |
9 | ||
f91f0fd5 | 10 | with patch('builtins.open', create=True): |
9f95a23c TL |
11 | from importlib.machinery import SourceFileLoader |
12 | cd = SourceFileLoader('cephadm', 'cephadm').load_module() | |
9f95a23c TL |
13 | |
14 | class TestCephAdm(object): | |
7f7e6c64 TL |
15 | |
16 | def test_docker_unit_file(self): | |
17 | cd.args = mock.Mock() | |
18 | cd.container_path = '/usr/bin/docker' | |
19 | r = cd.get_unit_file('9b9d7609-f4d5-4aba-94c8-effa764d96c9') | |
20 | assert 'Requires=docker.service' in r | |
21 | cd.container_path = '/usr/sbin/podman' | |
22 | r = cd.get_unit_file('9b9d7609-f4d5-4aba-94c8-effa764d96c9') | |
23 | assert 'Requires=docker.service' not in r | |
24 | ||
ec96510d FG |
25 | def test_attempt_bind(self): |
26 | cd.logger = mock.Mock() | |
27 | address = None | |
28 | port = 0 | |
29 | ||
30 | def os_error(errno): | |
31 | _os_error = OSError() | |
32 | _os_error.errno = errno | |
33 | return _os_error | |
34 | ||
35 | for side_effect, expected_exception in ( | |
36 | (os_error(errno.EADDRINUSE), cd.PortOccupiedError), | |
37 | (os_error(errno.EAFNOSUPPORT), OSError), | |
38 | (os_error(errno.EADDRNOTAVAIL), OSError), | |
39 | (None, None), | |
40 | ): | |
41 | _socket = mock.Mock() | |
42 | _socket.bind.side_effect = side_effect | |
43 | try: | |
44 | cd.attempt_bind(_socket, address, port) | |
45 | except Exception as e: | |
46 | assert isinstance(e, expected_exception) | |
47 | else: | |
48 | if expected_exception is not None: | |
49 | assert False, '{} should not be None'.format(expected_exception) | |
50 | ||
51 | @mock.patch('cephadm.attempt_bind') | |
52 | def test_port_in_use(self, attempt_bind): | |
53 | ||
54 | assert cd.port_in_use(9100) == False | |
55 | ||
56 | attempt_bind.side_effect = cd.PortOccupiedError('msg') | |
57 | assert cd.port_in_use(9100) == True | |
58 | ||
59 | os_error = OSError() | |
60 | os_error.errno = errno.EADDRNOTAVAIL | |
61 | attempt_bind.side_effect = os_error | |
62 | assert cd.port_in_use(9100) == False | |
63 | ||
64 | os_error = OSError() | |
65 | os_error.errno = errno.EAFNOSUPPORT | |
66 | attempt_bind.side_effect = os_error | |
67 | assert cd.port_in_use(9100) == False | |
68 | ||
69 | @mock.patch('socket.socket') | |
70 | @mock.patch('cephadm.args') | |
71 | def test_check_ip_port_success(self, args, _socket): | |
72 | args.skip_ping_check = False | |
73 | ||
74 | for address, address_family in ( | |
75 | ('0.0.0.0', socket.AF_INET), | |
76 | ('::', socket.AF_INET6), | |
77 | ): | |
78 | try: | |
79 | cd.check_ip_port(address, 9100) | |
80 | except: | |
81 | assert False | |
82 | else: | |
83 | assert _socket.call_args == call(address_family, socket.SOCK_STREAM) | |
84 | ||
85 | @mock.patch('socket.socket') | |
86 | @mock.patch('cephadm.args') | |
87 | def test_check_ip_port_failure(self, args, _socket): | |
88 | args.skip_ping_check = False | |
89 | ||
90 | def os_error(errno): | |
91 | _os_error = OSError() | |
92 | _os_error.errno = errno | |
93 | return _os_error | |
94 | ||
95 | for address, address_family in ( | |
96 | ('0.0.0.0', socket.AF_INET), | |
97 | ('::', socket.AF_INET6), | |
98 | ): | |
99 | for side_effect, expected_exception in ( | |
100 | (os_error(errno.EADDRINUSE), cd.PortOccupiedError), | |
101 | (os_error(errno.EADDRNOTAVAIL), OSError), | |
102 | (os_error(errno.EAFNOSUPPORT), OSError), | |
103 | (None, None), | |
104 | ): | |
105 | mock_socket_obj = mock.Mock() | |
106 | mock_socket_obj.bind.side_effect = side_effect | |
107 | _socket.return_value = mock_socket_obj | |
108 | try: | |
109 | cd.check_ip_port(address, 9100) | |
110 | except Exception as e: | |
111 | assert isinstance(e, expected_exception) | |
112 | else: | |
113 | assert side_effect is None | |
114 | ||
115 | ||
f91f0fd5 | 116 | def test_is_not_fsid(self): |
9f95a23c TL |
117 | assert not cd.is_fsid('no-uuid') |
118 | ||
f91f0fd5 TL |
119 | def test_is_fsid(self): |
120 | assert cd.is_fsid('e863154d-33c7-4350-bca5-921e0467e55b') | |
121 | ||
9f95a23c TL |
122 | def test__get_parser_image(self): |
123 | args = cd._parse_args(['--image', 'foo', 'version']) | |
124 | assert args.image == 'foo' | |
125 | ||
126 | def test_CustomValidation(self): | |
127 | assert cd._parse_args(['deploy', '--name', 'mon.a', '--fsid', 'fsid']) | |
128 | ||
129 | with pytest.raises(SystemExit): | |
130 | cd._parse_args(['deploy', '--name', 'wrong', '--fsid', 'fsid']) | |
131 | ||
132 | @pytest.mark.parametrize("test_input, expected", [ | |
133 | ("podman version 1.6.2", (1,6,2)), | |
134 | ("podman version 1.6.2-stable2", (1,6,2)), | |
135 | ]) | |
136 | def test_parse_podman_version(self, test_input, expected): | |
137 | assert cd._parse_podman_version(test_input) == expected | |
138 | ||
139 | def test_parse_podman_version_invalid(self): | |
140 | with pytest.raises(ValueError) as res: | |
141 | cd._parse_podman_version('podman version inval.id') | |
142 | assert 'inval' in str(res.value) | |
143 | ||
144 | @pytest.mark.parametrize("test_input, expected", [ | |
145 | ( | |
146 | """ | |
147 | default via 192.168.178.1 dev enxd89ef3f34260 proto dhcp metric 100 | |
148 | 10.0.0.0/8 via 10.4.0.1 dev tun0 proto static metric 50 | |
149 | 10.3.0.0/21 via 10.4.0.1 dev tun0 proto static metric 50 | |
150 | 10.4.0.1 dev tun0 proto kernel scope link src 10.4.0.2 metric 50 | |
151 | 137.1.0.0/16 via 10.4.0.1 dev tun0 proto static metric 50 | |
152 | 138.1.0.0/16 via 10.4.0.1 dev tun0 proto static metric 50 | |
153 | 139.1.0.0/16 via 10.4.0.1 dev tun0 proto static metric 50 | |
154 | 140.1.0.0/17 via 10.4.0.1 dev tun0 proto static metric 50 | |
155 | 141.1.0.0/16 via 10.4.0.1 dev tun0 proto static metric 50 | |
156 | 169.254.0.0/16 dev docker0 scope link metric 1000 | |
157 | 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 | |
158 | 192.168.39.0/24 dev virbr1 proto kernel scope link src 192.168.39.1 linkdown | |
159 | 192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1 linkdown | |
160 | 192.168.178.0/24 dev enxd89ef3f34260 proto kernel scope link src 192.168.178.28 metric 100 | |
161 | 192.168.178.1 dev enxd89ef3f34260 proto static scope link metric 100 | |
162 | 195.135.221.12 via 192.168.178.1 dev enxd89ef3f34260 proto static metric 100 | |
163 | """, | |
164 | { | |
165 | '10.4.0.1': ['10.4.0.2'], | |
166 | '172.17.0.0/16': ['172.17.0.1'], | |
167 | '192.168.39.0/24': ['192.168.39.1'], | |
168 | '192.168.122.0/24': ['192.168.122.1'], | |
169 | '192.168.178.0/24': ['192.168.178.28'] | |
170 | } | |
171 | ), ( | |
172 | """ | |
173 | default via 10.3.64.1 dev eno1 proto static metric 100 | |
174 | 10.3.64.0/24 dev eno1 proto kernel scope link src 10.3.64.23 metric 100 | |
175 | 10.3.64.0/24 dev eno1 proto kernel scope link src 10.3.64.27 metric 100 | |
176 | 10.88.0.0/16 dev cni-podman0 proto kernel scope link src 10.88.0.1 linkdown | |
177 | 172.21.0.0/20 via 172.21.3.189 dev tun0 | |
178 | 172.21.1.0/20 via 172.21.3.189 dev tun0 | |
179 | 172.21.2.1 via 172.21.3.189 dev tun0 | |
180 | 172.21.3.1 dev tun0 proto kernel scope link src 172.21.3.2 | |
181 | 172.21.4.0/24 via 172.21.3.1 dev tun0 | |
182 | 172.21.5.0/24 via 172.21.3.1 dev tun0 | |
183 | 172.21.6.0/24 via 172.21.3.1 dev tun0 | |
184 | 172.21.7.0/24 via 172.21.3.1 dev tun0 | |
185 | 192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1 linkdown | |
186 | """, | |
187 | { | |
188 | '10.3.64.0/24': ['10.3.64.23', '10.3.64.27'], | |
189 | '10.88.0.0/16': ['10.88.0.1'], | |
190 | '172.21.3.1': ['172.21.3.2'], | |
191 | '192.168.122.0/24': ['192.168.122.1']} | |
192 | ), | |
193 | ]) | |
f6b5b4d7 TL |
194 | def test_parse_ipv4_route(self, test_input, expected): |
195 | assert cd._parse_ipv4_route(test_input) == expected | |
196 | ||
197 | @pytest.mark.parametrize("test_routes, test_ips, expected", [ | |
198 | ( | |
199 | """ | |
200 | ::1 dev lo proto kernel metric 256 pref medium | |
201 | fdbc:7574:21fe:9200::/64 dev wlp2s0 proto ra metric 600 pref medium | |
202 | fdd8:591e:4969:6363::/64 dev wlp2s0 proto ra metric 600 pref medium | |
203 | fde4:8dba:82e1::/64 dev eth1 proto kernel metric 256 expires 1844sec pref medium | |
204 | fe80::/64 dev tun0 proto kernel metric 256 pref medium | |
205 | fe80::/64 dev wlp2s0 proto kernel metric 600 pref medium | |
206 | default dev tun0 proto static metric 50 pref medium | |
207 | default via fe80::2480:28ec:5097:3fe2 dev wlp2s0 proto ra metric 20600 pref medium | |
208 | """, | |
209 | """ | |
210 | 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 state UNKNOWN qlen 1000 | |
211 | inet6 ::1/128 scope host | |
212 | valid_lft forever preferred_lft forever | |
213 | 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000 | |
214 | inet6 fdd8:591e:4969:6363:4c52:cafe:8dd4:dc4/64 scope global temporary dynamic | |
215 | valid_lft 86394sec preferred_lft 14394sec | |
216 | inet6 fdbc:7574:21fe:9200:4c52:cafe:8dd4:dc4/64 scope global temporary dynamic | |
217 | valid_lft 6745sec preferred_lft 3145sec | |
218 | inet6 fdd8:591e:4969:6363:103a:abcd:af1f:57f3/64 scope global temporary deprecated dynamic | |
219 | valid_lft 86394sec preferred_lft 0sec | |
220 | inet6 fdbc:7574:21fe:9200:103a:abcd:af1f:57f3/64 scope global temporary deprecated dynamic | |
221 | valid_lft 6745sec preferred_lft 0sec | |
222 | inet6 fdd8:591e:4969:6363:a128:1234:2bdd:1b6f/64 scope global temporary deprecated dynamic | |
223 | valid_lft 86394sec preferred_lft 0sec | |
224 | inet6 fdbc:7574:21fe:9200:a128:1234:2bdd:1b6f/64 scope global temporary deprecated dynamic | |
225 | valid_lft 6745sec preferred_lft 0sec | |
226 | inet6 fdd8:591e:4969:6363:d581:4321:380b:3905/64 scope global temporary deprecated dynamic | |
227 | valid_lft 86394sec preferred_lft 0sec | |
228 | inet6 fdbc:7574:21fe:9200:d581:4321:380b:3905/64 scope global temporary deprecated dynamic | |
229 | valid_lft 6745sec preferred_lft 0sec | |
230 | inet6 fe80::1111:2222:3333:4444/64 scope link noprefixroute | |
231 | valid_lft forever preferred_lft forever | |
232 | inet6 fde4:8dba:82e1:0:ec4a:e402:e9df:b357/64 scope global temporary dynamic | |
233 | valid_lft 1074sec preferred_lft 1074sec | |
234 | inet6 fde4:8dba:82e1:0:5054:ff:fe72:61af/64 scope global dynamic mngtmpaddr | |
235 | valid_lft 1074sec preferred_lft 1074sec | |
236 | 12: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 state UNKNOWN qlen 100 | |
237 | inet6 fe80::cafe:cafe:cafe:cafe/64 scope link stable-privacy | |
238 | valid_lft forever preferred_lft forever | |
239 | """, | |
240 | { | |
241 | "::1": ["::1"], | |
242 | "fdbc:7574:21fe:9200::/64": ["fdbc:7574:21fe:9200:4c52:cafe:8dd4:dc4", | |
243 | "fdbc:7574:21fe:9200:103a:abcd:af1f:57f3", | |
244 | "fdbc:7574:21fe:9200:a128:1234:2bdd:1b6f", | |
245 | "fdbc:7574:21fe:9200:d581:4321:380b:3905"], | |
246 | "fdd8:591e:4969:6363::/64": ["fdd8:591e:4969:6363:4c52:cafe:8dd4:dc4", | |
247 | "fdd8:591e:4969:6363:103a:abcd:af1f:57f3", | |
248 | "fdd8:591e:4969:6363:a128:1234:2bdd:1b6f", | |
249 | "fdd8:591e:4969:6363:d581:4321:380b:3905"], | |
250 | "fde4:8dba:82e1::/64": ["fde4:8dba:82e1:0:ec4a:e402:e9df:b357", | |
251 | "fde4:8dba:82e1:0:5054:ff:fe72:61af"], | |
252 | "fe80::/64": ["fe80::1111:2222:3333:4444", | |
253 | "fe80::cafe:cafe:cafe:cafe"] | |
254 | } | |
255 | )]) | |
256 | def test_parse_ipv6_route(self, test_routes, test_ips, expected): | |
257 | assert cd._parse_ipv6_route(test_routes, test_ips) == expected | |
258 | ||
259 | def test_is_ipv6(self): | |
260 | cd.logger = mock.Mock() | |
261 | for good in ("[::1]", "::1", | |
262 | "fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"): | |
263 | assert cd.is_ipv6(good) | |
264 | for bad in ("127.0.0.1", | |
265 | "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffg", | |
266 | "1:2:3:4:5:6:7:8:9", "fd00::1::1", "[fg::1]"): | |
267 | assert not cd.is_ipv6(bad) | |
268 | ||
269 | def test_unwrap_ipv6(self): | |
270 | def unwrap_test(address, expected): | |
271 | assert cd.unwrap_ipv6(address) == expected | |
272 | ||
273 | tests = [ | |
274 | ('::1', '::1'), ('[::1]', '::1'), | |
275 | ('[fde4:8dba:82e1:0:5054:ff:fe6a:357]', 'fde4:8dba:82e1:0:5054:ff:fe6a:357'), | |
276 | ('can actually be any string', 'can actually be any string'), | |
277 | ('[but needs to be stripped] ', '[but needs to be stripped] ')] | |
278 | for address, expected in tests: | |
279 | unwrap_test(address, expected) | |
280 | ||
f91f0fd5 TL |
281 | def test_wrap_ipv6(self): |
282 | def wrap_test(address, expected): | |
283 | assert cd.wrap_ipv6(address) == expected | |
284 | ||
285 | tests = [ | |
286 | ('::1', '[::1]'), ('[::1]', '[::1]'), | |
287 | ('fde4:8dba:82e1:0:5054:ff:fe6a:357', | |
288 | '[fde4:8dba:82e1:0:5054:ff:fe6a:357]'), | |
289 | ('myhost.example.com', 'myhost.example.com'), | |
290 | ('192.168.0.1', '192.168.0.1'), | |
291 | ('', ''), ('fd00::1::1', 'fd00::1::1')] | |
292 | for address, expected in tests: | |
293 | wrap_test(address, expected) | |
294 | ||
f6b5b4d7 TL |
295 | @mock.patch('cephadm.call_throws') |
296 | @mock.patch('cephadm.get_parm') | |
297 | def test_registry_login(self, get_parm, call_throws): | |
298 | ||
299 | # test normal valid login with url, username and password specified | |
300 | call_throws.return_value = '', '', 0 | |
301 | args = cd._parse_args(['registry-login', '--registry-url', 'sample-url', '--registry-username', 'sample-user', '--registry-password', 'sample-pass']) | |
302 | cd.args = args | |
303 | retval = cd.command_registry_login() | |
304 | assert retval == 0 | |
305 | ||
306 | # test bad login attempt with invalid arguments given | |
307 | args = cd._parse_args(['registry-login', '--registry-url', 'bad-args-url']) | |
308 | cd.args = args | |
309 | with pytest.raises(Exception) as e: | |
310 | assert cd.command_registry_login() | |
311 | assert str(e.value) == ('Invalid custom registry arguments received. To login to a custom registry include ' | |
312 | '--registry-url, --registry-username and --registry-password options or --registry-json option') | |
313 | ||
314 | # test normal valid login with json file | |
315 | get_parm.return_value = {"url": "sample-url", "username": "sample-username", "password": "sample-password"} | |
316 | args = cd._parse_args(['registry-login', '--registry-json', 'sample-json']) | |
317 | cd.args = args | |
318 | retval = cd.command_registry_login() | |
319 | assert retval == 0 | |
320 | ||
321 | # test bad login attempt with bad json file | |
322 | get_parm.return_value = {"bad-json": "bad-json"} | |
323 | args = cd._parse_args(['registry-login', '--registry-json', 'sample-json']) | |
324 | cd.args = args | |
325 | with pytest.raises(Exception) as e: | |
326 | assert cd.command_registry_login() | |
327 | assert str(e.value) == ("json provided for custom registry login did not include all necessary fields. " | |
328 | "Please setup json file as\n" | |
329 | "{\n" | |
330 | " \"url\": \"REGISTRY_URL\",\n" | |
331 | " \"username\": \"REGISTRY_USERNAME\",\n" | |
332 | " \"password\": \"REGISTRY_PASSWORD\"\n" | |
333 | "}\n") | |
334 | ||
335 | # test login attempt with valid arguments where login command fails | |
336 | call_throws.side_effect = Exception | |
337 | args = cd._parse_args(['registry-login', '--registry-url', 'sample-url', '--registry-username', 'sample-user', '--registry-password', 'sample-pass']) | |
338 | cd.args = args | |
339 | with pytest.raises(Exception) as e: | |
340 | cd.command_registry_login() | |
341 | assert str(e.value) == "Failed to login to custom registry @ sample-url as sample-user with given password" | |
f91f0fd5 TL |
342 | |
343 | def test_get_image_info_from_inspect(self): | |
344 | # podman | |
cd265ab1 | 345 | out = """204a01f9b0b6710dd0c0af7f37ce7139c47ff0f0105d778d7104c69282dfbbf1,[docker.io/ceph/ceph@sha256:1cc9b824e1b076cdff52a9aa3f0cc8557d879fb2fbbba0cafed970aca59a3992]""" |
f91f0fd5 TL |
346 | r = cd.get_image_info_from_inspect(out, 'registry/ceph/ceph:latest') |
347 | assert r == { | |
348 | 'image_id': '204a01f9b0b6710dd0c0af7f37ce7139c47ff0f0105d778d7104c69282dfbbf1', | |
349 | 'repo_digest': 'docker.io/ceph/ceph@sha256:1cc9b824e1b076cdff52a9aa3f0cc8557d879fb2fbbba0cafed970aca59a3992' | |
350 | } | |
351 | ||
352 | # docker | |
cd265ab1 | 353 | out = """sha256:16f4549cf7a8f112bbebf7946749e961fbbd1b0838627fe619aab16bc17ce552,[quay.ceph.io/ceph-ci/ceph@sha256:4e13da36c1bd6780b312a985410ae678984c37e6a9493a74c87e4a50b9bda41f]""" |
f91f0fd5 TL |
354 | r = cd.get_image_info_from_inspect(out, 'registry/ceph/ceph:latest') |
355 | assert r == { | |
356 | 'image_id': '16f4549cf7a8f112bbebf7946749e961fbbd1b0838627fe619aab16bc17ce552', | |
357 | 'repo_digest': 'quay.ceph.io/ceph-ci/ceph@sha256:4e13da36c1bd6780b312a985410ae678984c37e6a9493a74c87e4a50b9bda41f' | |
358 | } | |
359 | ||
360 | def test_dict_get(self): | |
361 | result = cd.dict_get({'a': 1}, 'a', require=True) | |
362 | assert result == 1 | |
363 | result = cd.dict_get({'a': 1}, 'b') | |
364 | assert result is None | |
365 | result = cd.dict_get({'a': 1}, 'b', default=2) | |
366 | assert result == 2 | |
367 | ||
368 | def test_dict_get_error(self): | |
369 | with pytest.raises(cd.Error): | |
370 | cd.dict_get({'a': 1}, 'b', require=True) | |
371 | ||
372 | def test_dict_get_join(self): | |
373 | result = cd.dict_get_join({'foo': ['a', 'b']}, 'foo') | |
374 | assert result == 'a\nb' | |
375 | result = cd.dict_get_join({'foo': [1, 2]}, 'foo') | |
376 | assert result == '1\n2' | |
377 | result = cd.dict_get_join({'bar': 'a'}, 'bar') | |
378 | assert result == 'a' | |
379 | result = cd.dict_get_join({'a': 1}, 'a') | |
380 | assert result == 1 | |
381 | ||
adb31ebb TL |
382 | def test_last_local_images(self): |
383 | out = ''' | |
384 | docker.io/ceph/daemon-base@ | |
385 | docker.io/ceph/ceph:v15.2.5 | |
386 | docker.io/ceph/daemon-base:octopus | |
387 | ''' | |
388 | image = cd._filter_last_local_ceph_image(out) | |
389 | assert image == 'docker.io/ceph/ceph:v15.2.5' | |
390 | ||
f91f0fd5 TL |
391 | |
392 | class TestCustomContainer(unittest.TestCase): | |
393 | cc: cd.CustomContainer | |
394 | ||
395 | def setUp(self): | |
396 | self.cc = cd.CustomContainer( | |
397 | 'e863154d-33c7-4350-bca5-921e0467e55b', | |
398 | 'container', | |
399 | config_json={ | |
400 | 'entrypoint': 'bash', | |
401 | 'gid': 1000, | |
402 | 'args': [ | |
403 | '--no-healthcheck', | |
404 | '-p 6800:6800' | |
405 | ], | |
406 | 'envs': ['SECRET=password'], | |
407 | 'ports': [8080, 8443], | |
408 | 'volume_mounts': { | |
409 | '/CONFIG_DIR': '/foo/conf', | |
410 | 'bar/config': '/bar:ro' | |
411 | }, | |
412 | 'bind_mounts': [ | |
413 | [ | |
414 | 'type=bind', | |
415 | 'source=/CONFIG_DIR', | |
416 | 'destination=/foo/conf', | |
417 | '' | |
418 | ], | |
419 | [ | |
420 | 'type=bind', | |
421 | 'source=bar/config', | |
422 | 'destination=/bar:ro', | |
423 | 'ro=true' | |
424 | ] | |
425 | ] | |
426 | }, | |
427 | image='docker.io/library/hello-world:latest' | |
428 | ) | |
429 | ||
430 | def test_entrypoint(self): | |
431 | self.assertEqual(self.cc.entrypoint, 'bash') | |
432 | ||
433 | def test_uid_gid(self): | |
434 | self.assertEqual(self.cc.uid, 65534) | |
435 | self.assertEqual(self.cc.gid, 1000) | |
436 | ||
437 | def test_ports(self): | |
438 | self.assertEqual(self.cc.ports, [8080, 8443]) | |
439 | ||
440 | def test_get_container_args(self): | |
441 | result = self.cc.get_container_args() | |
442 | self.assertEqual(result, [ | |
443 | '--no-healthcheck', | |
444 | '-p 6800:6800' | |
445 | ]) | |
446 | ||
447 | def test_get_container_envs(self): | |
448 | result = self.cc.get_container_envs() | |
449 | self.assertEqual(result, ['SECRET=password']) | |
450 | ||
451 | def test_get_container_mounts(self): | |
452 | result = self.cc.get_container_mounts('/xyz') | |
453 | self.assertDictEqual(result, { | |
454 | '/CONFIG_DIR': '/foo/conf', | |
455 | '/xyz/bar/config': '/bar:ro' | |
456 | }) | |
457 | ||
458 | def test_get_container_binds(self): | |
459 | result = self.cc.get_container_binds('/xyz') | |
460 | self.assertEqual(result, [ | |
461 | [ | |
462 | 'type=bind', | |
463 | 'source=/CONFIG_DIR', | |
464 | 'destination=/foo/conf', | |
465 | '' | |
466 | ], | |
467 | [ | |
468 | 'type=bind', | |
469 | 'source=/xyz/bar/config', | |
470 | 'destination=/bar:ro', | |
471 | 'ro=true' | |
472 | ] | |
473 | ]) | |
7f7e6c64 TL |
474 | |
475 | ||
476 | class TestMonitoring(object): | |
477 | @mock.patch('cephadm.call') | |
478 | def test_get_version_alertmanager(self, _call): | |
479 | ctx = mock.Mock() | |
480 | daemon_type = 'alertmanager' | |
481 | ||
482 | # binary `prometheus` | |
483 | _call.return_value = '', '{}, version 0.16.1'.format(daemon_type), 0 | |
484 | version = cd.Monitoring.get_version(ctx, 'container_id', daemon_type) | |
485 | assert version == '0.16.1' | |
486 | ||
487 | # binary `prometheus-alertmanager` | |
488 | _call.side_effect = ( | |
489 | ('', '', 1), | |
490 | ('', '{}, version 0.16.1'.format(daemon_type), 0), | |
491 | ) | |
492 | version = cd.Monitoring.get_version(ctx, 'container_id', daemon_type) | |
493 | assert version == '0.16.1' | |
494 | ||
495 | @mock.patch('cephadm.call') | |
496 | def test_get_version_prometheus(self, _call): | |
497 | ctx = mock.Mock() | |
498 | daemon_type = 'prometheus' | |
499 | _call.return_value = '', '{}, version 0.16.1'.format(daemon_type), 0 | |
500 | version = cd.Monitoring.get_version(ctx, 'container_id', daemon_type) | |
501 | assert version == '0.16.1' | |
502 | ||
503 | @mock.patch('cephadm.call') | |
504 | def test_get_version_node_exporter(self, _call): | |
505 | ctx = mock.Mock() | |
506 | daemon_type = 'node-exporter' | |
507 | _call.return_value = '', '{}, version 0.16.1'.format(daemon_type.replace('-', '_')), 0 | |
508 | version = cd.Monitoring.get_version(ctx, 'container_id', daemon_type) | |
509 | assert version == '0.16.1' | |
ec96510d FG |
510 | |
511 | @mock.patch('cephadm.os.fchown') | |
512 | @mock.patch('cephadm.get_parm') | |
513 | @mock.patch('cephadm.makedirs') | |
514 | @mock.patch('cephadm.open') | |
515 | @mock.patch('cephadm.make_log_dir') | |
516 | @mock.patch('cephadm.make_data_dir') | |
517 | @mock.patch('cephadm.args') | |
518 | def test_create_daemon_dirs_prometheus(self, args, make_data_dir, make_log_dir, _open, makedirs, | |
519 | get_parm, fchown): | |
520 | """ | |
521 | Ensures the required and optional files given in the configuration are | |
522 | created and mapped correctly inside the container. Tests absolute and | |
523 | relative file paths given in the configuration. | |
524 | """ | |
525 | args.data_dir = '/somedir' | |
526 | fsid = 'aaf5a720-13fe-4a3b-82b9-2d99b7fd9704' | |
527 | daemon_type = 'prometheus' | |
528 | uid, gid = 50, 50 | |
529 | daemon_id = 'home' | |
530 | files = { | |
531 | 'files': { | |
532 | 'prometheus.yml': 'foo', | |
533 | '/etc/prometheus/alerting/ceph_alerts.yml': 'bar' | |
534 | } | |
535 | } | |
536 | get_parm.return_value = files | |
537 | ||
538 | cd.create_daemon_dirs(fsid, | |
539 | daemon_type, | |
540 | daemon_id, | |
541 | uid, | |
542 | gid, | |
543 | config=None, | |
544 | keyring=None) | |
545 | ||
546 | prefix = '{data_dir}/{fsid}/{daemon_type}.{daemon_id}'.format( | |
547 | data_dir=args.data_dir, | |
548 | fsid=fsid, | |
549 | daemon_type=daemon_type, | |
550 | daemon_id=daemon_id | |
551 | ) | |
552 | assert _open.call_args_list == [ | |
553 | call('{}/etc/prometheus/prometheus.yml'.format(prefix), 'w', | |
554 | encoding='utf-8'), | |
555 | call('{}/etc/prometheus/alerting/ceph_alerts.yml'.format(prefix), 'w', | |
556 | encoding='utf-8'), | |
557 | ] | |
558 | assert call().__enter__().write('foo') in _open.mock_calls | |
559 | assert call().__enter__().write('bar') in _open.mock_calls |