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