Merge ~peppepetra/charm-sudo-pair:blacken-20.08 into charm-sudo-pair:master
- Git
- lp:~peppepetra/charm-sudo-pair
- blacken-20.08
- Merge into master
Proposed by
Giuseppe Petralia
Status: | Merged |
---|---|
Approved by: | Xav Paice |
Approved revision: | 224d95033be074db000f82e2c66fbcede961d58c |
Merged at revision: | 224d95033be074db000f82e2c66fbcede961d58c |
Proposed branch: | ~peppepetra/charm-sudo-pair:blacken-20.08 |
Merge into: | charm-sudo-pair:master |
Prerequisite: | ~peppepetra/charm-sudo-pair:makefile-20.08 |
Diff against target: |
801 lines (+242/-151) 9 files modified
src/actions/actions.py (+1/-1) src/lib/libsudopair.py (+92/-38) src/reactive/sudo_pair.py (+17/-10) src/tests/functional/conftest.py (+27/-15) src/tests/functional/test_deploy.py (+1/-1) src/tests/unit/conftest.py (+6/-5) src/tests/unit/test_actions.py (+9/-8) src/tests/unit/test_libsudopair.py (+87/-70) src/tox.ini (+2/-3) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Xav Paice (community) | Approve | ||
Paul Goins | Approve | ||
Review via email: mp+388998@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/src/actions/actions.py b/src/actions/actions.py |
2 | index 1cb455c..2416eb3 100755 |
3 | --- a/src/actions/actions.py |
4 | +++ b/src/actions/actions.py |
5 | @@ -30,7 +30,7 @@ def remove(): |
6 | sph.deconfigure() |
7 | msg = "Successfully removed sudo-pair config and binaries" |
8 | action_set({"message": msg}) |
9 | - status_set('active', msg) |
10 | + status_set("active", msg) |
11 | |
12 | |
13 | # A dictionary of all the defined actions to callables (which take |
14 | diff --git a/src/lib/libsudopair.py b/src/lib/libsudopair.py |
15 | index 05d1f9d..cbaf467 100644 |
16 | --- a/src/lib/libsudopair.py |
17 | +++ b/src/lib/libsudopair.py |
18 | @@ -25,8 +25,8 @@ def group_names_to_group_ids(group_names): |
19 | :param group_names: i.e. "root,user1,user2" |
20 | :return gids: i.e. "0,1001,1002" |
21 | """ |
22 | - group_names = list(filter(check_valid_group, group_names.split(','))) |
23 | - return ','.join(map(str, (map(group_id, group_names)))) |
24 | + group_names = list(filter(check_valid_group, group_names.split(","))) |
25 | + return ",".join(map(str, (map(group_id, group_names)))) |
26 | |
27 | |
28 | def copy_file(source, destination, owner, group, perms): |
29 | @@ -37,8 +37,10 @@ def copy_file(source, destination, owner, group, perms): |
30 | # This is a terrible default directory permission, as the file |
31 | # or its siblings will often contain secrets. |
32 | host.mkdir(os.path.dirname(destination), owner, group, perms=0o755) |
33 | - with open(source, 'rb') as source_f: |
34 | - host.write_file(destination, source_f.read(), perms=perms, owner=owner, group=group) |
35 | + with open(source, "rb") as source_f: |
36 | + host.write_file( |
37 | + destination, source_f.read(), perms=perms, owner=owner, group=group |
38 | + ) |
39 | |
40 | |
41 | class SudoPairHelper(object): |
42 | @@ -47,17 +49,17 @@ class SudoPairHelper(object): |
43 | def __init__(self): |
44 | """Retrieve charm config and set defaults.""" |
45 | self.charm_config = hookenv.config() |
46 | - self.binary_path = '/usr/bin/sudo_approve' |
47 | - self.sudo_conf_path = '/etc/sudo.conf' |
48 | - self.sudoers_path = '/etc/sudoers' |
49 | - self.sudo_lib_path = '/usr/lib/sudo/sudo_pair.so' |
50 | + self.binary_path = "/usr/bin/sudo_approve" |
51 | + self.sudo_conf_path = "/etc/sudo.conf" |
52 | + self.sudoers_path = "/etc/sudoers" |
53 | + self.sudo_lib_path = "/usr/lib/sudo/sudo_pair.so" |
54 | self.sudoers_bypass_path = "/etc/sudoers.d/91-bypass-sudopair-cmds" |
55 | - self.user_prompt_path = '/etc/sudo_pair.prompt.user' |
56 | - self.pair_prompt_path = '/etc/sudo_pair.prompt.pair' |
57 | - self.socket_dir = '/var/run/sudo_pair' |
58 | - self.tmpfiles_conf = '/usr/lib/tmpfiles.d/sudo_pair.conf' |
59 | - self.owner = 'root' |
60 | - self.group = 'root' |
61 | + self.user_prompt_path = "/etc/sudo_pair.prompt.user" |
62 | + self.pair_prompt_path = "/etc/sudo_pair.prompt.pair" |
63 | + self.socket_dir = "/var/run/sudo_pair" |
64 | + self.tmpfiles_conf = "/usr/lib/tmpfiles.d/sudo_pair.conf" |
65 | + self.owner = "root" |
66 | + self.group = "root" |
67 | self.socket_dir_perms = 0o644 |
68 | self.sudo_pair_so_perms = 0o644 |
69 | self.prompt_perms = 0o644 |
70 | @@ -68,12 +70,16 @@ class SudoPairHelper(object): |
71 | def get_config(self): |
72 | """Return config as a dict.""" |
73 | config = { |
74 | - 'binary_path': self.binary_path, |
75 | - 'user_prompt_path': self.user_prompt_path, |
76 | - 'pair_prompt_path': self.pair_prompt_path, |
77 | - 'socket_dir': self.socket_dir, |
78 | - 'gids_enforced': group_names_to_group_ids(self.charm_config['groups_enforced']), |
79 | - 'gids_exempted': group_names_to_group_ids(self.charm_config['groups_exempted']), |
80 | + "binary_path": self.binary_path, |
81 | + "user_prompt_path": self.user_prompt_path, |
82 | + "pair_prompt_path": self.pair_prompt_path, |
83 | + "socket_dir": self.socket_dir, |
84 | + "gids_enforced": group_names_to_group_ids( |
85 | + self.charm_config["groups_enforced"] |
86 | + ), |
87 | + "gids_exempted": group_names_to_group_ids( |
88 | + self.charm_config["groups_exempted"] |
89 | + ), |
90 | } |
91 | |
92 | config.update(self.charm_config) |
93 | @@ -85,12 +91,23 @@ class SudoPairHelper(object): |
94 | |
95 | def render_sudo_conf(self): |
96 | """Render sudo.conf file.""" |
97 | - return templating.render('sudo.conf.tmpl', self.sudo_conf_path, self.get_config(), |
98 | - perms=self.sudo_conf_perms, owner=self.owner, group=self.group) |
99 | + return templating.render( |
100 | + "sudo.conf.tmpl", |
101 | + self.sudo_conf_path, |
102 | + self.get_config(), |
103 | + perms=self.sudo_conf_perms, |
104 | + owner=self.owner, |
105 | + group=self.group, |
106 | + ) |
107 | |
108 | def create_socket_dir(self): |
109 | """Create socket dir.""" |
110 | - host.mkdir(self.socket_dir, perms=self.socket_dir_perms, owner=self.owner, group=self.group) |
111 | + host.mkdir( |
112 | + self.socket_dir, |
113 | + perms=self.socket_dir_perms, |
114 | + owner=self.owner, |
115 | + group=self.group, |
116 | + ) |
117 | |
118 | def create_tmpfiles_conf(self): |
119 | """Create temporary conf file.""" |
120 | @@ -99,36 +116,71 @@ class SudoPairHelper(object): |
121 | |
122 | def install_sudo_pair_so(self): |
123 | """Install sudo-pair lib.""" |
124 | - sudo_pair_lib = os.path.join(hookenv.charm_dir(), 'files', 'sudo_pair.so') |
125 | - copy_file(sudo_pair_lib, self.sudo_lib_path, self.owner, self.group, self.sudo_pair_so_perms) |
126 | + sudo_pair_lib = os.path.join(hookenv.charm_dir(), "files", "sudo_pair.so") |
127 | + copy_file( |
128 | + sudo_pair_lib, |
129 | + self.sudo_lib_path, |
130 | + self.owner, |
131 | + self.group, |
132 | + self.sudo_pair_so_perms, |
133 | + ) |
134 | |
135 | def copy_user_prompt(self): |
136 | """Copy user prompt on the unit.""" |
137 | - prompt_file = os.path.join(hookenv.charm_dir(), 'files', 'sudo.prompt.user') |
138 | - copy_file(prompt_file, self.user_prompt_path, self.owner, self.group, self.prompt_perms) |
139 | + prompt_file = os.path.join(hookenv.charm_dir(), "files", "sudo.prompt.user") |
140 | + copy_file( |
141 | + prompt_file, |
142 | + self.user_prompt_path, |
143 | + self.owner, |
144 | + self.group, |
145 | + self.prompt_perms, |
146 | + ) |
147 | |
148 | def copy_pair_prompt(self): |
149 | """Copy pair prompt on the unit.""" |
150 | - prompt_file = os.path.join(hookenv.charm_dir(), 'files', 'sudo.prompt.pair') |
151 | - copy_file(prompt_file, self.pair_prompt_path, self.owner, self.group, self.prompt_perms) |
152 | + prompt_file = os.path.join(hookenv.charm_dir(), "files", "sudo.prompt.pair") |
153 | + copy_file( |
154 | + prompt_file, |
155 | + self.pair_prompt_path, |
156 | + self.owner, |
157 | + self.group, |
158 | + self.prompt_perms, |
159 | + ) |
160 | |
161 | def copy_sudoers(self): |
162 | """Copy sudoers file on the unit.""" |
163 | - sudoers_file = os.path.join(hookenv.charm_dir(), 'files', 'sudoers') |
164 | - copy_file(sudoers_file, self.sudoers_path, self.owner, self.group, self.sudoers_perms) |
165 | + sudoers_file = os.path.join(hookenv.charm_dir(), "files", "sudoers") |
166 | + copy_file( |
167 | + sudoers_file, self.sudoers_path, self.owner, self.group, self.sudoers_perms |
168 | + ) |
169 | |
170 | def render_sudo_approve(self): |
171 | """Render sudo-approve file.""" |
172 | hookenv.log("Rendering sudo_approve.tmpl to {}".format(self.binary_path)) |
173 | - return templating.render('sudo_approve.tmpl', self.binary_path, self.get_config(), |
174 | - perms=self.sudo_approve_perms, owner=self.owner, group=self.group) |
175 | + return templating.render( |
176 | + "sudo_approve.tmpl", |
177 | + self.binary_path, |
178 | + self.get_config(), |
179 | + perms=self.sudo_approve_perms, |
180 | + owner=self.owner, |
181 | + group=self.group, |
182 | + ) |
183 | |
184 | def render_bypass_cmds(self): |
185 | """Render bypass command file.""" |
186 | - if self.get_config()['bypass_cmds'] != "" and self.get_config()['bypass_group'] != "": |
187 | + if ( |
188 | + self.get_config()["bypass_cmds"] != "" |
189 | + and self.get_config()["bypass_group"] != "" |
190 | + ): |
191 | hookenv.log("Render bypass cmds to {}".format(self.sudoers_bypass_path)) |
192 | - return templating.render('91-bypass-sudopair-cmds.tmpl', self.sudoers_bypass_path, |
193 | - self.get_config(), perms=0o440, owner=self.owner, group=self.group) |
194 | + return templating.render( |
195 | + "91-bypass-sudopair-cmds.tmpl", |
196 | + self.sudoers_bypass_path, |
197 | + self.get_config(), |
198 | + perms=0o440, |
199 | + owner=self.owner, |
200 | + group=self.group, |
201 | + ) |
202 | return None |
203 | |
204 | def deconfigure(self): |
205 | @@ -140,7 +192,7 @@ class SudoPairHelper(object): |
206 | self.user_prompt_path, |
207 | self.pair_prompt_path, |
208 | self.sudoers_bypass_path, |
209 | - self.tmpfiles_conf |
210 | + self.tmpfiles_conf, |
211 | ] |
212 | hookenv.log("Deleting: {}".format(paths)) |
213 | for path in paths: |
214 | @@ -148,4 +200,6 @@ class SudoPairHelper(object): |
215 | os.unlink(path) |
216 | except Exception as e: |
217 | # We're trying hard to delete all files, even if some might fail |
218 | - hookenv.log("Got exception unlinking {}: {}, continuing".format(path, e)) |
219 | + hookenv.log( |
220 | + "Got exception unlinking {}: {}, continuing".format(path, e) |
221 | + ) |
222 | diff --git a/src/reactive/sudo_pair.py b/src/reactive/sudo_pair.py |
223 | index 974dd66..ed1d7ba 100644 |
224 | --- a/src/reactive/sudo_pair.py |
225 | +++ b/src/reactive/sudo_pair.py |
226 | @@ -7,10 +7,14 @@ from libsudopair import SudoPairHelper |
227 | sph = SudoPairHelper() |
228 | |
229 | |
230 | -@when('apt.installed.socat') |
231 | -@when_not('sudo-pair.configured') |
232 | +@when("apt.installed.socat") |
233 | +@when_not("sudo-pair.configured") |
234 | def install_sudo_pair(): |
235 | - # Install sudo_pair.so, create socket dir, copy sudo_approve to /usr/bin, copy prompts to /etc |
236 | + """Install sudo pair. |
237 | + |
238 | + Install sudo_pair.so, create socket dir, copy sudo_approve to /usr/bin |
239 | + and copy prompts to /etc. |
240 | + """ |
241 | sph.install_sudo_pair_so() |
242 | |
243 | sph.create_socket_dir() |
244 | @@ -32,18 +36,21 @@ def install_sudo_pair(): |
245 | # Add Plugin sudo_pair sudo_pair.so to sudo.conf |
246 | sph.render_sudo_conf() |
247 | |
248 | - set_state('sudo-pair.installed') |
249 | - set_state('sudo-pair.configured') |
250 | - hookenv.status_set('active', 'sudo pairing for users groups: [{}]'.format(sph.get_config()['gids_enforced'])) |
251 | + set_state("sudo-pair.installed") |
252 | + set_state("sudo-pair.configured") |
253 | + hookenv.status_set( |
254 | + "active", |
255 | + "sudo pairing for users groups: [{}]".format(sph.get_config()["gids_enforced"]), |
256 | + ) |
257 | |
258 | |
259 | -@hook('config-changed') |
260 | +@hook("config-changed") |
261 | def reconfigure_sudo_pair_charm(): |
262 | sph.set_charm_config(hookenv.config()) |
263 | - remove_state('sudo-pair.configured') |
264 | + remove_state("sudo-pair.configured") |
265 | |
266 | |
267 | -@hook('stop') |
268 | +@hook("stop") |
269 | def stop(): |
270 | sph.deconfigure() |
271 | - remove_state('sudo-pair.installed') |
272 | + remove_state("sudo-pair.installed") |
273 | diff --git a/src/tests/functional/conftest.py b/src/tests/functional/conftest.py |
274 | index 68f3ffe..7a8523e 100644 |
275 | --- a/src/tests/functional/conftest.py |
276 | +++ b/src/tests/functional/conftest.py |
277 | @@ -16,7 +16,7 @@ import pytest |
278 | STAT_FILE = "python3 -c \"import json; import os; s=os.stat('%s'); print(json.dumps({'uid': s.st_uid, 'gid': s.st_gid, 'mode': oct(s.st_mode), 'size': s.st_size}))\"" # noqa: E501 |
279 | |
280 | |
281 | -@pytest.yield_fixture(scope='module') |
282 | +@pytest.yield_fixture(scope="module") |
283 | def event_loop(request): |
284 | """Override the default pytest event loop to allow for broaded scopedv fixtures.""" |
285 | loop = asyncio.get_event_loop_policy().new_event_loop() |
286 | @@ -27,7 +27,7 @@ def event_loop(request): |
287 | asyncio.set_event_loop(None) |
288 | |
289 | |
290 | -@pytest.fixture(scope='module') |
291 | +@pytest.fixture(scope="module") |
292 | async def controller(): |
293 | """Connect to the current controller.""" |
294 | controller = Controller() |
295 | @@ -36,21 +36,21 @@ async def controller(): |
296 | await controller.disconnect() |
297 | |
298 | |
299 | -@pytest.fixture(scope='module') |
300 | +@pytest.fixture(scope="module") |
301 | async def model(controller): |
302 | """Create a model that lives only for the duration of the test.""" |
303 | model_name = "functest-{}".format(uuid.uuid4()) |
304 | model = await controller.add_model(model_name) |
305 | yield model |
306 | await model.disconnect() |
307 | - if os.getenv('PYTEST_KEEP_MODEL'): |
308 | + if os.getenv("PYTEST_KEEP_MODEL"): |
309 | return |
310 | await controller.destroy_model(model_name) |
311 | while model_name in await controller.list_models(): |
312 | await asyncio.sleep(1) |
313 | |
314 | |
315 | -@pytest.fixture(scope='module') |
316 | +@pytest.fixture(scope="module") |
317 | async def current_model(): |
318 | """Return the current model, does not create or destroy it.""" |
319 | model = Model() |
320 | @@ -62,29 +62,34 @@ async def current_model(): |
321 | @pytest.fixture |
322 | async def get_app(model): |
323 | """Return the application requested.""" |
324 | + |
325 | async def _get_app(name): |
326 | try: |
327 | return model.applications[name] |
328 | except KeyError: |
329 | raise JujuError("Cannot find application {}".format(name)) |
330 | + |
331 | return _get_app |
332 | |
333 | |
334 | @pytest.fixture |
335 | async def get_unit(model): |
336 | """Return the requested <app_name>/<unit_number> unit.""" |
337 | + |
338 | async def _get_unit(name): |
339 | try: |
340 | - (app_name, unit_number) = name.split('/') |
341 | + (app_name, unit_number) = name.split("/") |
342 | return model.applications[app_name].units[unit_number] |
343 | except (KeyError, ValueError): |
344 | raise JujuError("Cannot find unit {}".format(name)) |
345 | + |
346 | return _get_unit |
347 | |
348 | |
349 | @pytest.fixture |
350 | async def get_entity(model, get_unit, get_app): |
351 | """Return a unit or an application.""" |
352 | + |
353 | async def _get_entity(name): |
354 | try: |
355 | return await get_unit(name) |
356 | @@ -93,6 +98,7 @@ async def get_entity(model, get_unit, get_app): |
357 | return await get_app(name) |
358 | except JujuError: |
359 | raise JujuError("Cannot find entity {}".format(name)) |
360 | + |
361 | return _get_entity |
362 | |
363 | |
364 | @@ -104,14 +110,12 @@ async def run_command(get_unit): |
365 | :param cmd: Command to be run |
366 | :param target: Unit object or unit name string |
367 | """ |
368 | + |
369 | async def _run_command(cmd, target): |
370 | - unit = ( |
371 | - target |
372 | - if type(target) is juju.unit.Unit |
373 | - else await get_unit(target) |
374 | - ) |
375 | + unit = target if type(target) is juju.unit.Unit else await get_unit(target) |
376 | action = await unit.run(cmd) |
377 | return action.results |
378 | + |
379 | return _run_command |
380 | |
381 | |
382 | @@ -123,10 +127,12 @@ async def file_stat(run_command): |
383 | :param path: File path |
384 | :param target: Unit object or unit name string |
385 | """ |
386 | + |
387 | async def _file_stat(path, target): |
388 | cmd = STAT_FILE % path |
389 | results = await run_command(cmd, target) |
390 | - return json.loads(results['Stdout']) |
391 | + return json.loads(results["Stdout"]) |
392 | + |
393 | return _file_stat |
394 | |
395 | |
396 | @@ -138,16 +144,19 @@ async def file_contents(run_command): |
397 | :param path: File path |
398 | :param target: Unit object or unit name string |
399 | """ |
400 | + |
401 | async def _file_contents(path, target): |
402 | - cmd = 'cat {}'.format(path) |
403 | + cmd = "cat {}".format(path) |
404 | results = await run_command(cmd, target) |
405 | - return results['Stdout'] |
406 | + return results["Stdout"] |
407 | + |
408 | return _file_contents |
409 | |
410 | |
411 | @pytest.fixture |
412 | async def reconfigure_app(get_app, model): |
413 | """Apply a different config to the requested app.""" |
414 | + |
415 | async def _reconfigure_app(cfg, target): |
416 | application = ( |
417 | target |
418 | @@ -156,14 +165,17 @@ async def reconfigure_app(get_app, model): |
419 | ) |
420 | await application.set_config(cfg) |
421 | await application.get_config() |
422 | - await model.block_until(lambda: application.status == 'active') |
423 | + await model.block_until(lambda: application.status == "active") |
424 | + |
425 | return _reconfigure_app |
426 | |
427 | |
428 | @pytest.fixture |
429 | async def create_group(run_command): |
430 | """Create the UNIX group specified.""" |
431 | + |
432 | async def _create_group(group_name, target): |
433 | cmd = "sudo groupadd %s" % group_name |
434 | await run_command(cmd, target) |
435 | + |
436 | return _create_group |
437 | diff --git a/src/tests/functional/test_deploy.py b/src/tests/functional/test_deploy.py |
438 | index 1d531d1..7f24224 100644 |
439 | --- a/src/tests/functional/test_deploy.py |
440 | +++ b/src/tests/functional/test_deploy.py |
441 | @@ -117,7 +117,7 @@ async def test_sudoers_bypass_conf(file_contents, unit): |
442 | |
443 | |
444 | async def test_reconfigure(reconfigure_app, file_contents, unit, app): |
445 | - """Change a charm config parameter and verify that it has been propagated to the unit.""" |
446 | + """Change a charm config parameter and verify it is applied.""" |
447 | sudo_approve_path = "/usr/bin/sudo_approve" |
448 | await reconfigure_app(cfg={"auto_approve": "false"}, target=app) |
449 | sudo_approve_content = await file_contents(path=sudo_approve_path, target=unit) |
450 | diff --git a/src/tests/unit/conftest.py b/src/tests/unit/conftest.py |
451 | index c0800ea..9ca50ec 100644 |
452 | --- a/src/tests/unit/conftest.py |
453 | +++ b/src/tests/unit/conftest.py |
454 | @@ -14,25 +14,26 @@ def mock_hookenv_config(monkeypatch): |
455 | |
456 | def mock_config(): |
457 | cfg = {} |
458 | - yml = yaml.load(open('./config.yaml')) |
459 | + yml = yaml.load(open("./config.yaml")) |
460 | |
461 | # Load all defaults |
462 | - for key, value in yml['options'].items(): |
463 | - cfg[key] = value['default'] |
464 | + for key, value in yml["options"].items(): |
465 | + cfg[key] = value["default"] |
466 | |
467 | return cfg |
468 | |
469 | - monkeypatch.setattr('charmhelpers.core.hookenv.config', mock_config) |
470 | + monkeypatch.setattr("charmhelpers.core.hookenv.config", mock_config) |
471 | |
472 | |
473 | @pytest.fixture |
474 | def mock_charm_dir(monkeypatch): |
475 | - monkeypatch.setattr('charmhelpers.core.hookenv.charm_dir', lambda: '.') |
476 | + monkeypatch.setattr("charmhelpers.core.hookenv.charm_dir", lambda: ".") |
477 | |
478 | |
479 | @pytest.fixture |
480 | def sph(mock_hookenv_config, mock_charm_dir, tmpdir): |
481 | from libsudopair import SudoPairHelper |
482 | + |
483 | sph = SudoPairHelper() |
484 | sph.owner = pwd.getpwuid(os.getuid()).pw_name |
485 | sph.group = grp.getgrgid(os.getgid()).gr_name |
486 | diff --git a/src/tests/unit/test_actions.py b/src/tests/unit/test_actions.py |
487 | index c2da297..d6d86d7 100644 |
488 | --- a/src/tests/unit/test_actions.py |
489 | +++ b/src/tests/unit/test_actions.py |
490 | @@ -1,17 +1,18 @@ |
491 | import os |
492 | import sys |
493 | import unittest.mock as mock |
494 | -action_path = os.path.join(os.path.dirname(__file__), '..', '..', 'actions') |
495 | + |
496 | +action_path = os.path.join(os.path.dirname(__file__), "..", "..", "actions") |
497 | sys.path.append(action_path) |
498 | -import actions # NOQA |
499 | +import actions # NOQA |
500 | |
501 | |
502 | -@mock.patch('libsudopair.SudoPairHelper') |
503 | -@mock.patch('actions.action_set') |
504 | -@mock.patch('actions.status_set') |
505 | +@mock.patch("libsudopair.SudoPairHelper") |
506 | +@mock.patch("actions.action_set") |
507 | +@mock.patch("actions.status_set") |
508 | def test_remove_action(status_set, action_set, sudo_pair_helper): |
509 | actions.remove() |
510 | - msg = 'Successfully removed sudo-pair config and binaries' |
511 | - action_set.assert_called_with({'message': msg}) |
512 | - status_set.assert_called_with('active', msg) |
513 | + msg = "Successfully removed sudo-pair config and binaries" |
514 | + action_set.assert_called_with({"message": msg}) |
515 | + status_set.assert_called_with("active", msg) |
516 | sudo_pair_helper().deconfigure.assert_called() |
517 | diff --git a/src/tests/unit/test_libsudopair.py b/src/tests/unit/test_libsudopair.py |
518 | index 3a598af..874f0a3 100644 |
519 | --- a/src/tests/unit/test_libsudopair.py |
520 | +++ b/src/tests/unit/test_libsudopair.py |
521 | @@ -2,14 +2,11 @@ import filecmp |
522 | import grp |
523 | import os |
524 | |
525 | -from libsudopair import ( |
526 | - check_valid_group, |
527 | - group_id |
528 | -) |
529 | +from libsudopair import check_valid_group, group_id |
530 | |
531 | |
532 | def test_check_valid_group(): |
533 | - assert not check_valid_group('fake_group') |
534 | + assert not check_valid_group("fake_group") |
535 | assert check_valid_group(grp.getgrgid(os.getgid()).gr_name) |
536 | |
537 | |
538 | @@ -17,7 +14,7 @@ def test_group_id(): |
539 | assert group_id(grp.getgrgid(os.getgid()).gr_name) == os.getgid() |
540 | |
541 | |
542 | -class TestSudoPairHelper(): |
543 | +class TestSudoPairHelper: |
544 | """Module to test SudoPairHelper lib.""" |
545 | |
546 | def test_pytest(self): |
547 | @@ -31,12 +28,12 @@ class TestSudoPairHelper(): |
548 | def test_get_config(self, sph): |
549 | """Check if config contains all the required entries.""" |
550 | default_keywords = [ |
551 | - 'binary_path', |
552 | - 'user_prompt_path', |
553 | - 'pair_prompt_path', |
554 | - 'socket_dir', |
555 | - 'gids_enforced', |
556 | - 'gids_exempted', |
557 | + "binary_path", |
558 | + "user_prompt_path", |
559 | + "pair_prompt_path", |
560 | + "socket_dir", |
561 | + "gids_enforced", |
562 | + "gids_exempted", |
563 | ] |
564 | config = sph.get_config() |
565 | for option in default_keywords: |
566 | @@ -45,11 +42,11 @@ class TestSudoPairHelper(): |
567 | def test_set_charm_config(self, sph): |
568 | """Set new config.""" |
569 | charm_config = { |
570 | - 'groups_enforced': 'root', |
571 | - 'groups_exempted': '', |
572 | - 'bypass_cmds': '', |
573 | - 'bypass_group': '', |
574 | - 'auto_approve': True |
575 | + "groups_enforced": "root", |
576 | + "groups_exempted": "", |
577 | + "bypass_cmds": "", |
578 | + "bypass_group": "", |
579 | + "auto_approve": True, |
580 | } |
581 | |
582 | sph.set_charm_config(charm_config) |
583 | @@ -62,67 +59,79 @@ class TestSudoPairHelper(): |
584 | """Check that sudo.conf is rendered correctly.""" |
585 | # Default config |
586 | content = sph.render_sudo_conf() |
587 | - expected_content = 'Plugin sudo_pair sudo_pair.so binary_path={} ' \ |
588 | - 'user_prompt_path={} ' \ |
589 | - 'pair_prompt_path={} socket_dir={} gids_enforced={}'.format( |
590 | - tmpdir.join('/usr/bin/sudo_approve'), |
591 | - tmpdir.join('/etc/sudo_pair.prompt.user'), |
592 | - tmpdir.join('/etc/sudo_pair.prompt.pair'), |
593 | - tmpdir.join('/var/run/sudo_pair'), |
594 | - '0') |
595 | + expected_content = ( |
596 | + "Plugin sudo_pair sudo_pair.so binary_path={} " |
597 | + "user_prompt_path={} " |
598 | + "pair_prompt_path={} socket_dir={} gids_enforced={}".format( |
599 | + tmpdir.join("/usr/bin/sudo_approve"), |
600 | + tmpdir.join("/etc/sudo_pair.prompt.user"), |
601 | + tmpdir.join("/etc/sudo_pair.prompt.pair"), |
602 | + tmpdir.join("/var/run/sudo_pair"), |
603 | + "0", |
604 | + ) |
605 | + ) |
606 | assert expected_content in content |
607 | |
608 | # Gid exempted |
609 | groups_exempted = grp.getgrgid(os.getgid()).gr_name |
610 | charm_config = { |
611 | - 'groups_enforced': 'root', |
612 | - 'groups_exempted': groups_exempted, |
613 | - 'bypass_cmds': '', |
614 | - 'bypass_group': '', |
615 | - 'auto_approve': True |
616 | + "groups_enforced": "root", |
617 | + "groups_exempted": groups_exempted, |
618 | + "bypass_cmds": "", |
619 | + "bypass_group": "", |
620 | + "auto_approve": True, |
621 | } |
622 | |
623 | sph.set_charm_config(charm_config) |
624 | - expected_content = \ |
625 | - 'Plugin sudo_pair sudo_pair.so binary_path={} user_prompt_path={} ' \ |
626 | - 'pair_prompt_path={} socket_dir={} gids_enforced={} gids_exempted={}'.format( |
627 | - tmpdir.join('/usr/bin/sudo_approve'), |
628 | - tmpdir.join('/etc/sudo_pair.prompt.user'), |
629 | - tmpdir.join('/etc/sudo_pair.prompt.pair'), |
630 | - tmpdir.join('/var/run/sudo_pair'), '0', os.getgid()) |
631 | + expected_content = ( |
632 | + "Plugin sudo_pair sudo_pair.so binary_path={} user_prompt_path={} " |
633 | + "pair_prompt_path={} socket_dir={} gids_enforced={} " |
634 | + "gids_exempted={}".format( |
635 | + tmpdir.join("/usr/bin/sudo_approve"), |
636 | + tmpdir.join("/etc/sudo_pair.prompt.user"), |
637 | + tmpdir.join("/etc/sudo_pair.prompt.pair"), |
638 | + tmpdir.join("/var/run/sudo_pair"), |
639 | + "0", |
640 | + os.getgid(), |
641 | + ) |
642 | + ) |
643 | |
644 | content = sph.render_sudo_conf() |
645 | assert expected_content in content |
646 | |
647 | # Groups enforced |
648 | - groups_enforced = 'root,' + grp.getgrgid(os.getgid()).gr_name |
649 | + groups_enforced = "root," + grp.getgrgid(os.getgid()).gr_name |
650 | charm_config = { |
651 | - 'groups_enforced': groups_enforced, |
652 | - 'groups_exempted': '', |
653 | - 'bypass_cmds': '', |
654 | - 'bypass_group': '', |
655 | - 'auto_approve': True |
656 | + "groups_enforced": groups_enforced, |
657 | + "groups_exempted": "", |
658 | + "bypass_cmds": "", |
659 | + "bypass_group": "", |
660 | + "auto_approve": True, |
661 | } |
662 | sph.set_charm_config(charm_config) |
663 | - expected_content = 'Plugin sudo_pair sudo_pair.so binary_path={} user_prompt_path={} ' \ |
664 | - 'pair_prompt_path={} socket_dir={} gids_enforced={}'.format( |
665 | - tmpdir.join('/usr/bin/sudo_approve'), |
666 | - tmpdir.join('/etc/sudo_pair.prompt.user'), |
667 | - tmpdir.join('/etc/sudo_pair.prompt.pair'), |
668 | - tmpdir.join('/var/run/sudo_pair'), '0,{}'.format(os.getgid())) |
669 | + expected_content = ( |
670 | + "Plugin sudo_pair sudo_pair.so binary_path={} user_prompt_path={} " |
671 | + "pair_prompt_path={} socket_dir={} gids_enforced={}".format( |
672 | + tmpdir.join("/usr/bin/sudo_approve"), |
673 | + tmpdir.join("/etc/sudo_pair.prompt.user"), |
674 | + tmpdir.join("/etc/sudo_pair.prompt.pair"), |
675 | + tmpdir.join("/var/run/sudo_pair"), |
676 | + "0,{}".format(os.getgid()), |
677 | + ) |
678 | + ) |
679 | content = sph.render_sudo_conf() |
680 | assert expected_content in content |
681 | |
682 | def test_render_bypass_cmds(self, sph): |
683 | """Check that sudoers file is rendered correctly.""" |
684 | # Root bypass /bin/ls |
685 | - expected_content = '%root ALL = (ALL) NOLOG_OUTPUT: /bin/ls' |
686 | + expected_content = "%root ALL = (ALL) NOLOG_OUTPUT: /bin/ls" |
687 | charm_config = { |
688 | - 'groups_enforced': 'root', |
689 | - 'groups_exempted': '', |
690 | - 'bypass_cmds': '/bin/ls', |
691 | - 'bypass_group': 'root', |
692 | - 'auto_approve': True |
693 | + "groups_enforced": "root", |
694 | + "groups_exempted": "", |
695 | + "bypass_cmds": "/bin/ls", |
696 | + "bypass_group": "root", |
697 | + "auto_approve": True, |
698 | } |
699 | sph.set_charm_config(charm_config) |
700 | content = sph.render_bypass_cmds() |
701 | @@ -131,9 +140,11 @@ class TestSudoPairHelper(): |
702 | def test_render_sudo_approve(self, sph, tmpdir): |
703 | """Check that sudo_approve file is rendered correctly.""" |
704 | # Auto Approve true |
705 | - expected_content = 'echo ${log_line} >> /var/log/sudo_pair.log' |
706 | - socket_dir = tmpdir.join('/var/run/sudo_pair') |
707 | - expected_content_socket_dir = 'declare -r SUDO_SOCKET_PATH="{}"'.format(socket_dir) |
708 | + expected_content = "echo ${log_line} >> /var/log/sudo_pair.log" |
709 | + socket_dir = tmpdir.join("/var/run/sudo_pair") |
710 | + expected_content_socket_dir = 'declare -r SUDO_SOCKET_PATH="{}"'.format( |
711 | + socket_dir |
712 | + ) |
713 | content = sph.render_sudo_approve() |
714 | assert expected_content in content |
715 | assert expected_content_socket_dir in content |
716 | @@ -141,11 +152,11 @@ class TestSudoPairHelper(): |
717 | # Auto Approve false |
718 | expected_content = 'echo "You can\'t approve your own session."' |
719 | charm_config = { |
720 | - 'groups_enforced': 'root', |
721 | - 'groups_exempted': '', |
722 | - 'bypass_cmds': '/bin/ls', |
723 | - 'bypass_group': 'root', |
724 | - 'auto_approve': False |
725 | + "groups_enforced": "root", |
726 | + "groups_exempted": "", |
727 | + "bypass_cmds": "/bin/ls", |
728 | + "bypass_group": "root", |
729 | + "auto_approve": False, |
730 | } |
731 | sph.set_charm_config(charm_config) |
732 | content = sph.render_sudo_approve() |
733 | @@ -154,32 +165,38 @@ class TestSudoPairHelper(): |
734 | def test_create_socket_dir(self, sph, tmpdir): |
735 | """Check that sudo_pair socket dir exists.""" |
736 | sph.create_socket_dir() |
737 | - assert os.path.exists(tmpdir.join('/var/run/sudo_pair')) |
738 | + assert os.path.exists(tmpdir.join("/var/run/sudo_pair")) |
739 | |
740 | def test_create_tmpfiles_conf(self, sph, tmpdir): |
741 | """Check that sudo pair temporary conf is rendered correctly.""" |
742 | sph.create_tmpfiles_conf() |
743 | - expected_content = 'd {} 0755 - - -\n'.format(sph.socket_dir) |
744 | - with open(tmpdir.join('/usr/lib/tmpfiles.d/sudo_pair.conf')) as f: |
745 | + expected_content = "d {} 0755 - - -\n".format(sph.socket_dir) |
746 | + with open(tmpdir.join("/usr/lib/tmpfiles.d/sudo_pair.conf")) as f: |
747 | content = f.read() |
748 | assert expected_content in content |
749 | |
750 | def test_install_sudo_pair_so(self, sph, tmpdir): |
751 | """Check that sudo system lib exists.""" |
752 | sph.install_sudo_pair_so() |
753 | - assert filecmp.cmp('./files/sudo_pair.so', tmpdir.join('/usr/lib/sudo/sudo_pair.so')) |
754 | + assert filecmp.cmp( |
755 | + "./files/sudo_pair.so", tmpdir.join("/usr/lib/sudo/sudo_pair.so") |
756 | + ) |
757 | |
758 | def test_copy_user_prompt(self, sph, tmpdir): |
759 | """Check that user prompt exists.""" |
760 | sph.copy_user_prompt() |
761 | - assert filecmp.cmp('./files/sudo.prompt.user', tmpdir.join('/etc/sudo_pair.prompt.user')) |
762 | + assert filecmp.cmp( |
763 | + "./files/sudo.prompt.user", tmpdir.join("/etc/sudo_pair.prompt.user") |
764 | + ) |
765 | |
766 | def test_copy_pair_prompt(self, sph, tmpdir): |
767 | """Check that pair prompt exists.""" |
768 | sph.copy_pair_prompt() |
769 | - assert filecmp.cmp('./files/sudo.prompt.pair', tmpdir.join('/etc/sudo_pair.prompt.pair')) |
770 | + assert filecmp.cmp( |
771 | + "./files/sudo.prompt.pair", tmpdir.join("/etc/sudo_pair.prompt.pair") |
772 | + ) |
773 | |
774 | def test_copy_sudoers(self, sph, tmpdir): |
775 | """Check that sudoers file exists.""" |
776 | sph.copy_sudoers() |
777 | - assert filecmp.cmp('./files/sudoers', tmpdir.join('/etc/sudoers')) |
778 | + assert filecmp.cmp("./files/sudoers", tmpdir.join("/etc/sudoers")) |
779 | diff --git a/src/tox.ini b/src/tox.ini |
780 | index d07b692..2085ea9 100644 |
781 | --- a/src/tox.ini |
782 | +++ b/src/tox.ini |
783 | @@ -25,7 +25,7 @@ passenv = |
784 | [testenv:lint] |
785 | commands = |
786 | flake8 |
787 | -#TODO black --check --exclude "/(\.eggs|\.git|\.tox|\.venv|\.build|dist|charmhelpers|mod)/" . |
788 | + black --check --exclude "/(\.eggs|\.git|\.tox|\.venv|\.build|dist|charmhelpers|mod)/" . |
789 | deps = |
790 | black |
791 | flake8 |
792 | @@ -43,8 +43,7 @@ exclude = |
793 | mod, |
794 | .build |
795 | |
796 | -max-line-length = 120 |
797 | -#TODO max-line-length = 88 |
798 | +max-line-length = 88 |
799 | max-complexity = 10 |
800 | |
801 | [testenv:black] |
LGTM.