Merge ~peppepetra/charm-canonical-livepatch:fix_linting into charm-canonical-livepatch:master
- Git
- lp:~peppepetra/charm-canonical-livepatch
- fix_linting
- Merge into master
Proposed by
Giuseppe Petralia
Status: | Merged |
---|---|
Approved by: | Giuseppe Petralia |
Approved revision: | 0013cc5cfeb311b76e921b73812a9bf0889e6f9a |
Merged at revision: | 7bf3fd86aca5fc26e02cbabffa9f047004a2e79e |
Proposed branch: | ~peppepetra/charm-canonical-livepatch:fix_linting |
Merge into: | charm-canonical-livepatch:master |
Prerequisite: | ~peppepetra/charm-canonical-livepatch:zaza_functests |
Diff against target: |
933 lines (+268/-203) 5 files modified
Makefile (+3/-3) actions/actions.py (+31/-21) files/check_canonical-livepatch.py (+54/-50) reactive/canonical_livepatch.py (+149/-111) tests/functional/tests/test_canonical_livepatch.py (+31/-18) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Paul Goins | Approve | ||
Drew Freiberger (community) | Needs Fixing | ||
Review via email: mp+387846@code.launchpad.net |
Commit message
Fix linting
Description of the change
To post a comment you must log in.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Revision history for this message
Drew Freiberger (afreiberger) wrote : | # |
a couple minor nits inline.
Thanks for this!
review:
Needs Fixing
Revision history for this message
Paul Goins (vultaire) wrote : | # |
Pretty good, only minor stuff to fix.
review:
Needs Fixing
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Change successfully merged at revision 7bf3fd86aca5fc2
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/Makefile b/Makefile |
2 | index e5fd995..fa5236f 100644 |
3 | --- a/Makefile |
4 | +++ b/Makefile |
5 | @@ -30,9 +30,9 @@ clean: |
6 | |
7 | submodules: |
8 | @echo "Cloning submodules" |
9 | - @git submodule update --init --recursive --remote --merge |
10 | + @git submodule update --init --recursive |
11 | |
12 | -build: submodules |
13 | +build: |
14 | @echo "Building charm to directory ${CHARM_BUILD_DIR}/${CHARM_NAME}" |
15 | @-git describe --tags > ./repo-info |
16 | @CHARM_LAYERS_DIR=${CHARM_LAYERS_DIR} CHARM_INTERFACES_DIR=${CHARM_INTERFACES_DIR} \ |
17 | @@ -56,7 +56,7 @@ functional: build |
18 | @echo "Executing functional tests in ${CHARM_BUILD_DIR}" |
19 | @CHARM_BUILD_DIR=${CHARM_BUILD_DIR} tox -e func |
20 | |
21 | -test: proof unittests functional |
22 | +test: lint proof unittests functional |
23 | @echo "Tests completed for charm ${CHARM_NAME}." |
24 | |
25 | # The targets below don't depend on a file |
26 | diff --git a/actions/actions.py b/actions/actions.py |
27 | index 6c956cd..e3930d1 100755 |
28 | --- a/actions/actions.py |
29 | +++ b/actions/actions.py |
30 | @@ -1,76 +1,86 @@ |
31 | #!/usr/local/sbin/charm-env python3 |
32 | +"""Charm actions.""" |
33 | |
34 | import os.path |
35 | import sys |
36 | import traceback |
37 | -from subprocess import check_output, CalledProcessError |
38 | +from subprocess import CalledProcessError, check_output |
39 | + |
40 | from charmhelpers.core import hookenv |
41 | |
42 | |
43 | def activate(params): |
44 | + """Execure activate action.""" |
45 | config = hookenv.config() |
46 | - livepatch_key = config.get('livepatch_key') |
47 | + livepatch_key = config.get("livepatch_key") |
48 | |
49 | if livepatch_key: |
50 | # disable prior to enabling to work around LP#1628823 |
51 | - cmd = ['/snap/bin/canonical-livepatch', 'disable'] |
52 | + cmd = ["/snap/bin/canonical-livepatch", "disable"] |
53 | try: |
54 | check_output(cmd, universal_newlines=True) |
55 | except CalledProcessError as e: |
56 | - hookenv.log('Unable to deactivate: {}'.format(str(e))) |
57 | + hookenv.log("Unable to deactivate: {}".format(str(e))) |
58 | # but let's soldier on... |
59 | - cmd = ['/snap/bin/canonical-livepatch', 'enable', '{}'.format(livepatch_key.strip())] |
60 | + cmd = [ |
61 | + "/snap/bin/canonical-livepatch", |
62 | + "enable", |
63 | + "{}".format(livepatch_key.strip()), |
64 | + ] |
65 | try: |
66 | check_output(cmd, universal_newlines=True) |
67 | except CalledProcessError as e: |
68 | - hookenv.action_fail('Unable to activate: {}'.format(str(e))) |
69 | + hookenv.action_fail("Unable to activate: {}".format(str(e))) |
70 | return |
71 | else: |
72 | - hookenv.action_set(dict(result='Activated')) |
73 | + hookenv.action_set(dict(result="Activated")) |
74 | |
75 | else: |
76 | - hookenv.action_fail('Unable to activate as no key has been set') |
77 | + hookenv.action_fail("Unable to activate as no key has been set") |
78 | |
79 | |
80 | def deactivate(params): |
81 | - cmd = ['/snap/bin/canonical-livepatch', 'disable'] |
82 | + """Execute deactivate action.""" |
83 | + cmd = ["/snap/bin/canonical-livepatch", "disable"] |
84 | try: |
85 | check_output(cmd, universal_newlines=True) |
86 | except CalledProcessError as e: |
87 | - hookenv.action_fail('Unable to deactivate: {}'.format(str(e))) |
88 | + hookenv.action_fail("Unable to deactivate: {}".format(str(e))) |
89 | return |
90 | - hookenv.action_set(dict(result='Deactivated')) |
91 | + hookenv.action_set(dict(result="Deactivated")) |
92 | |
93 | |
94 | def refresh(params): |
95 | - cmd = ['/snap/bin/canonical-livepatch', 'refresh'] |
96 | + """Execute refresh action.""" |
97 | + cmd = ["/snap/bin/canonical-livepatch", "refresh"] |
98 | try: |
99 | check_output(cmd, universal_newlines=True) |
100 | except CalledProcessError as e: |
101 | - hookenv.action_fail('Unable to refresh: {}'.format(str(e))) |
102 | + hookenv.action_fail("Unable to refresh: {}".format(str(e))) |
103 | return |
104 | - hookenv.action_set(dict(result='Refreshed')) |
105 | + hookenv.action_set(dict(result="Refreshed")) |
106 | |
107 | |
108 | def main(argv): |
109 | + """Select the appropriate action.""" |
110 | action = os.path.basename(argv[0]) |
111 | params = hookenv.action_get() |
112 | try: |
113 | - if action == 'activate': |
114 | + if action == "activate": |
115 | activate(params) |
116 | - elif action == 'deactivate': |
117 | + elif action == "deactivate": |
118 | deactivate(params) |
119 | - elif action == 'refresh': |
120 | + elif action == "refresh": |
121 | refresh(params) |
122 | else: |
123 | - hookenv.action_fail('Action {} not implemented'.format(action)) |
124 | + hookenv.action_fail("Action {} not implemented".format(action)) |
125 | except Exception: |
126 | - hookenv.action_fail('Unhandled exception') |
127 | + hookenv.action_fail("Unhandled exception") |
128 | tb = traceback.format_exc() |
129 | hookenv.action_set(dict(traceback=tb)) |
130 | - hookenv.log('Unhandled exception in action {}'.format(action)) |
131 | + hookenv.log("Unhandled exception in action {}".format(action)) |
132 | print(tb) |
133 | |
134 | |
135 | -if __name__ == '__main__': |
136 | +if __name__ == "__main__": |
137 | main(sys.argv) |
138 | diff --git a/files/check_canonical-livepatch.py b/files/check_canonical-livepatch.py |
139 | index cafd472..f7ce127 100755 |
140 | --- a/files/check_canonical-livepatch.py |
141 | +++ b/files/check_canonical-livepatch.py |
142 | @@ -1,75 +1,78 @@ |
143 | #!/usr/bin/env python3 |
144 | +"""Check canonical-livepatch and alert.""" |
145 | |
146 | # Copyright (C) 2016 Canonical Ltd. |
147 | |
148 | import os |
149 | +from subprocess import call, check_output |
150 | + |
151 | import nagios_plugin3 |
152 | -from subprocess import check_output, call |
153 | + |
154 | from yaml import safe_load |
155 | |
156 | -supported_archs = ['x86_64'] |
157 | +supported_archs = ["x86_64"] |
158 | |
159 | |
160 | def check_snap_installed(): |
161 | - """Confirm the snap is installed, raise an error if not""" |
162 | - cmd = ['snap', 'list', 'canonical-livepatch'] |
163 | + """Confirm the snap is installed, raise an error if not.""" |
164 | + cmd = ["snap", "list", "canonical-livepatch"] |
165 | try: |
166 | - check_output(cmd).decode('UTF-8') |
167 | + check_output(cmd).decode("UTF-8") |
168 | except Exception: |
169 | - raise nagios_plugin3.CriticalError('canonical-livepatch snap is not installed') |
170 | + raise nagios_plugin3.CriticalError("canonical-livepatch snap is not installed") |
171 | |
172 | |
173 | def load_status(): |
174 | - """Load the cached status from disk, return it as a string""" |
175 | - livepatch_output_path = '/var/lib/nagios/canonical-livepatch-status.txt' |
176 | + """Load the cached status from disk, return it as a string.""" |
177 | + livepatch_output_path = "/var/lib/nagios/canonical-livepatch-status.txt" |
178 | |
179 | - with open(livepatch_output_path, 'r') as canonical_livepatch_log: |
180 | + with open(livepatch_output_path, "r") as canonical_livepatch_log: |
181 | livepatch_status = canonical_livepatch_log.read() |
182 | |
183 | return livepatch_status.strip() |
184 | |
185 | |
186 | def check_serious_errors(): |
187 | - """In serious error cases, the output will not be YAML""" |
188 | + """Check serious error cases, the output will not be YAML.""" |
189 | livepatch_status = load_status() |
190 | |
191 | # if it's empty, something's definitely wrong |
192 | - if livepatch_status == '': |
193 | - raise nagios_plugin3.CriticalError('No output from canonical-livepatch status') |
194 | + if livepatch_status == "": |
195 | + raise nagios_plugin3.CriticalError("No output from canonical-livepatch status") |
196 | |
197 | # in some cases, it's obviously not valid YAML |
198 | try: |
199 | livepatch_yaml = safe_load(livepatch_status) |
200 | except Exception: |
201 | - raise nagios_plugin3.CriticalError(livepatch_status.replace('\n', ' ')) |
202 | + raise nagios_plugin3.CriticalError(livepatch_status.replace("\n", " ")) |
203 | |
204 | # in other cases, it's less obvious until we try to parse |
205 | try: |
206 | - livepatch_yaml['status'] |
207 | + livepatch_yaml["status"] |
208 | except TypeError: |
209 | - raise nagios_plugin3.CriticalError(livepatch_status.replace('\n', ' ')) |
210 | + raise nagios_plugin3.CriticalError(livepatch_status.replace("\n", " ")) |
211 | |
212 | |
213 | def active_kernel_version(): |
214 | - """Return the active kernel version, from livepatch's perspective""" |
215 | + """Return the active kernel version, from livepatch's perspective.""" |
216 | livepatch_status = load_status() |
217 | status_yaml = safe_load(livepatch_status) |
218 | - for kernel in status_yaml.get('status'): |
219 | - if kernel.get('running') is True: |
220 | - return kernel.get('kernel') |
221 | + for kernel in status_yaml.get("status"): |
222 | + if kernel.get("running") is True: |
223 | + return kernel.get("kernel") |
224 | |
225 | |
226 | def check_status(): |
227 | - """Check the cached status, raise an error if we find any issues""" |
228 | + """Check the cached status, raise an error if we find any issues.""" |
229 | livepatch_status = load_status() |
230 | errors = [] |
231 | |
232 | status_yaml = safe_load(livepatch_status) |
233 | |
234 | - for kernel in status_yaml.get('status'): |
235 | - if kernel.get('running') is True: |
236 | - check_state = kernel.get('livepatch').get('checkState') |
237 | - patch_state = kernel.get('livepatch').get('patchState') |
238 | + for kernel in status_yaml.get("status"): |
239 | + if kernel.get("running") is True: |
240 | + check_state = kernel.get("livepatch").get("checkState") |
241 | + patch_state = kernel.get("livepatch").get("patchState") |
242 | |
243 | check_state_error = check_check_state(check_state) |
244 | patch_state_error = check_patch_state(patch_state) |
245 | @@ -80,66 +83,67 @@ def check_status(): |
246 | errors.append(patch_state_error) |
247 | |
248 | if errors: |
249 | - raise nagios_plugin3.CriticalError(' '.join(errors)) |
250 | + raise nagios_plugin3.CriticalError(" ".join(errors)) |
251 | |
252 | |
253 | def check_check_state(check_state): |
254 | - """Check for issues with checkState, including unexpected output""" |
255 | - if check_state in ['checked', 'needs-check']: |
256 | + """Check for issues with checkState, including unexpected output.""" |
257 | + if check_state in ["checked", "needs-check"]: |
258 | pass |
259 | - elif check_state == 'check-failed': |
260 | - return 'Livepatch failed to check the remote service for patches.' |
261 | + elif check_state == "check-failed": |
262 | + return "Livepatch failed to check the remote service for patches." |
263 | else: |
264 | - return 'Unknown check state: {}'.format(check_state) |
265 | + return "Unknown check state: {}".format(check_state) |
266 | |
267 | |
268 | def check_patch_state(patch_state): |
269 | - """Check for issues with patchState, including unexpected output""" |
270 | - if patch_state in ['applied', 'applying', 'nothing-to-apply']: |
271 | + """Check for issues with patchState, including unexpected output.""" |
272 | + if patch_state in ["applied", "applying", "nothing-to-apply"]: |
273 | pass |
274 | - elif patch_state == 'unapplied': |
275 | - return 'Livepatch has not applied the downloaded patches.' |
276 | - elif patch_state == 'applied-with-bug': |
277 | - return 'Livepatch has detected a kernel bug while applying patches.' |
278 | - elif patch_state == 'apply-failed': |
279 | - return 'Livepatch failed to apply patches.' |
280 | - elif patch_state == 'kernel-upgrade-required': |
281 | - return 'A kernel upgrade (and reboot) is required.' |
282 | + elif patch_state == "unapplied": |
283 | + return "Livepatch has not applied the downloaded patches." |
284 | + elif patch_state == "applied-with-bug": |
285 | + return "Livepatch has detected a kernel bug while applying patches." |
286 | + elif patch_state == "apply-failed": |
287 | + return "Livepatch failed to apply patches." |
288 | + elif patch_state == "kernel-upgrade-required": |
289 | + return "A kernel upgrade (and reboot) is required." |
290 | else: |
291 | - return 'Unknown patch state: {}'.format(patch_state) |
292 | + return "Unknown patch state: {}".format(patch_state) |
293 | |
294 | |
295 | def lsb_release(): |
296 | - """Return /etc/lsb-release in a dict""" |
297 | + """Return /etc/lsb-release in a dict.""" |
298 | d = {} |
299 | - with open('/etc/lsb-release', 'r') as lsb: |
300 | - for l in lsb: |
301 | - k, v = l.split('=') |
302 | + with open("/etc/lsb-release", "r") as lsb: |
303 | + for line in lsb: |
304 | + k, v = line.split("=") |
305 | d[k.strip()] = v.strip() |
306 | return d |
307 | |
308 | |
309 | def init_is_systemd(): |
310 | """Return True if the host system uses systemd, False otherwise.""" |
311 | - if lsb_release()['DISTRIB_CODENAME'] == 'trusty': |
312 | + if lsb_release()["DISTRIB_CODENAME"] == "trusty": |
313 | return False |
314 | - return os.path.isdir('/run/systemd/system') |
315 | + return os.path.isdir("/run/systemd/system") |
316 | |
317 | |
318 | def is_container(): |
319 | - """Determine whether unit is running in a container |
320 | + """Determine whether unit is running in a container. |
321 | |
322 | @return: boolean indicating if unit is in a container |
323 | """ |
324 | if init_is_systemd(): |
325 | # Detect using systemd-detect-virt |
326 | - return call(['systemd-detect-virt', '--container', '--quiet']) == 0 |
327 | + return call(["systemd-detect-virt", "--container", "--quiet"]) == 0 |
328 | else: |
329 | # Detect using upstart container file marker |
330 | - return os.path.exists('/run/container_type') |
331 | + return os.path.exists("/run/container_type") |
332 | |
333 | |
334 | def main(): |
335 | + """Run all the checks.""" |
336 | arch = os.uname()[4] |
337 | if arch not in supported_archs: |
338 | raise nagios_plugin3.CriticalError( |
339 | diff --git a/reactive/canonical_livepatch.py b/reactive/canonical_livepatch.py |
340 | index 25f1f3c..b3a0dcd 100644 |
341 | --- a/reactive/canonical_livepatch.py |
342 | +++ b/reactive/canonical_livepatch.py |
343 | @@ -1,264 +1,303 @@ |
344 | -from charms.layer import snap |
345 | -from charms.reactive import when, when_not, set_flag, clear_flag, hook |
346 | -from charms.reactive.flags import register_trigger |
347 | -from charmhelpers.core.host import write_file, is_container |
348 | -from charmhelpers.core import hookenv |
349 | -from charmhelpers.contrib.charmsupport import nrpe |
350 | +"""Charm hooks.""" |
351 | + |
352 | from distutils.version import LooseVersion |
353 | from os import environ, mkdir, path, uname |
354 | -from subprocess import check_call, check_output, CalledProcessError |
355 | +from subprocess import CalledProcessError, check_call, check_output |
356 | from time import sleep |
357 | + |
358 | +from charmhelpers.contrib.charmsupport import nrpe |
359 | +from charmhelpers.core import hookenv |
360 | +from charmhelpers.core.host import is_container, write_file |
361 | + |
362 | +from charms.layer import snap |
363 | +from charms.reactive import clear_flag, hook, set_flag, when, when_not |
364 | +from charms.reactive.flags import register_trigger |
365 | + |
366 | from yaml import safe_load |
367 | |
368 | |
369 | def file_to_units(local_path, unit_path): |
370 | - """ copy a file from the charm onto our unit(s) """ |
371 | - if local_path[-3:] == '.py': |
372 | + """Copy a file from the charm onto our unit(s).""" |
373 | + if local_path[-3:] == ".py": |
374 | perms = 0o755 |
375 | else: |
376 | perms = 0o644 |
377 | |
378 | - fh = open(local_path, 'r') |
379 | + fh = open(local_path, "r") |
380 | write_file( |
381 | - path=unit_path, |
382 | - content=fh.read().encode(), |
383 | - owner='root', |
384 | - perms=perms, |
385 | + path=unit_path, content=fh.read().encode(), owner="root", perms=perms, |
386 | ) |
387 | |
388 | |
389 | def wait_for_path(file_path, timeout=30): |
390 | - hookenv.log('Waiting for up to {}s for {}'.format(timeout, file_path)) |
391 | + """Wait until path exists or timeout.""" |
392 | + hookenv.log("Waiting for up to {}s for {}".format(timeout, file_path)) |
393 | seconds_waited = 0 |
394 | interval = 1 |
395 | while not (path.exists(file_path)): |
396 | - hookenv.log('{} does not exist - waiting {}s'.format(file_path, interval)) |
397 | + hookenv.log("{} does not exist - waiting {}s".format(file_path, interval)) |
398 | sleep(interval) |
399 | seconds_waited += interval |
400 | if seconds_waited >= timeout: |
401 | - hookenv.log('Giving up waiting for {}'.format(file_path), hookenv.ERROR) |
402 | + hookenv.log("Giving up waiting for {}".format(file_path), hookenv.ERROR) |
403 | return |
404 | return |
405 | |
406 | |
407 | def wait_for_livepatch(): |
408 | - wait_for_path('/var/snap/canonical-livepatch/current/livepatchd-priv.sock') |
409 | + """Wait until canonical-livepatch socket is created.""" |
410 | + wait_for_path("/var/snap/canonical-livepatch/current/livepatchd-priv.sock") |
411 | |
412 | |
413 | def unit_update(status=None, message=None): |
414 | + """Update status message.""" |
415 | if status and message: |
416 | hookenv.status_set(status, message) |
417 | else: |
418 | patch_details = get_patch_details() |
419 | - hookenv.status_set('active', 'Running kernel {}, patchState: {}'.format(*patch_details)) |
420 | + hookenv.status_set( |
421 | + "active", "Running kernel {}, patchState: {}".format(*patch_details) |
422 | + ) |
423 | |
424 | |
425 | def get_livepatch_status(): |
426 | - cmd = ['/snap/bin/canonical-livepatch', 'status', '--format', 'yaml'] |
427 | - livepatch_status = '' |
428 | + """Get livepatch status.""" |
429 | + cmd = ["/snap/bin/canonical-livepatch", "status", "--format", "yaml"] |
430 | + livepatch_status = "" |
431 | try: |
432 | livepatch_status = check_output(cmd, universal_newlines=True) |
433 | except FileNotFoundError: |
434 | # If the snap's not installed yet, don't crash out |
435 | pass |
436 | except CalledProcessError: |
437 | - # If the service hasn't been enabled yet, we'll get a process error - this is fine |
438 | + # If the service hasn't been enabled yet, |
439 | + # we'll get a process error - this is fine |
440 | pass |
441 | return livepatch_status |
442 | |
443 | |
444 | def write_status_to_disk(): |
445 | + """Write livepatch status to disk.""" |
446 | # In a race with nrpe, create /var/lib/nagios if nrpe didn't do it yet |
447 | - if not path.exists('/var/lib/nagios'): |
448 | - mkdir('/var/lib/nagios') |
449 | + if not path.exists("/var/lib/nagios"): |
450 | + mkdir("/var/lib/nagios") |
451 | |
452 | current_status = get_livepatch_status() |
453 | perms = 0o644 |
454 | write_file( |
455 | - path='/var/lib/nagios/canonical-livepatch-status.txt', |
456 | + path="/var/lib/nagios/canonical-livepatch-status.txt", |
457 | content=current_status, |
458 | - owner='root', |
459 | + owner="root", |
460 | perms=perms, |
461 | ) |
462 | |
463 | |
464 | def get_patch_details(): |
465 | - kernel = 'unknown' |
466 | - patch_state = 'unknown' |
467 | + """Get patch details. |
468 | + |
469 | + :return: a tuple containing kernel version and patch state. |
470 | + """ |
471 | + kernel = "unknown" |
472 | + patch_state = "unknown" |
473 | raw_status = get_livepatch_status() |
474 | |
475 | # status will usually pass YAML (but not always!) |
476 | try: |
477 | status = safe_load(raw_status) |
478 | except Exception: |
479 | - hookenv.log('Unable to parse status yaml', hookenv.ERROR) |
480 | + hookenv.log("Unable to parse status yaml", hookenv.ERROR) |
481 | return kernel, raw_status |
482 | |
483 | # even if we got YAML, be paranoid |
484 | try: |
485 | - for k in status['status']: |
486 | - if k['running'] is True: |
487 | - kernel = k['kernel'] |
488 | - patch_state = k['livepatch']['patchState'] |
489 | + for k in status["status"]: |
490 | + if k["running"] is True: |
491 | + kernel = k["kernel"] |
492 | + patch_state = k["livepatch"]["patchState"] |
493 | except Exception: |
494 | - hookenv.log('Unable to find patching details in status yaml', hookenv.ERROR) |
495 | + hookenv.log("Unable to find patching details in status yaml", hookenv.ERROR) |
496 | return kernel, patch_state |
497 | |
498 | return kernel, patch_state |
499 | |
500 | |
501 | def configure_proxies(proxy=None): |
502 | - for proto in ('http-proxy', 'https-proxy'): |
503 | + """Configure proxies.""" |
504 | + for proto in ("http-proxy", "https-proxy"): |
505 | if proxy is None: |
506 | - proxy = '' |
507 | - cmd = ['/snap/bin/canonical-livepatch', 'config', '{}={}'.format(proto, proxy)] |
508 | - hookenv.log('Configuring {} as {}'.format(proto, proxy)) |
509 | + proxy = "" |
510 | + cmd = ["/snap/bin/canonical-livepatch", "config", "{}={}".format(proto, proxy)] |
511 | + hookenv.log("Configuring {} as {}".format(proto, proxy)) |
512 | try: |
513 | check_call(cmd, universal_newlines=True) |
514 | except CalledProcessError: |
515 | - hookenv.log('Failed to set proxy', hookenv.ERROR) |
516 | + hookenv.log("Failed to set proxy", hookenv.ERROR) |
517 | |
518 | |
519 | def activate_livepatch(): |
520 | - unit_update('maintenance', 'Updating API key') |
521 | + """Activate livepatch.""" |
522 | + unit_update("maintenance", "Updating API key") |
523 | config = hookenv.config() |
524 | |
525 | - livepatch_key = config.get('livepatch_key') |
526 | + livepatch_key = config.get("livepatch_key") |
527 | if livepatch_key: |
528 | # disable prior to enabling to work around LP#1628823 |
529 | - cmd = ['/snap/bin/canonical-livepatch', 'disable'] |
530 | - hookenv.log('Disabling canonical-livepatch ahead of key change to work around LP#1628823') |
531 | + cmd = ["/snap/bin/canonical-livepatch", "disable"] |
532 | + hookenv.log( |
533 | + "Disabling canonical-livepatch ahead " |
534 | + "of key change to work around LP#1628823" |
535 | + ) |
536 | try: |
537 | check_output(cmd, universal_newlines=True) |
538 | except CalledProcessError as e: |
539 | - hookenv.log('Unable to deactivate: {}'.format(str(e)), hookenv.ERROR) |
540 | - |
541 | - cmd = ['/snap/bin/canonical-livepatch', 'enable', '{}'.format(livepatch_key.strip())] |
542 | - hookenv.log('Activating canonical-livepatch with your key') |
543 | + hookenv.log("Unable to deactivate: {}".format(str(e)), hookenv.ERROR) |
544 | + |
545 | + cmd = [ |
546 | + "/snap/bin/canonical-livepatch", |
547 | + "enable", |
548 | + "{}".format(livepatch_key.strip()), |
549 | + ] |
550 | + hookenv.log("Activating canonical-livepatch with your key") |
551 | try: |
552 | check_output(cmd, universal_newlines=True) |
553 | except CalledProcessError as e: |
554 | - hookenv.log('Unable to activate: {}'.format(str(e)), hookenv.ERROR) |
555 | - clear_flag('canonical-livepatch.active') |
556 | - unit_update('blocked', 'Activation failed') |
557 | + hookenv.log("Unable to activate: {}".format(str(e)), hookenv.ERROR) |
558 | + clear_flag("canonical-livepatch.active") |
559 | + unit_update("blocked", "Activation failed") |
560 | else: |
561 | - set_flag('canonical-livepatch.active') |
562 | + set_flag("canonical-livepatch.active") |
563 | unit_update() |
564 | else: |
565 | - hookenv.log('Unable to activate canonical-livepatch as no key has been set', hookenv.ERROR) |
566 | - cmd = ['/snap/bin/canonical-livepatch', 'disable'] |
567 | + hookenv.log( |
568 | + "Unable to activate canonical-livepatch as no key has been set", |
569 | + hookenv.ERROR, |
570 | + ) |
571 | + cmd = ["/snap/bin/canonical-livepatch", "disable"] |
572 | try: |
573 | check_output(cmd, universal_newlines=True) |
574 | except CalledProcessError as e: |
575 | - hookenv.log('Unable to deactivate: {}'.format(str(e)), hookenv.ERROR) |
576 | - clear_flag('canonical-livepatch.active') |
577 | - unit_update('blocked', 'Service disabled, please set livepatch_key to activate') |
578 | + hookenv.log("Unable to deactivate: {}".format(str(e)), hookenv.ERROR) |
579 | + clear_flag("canonical-livepatch.active") |
580 | + unit_update("blocked", "Service disabled, please set livepatch_key to activate") |
581 | |
582 | |
583 | -register_trigger(when='config.changed.livepatch_proxy', clear_flag='livepatch-proxy.configured') |
584 | +register_trigger( |
585 | + when="config.changed.livepatch_proxy", clear_flag="livepatch-proxy.configured" |
586 | +) |
587 | |
588 | |
589 | -@when_not('canonical-livepatch.supported') |
590 | +@when_not("canonical-livepatch.supported") |
591 | def livepatch_supported(): |
592 | + """Check if livepatch is supported and set flag.""" |
593 | arch = uname()[4] |
594 | - supported_archs = ['x86_64', ] |
595 | + supported_archs = ["x86_64"] |
596 | + |
597 | if arch not in supported_archs: |
598 | - hookenv.log('Livepatch does not currently support {} architecture'.format(arch)) |
599 | - unit_update('blocked', 'Architecture {} is not supported by livepatch'.format(arch)) |
600 | + hookenv.log("Livepatch does not currently support {} architecture".format(arch)) |
601 | + unit_update( |
602 | + "blocked", "Architecture {} is not supported by livepatch".format(arch) |
603 | + ) |
604 | elif is_container() and not hookenv.config()["enable_container"]: |
605 | - hookenv.log('OS container detected, livepatch is not needed') |
606 | - unit_update('blocked', 'Livepatch is not needed in OS containers') |
607 | + hookenv.log("OS container detected, livepatch is not needed") |
608 | + unit_update("blocked", "Livepatch is not needed in OS containers") |
609 | else: |
610 | - set_flag('canonical-livepatch.supported') |
611 | + set_flag("canonical-livepatch.supported") |
612 | |
613 | |
614 | -@when('canonical-livepatch.supported') |
615 | -@when_not('snap.installed.canonical-livepatch') |
616 | +@when("canonical-livepatch.supported") |
617 | +@when_not("snap.installed.canonical-livepatch") |
618 | def install_livepatch(): |
619 | + """Install livepatch.""" |
620 | config = hookenv.config() |
621 | - snap_channel = config.get('snap_channel') |
622 | - snap.install('canonical-livepatch', **{'channel': snap_channel, }) |
623 | + snap_channel = config.get("snap_channel") |
624 | + snap.install("canonical-livepatch", **{"channel": snap_channel}) |
625 | |
626 | |
627 | -@when('snap.installed.canonical-livepatch') |
628 | -@when_not('livepatch-proxy.configured') |
629 | +@when("snap.installed.canonical-livepatch") |
630 | +@when_not("livepatch-proxy.configured") |
631 | def proxy_settings(): |
632 | + """Get proxy settings and configure it.""" |
633 | # Configure proxies early, if required - LP#1761661 |
634 | |
635 | preferred_proxy = None # default to None |
636 | |
637 | for key, value in environ.items(): |
638 | # use environment's https_proxy or http_proxy if either are set |
639 | - if key == 'https_proxy': |
640 | + if key == "https_proxy": |
641 | preferred_proxy = value |
642 | - elif key == 'http_proxy': |
643 | + elif key == "http_proxy": |
644 | preferred_proxy = value |
645 | |
646 | # but if we have a charm configured proxy, prefer that |
647 | config = hookenv.config() |
648 | - charm_config_proxy = config.get('livepatch_proxy') |
649 | + charm_config_proxy = config.get("livepatch_proxy") |
650 | if charm_config_proxy: |
651 | preferred_proxy = charm_config_proxy |
652 | |
653 | # whatever we prefer (or None), configure it! |
654 | configure_proxies(preferred_proxy) |
655 | |
656 | - set_flag('livepatch-proxy.configured') |
657 | + set_flag("livepatch-proxy.configured") |
658 | |
659 | |
660 | -@when('snap.installed.canonical-livepatch') |
661 | -@when('livepatch-proxy.configured') |
662 | -@when_not('canonical-livepatch.connected') |
663 | +@when("snap.installed.canonical-livepatch") |
664 | +@when("livepatch-proxy.configured") |
665 | +@when_not("canonical-livepatch.connected") |
666 | def canonical_livepatch_connect(): |
667 | + """Check if livepatch socket is created and update unit.""" |
668 | # So if we've just installed snapd on a trusty system, we will not be on |
669 | # the HWE kernel yet and unfortunately need to reboot first! |
670 | current = LooseVersion(uname()[2]) |
671 | - required = LooseVersion('4.4') |
672 | - uptrack_path = '/usr/sbin/uptrack-upgrade' |
673 | + required = LooseVersion("4.4") |
674 | + uptrack_path = "/usr/sbin/uptrack-upgrade" |
675 | if path.exists(uptrack_path): |
676 | - hookenv.log('Ksplice/Uptrack detected, please remove it and reboot') |
677 | - unit_update('blocked', 'Remove ksplice and then reboot') |
678 | + hookenv.log("Ksplice/Uptrack detected, please remove it and reboot") |
679 | + unit_update("blocked", "Remove ksplice and then reboot") |
680 | elif current < required: |
681 | - hookenv.log('Reboot required, kernel {} is too old'.format(current)) |
682 | - unit_update('blocked', 'A reboot is required') |
683 | + hookenv.log("Reboot required, kernel {} is too old".format(current)) |
684 | + unit_update("blocked", "A reboot is required") |
685 | else: |
686 | - unit_update('maintenance', 'Connecting to the livepatch service') |
687 | + unit_update("maintenance", "Connecting to the livepatch service") |
688 | # Make sure the service is ready for us |
689 | wait_for_livepatch() |
690 | - set_flag('canonical-livepatch.connected') |
691 | - unit_update('blocked', 'Service disabled, please set livepatch_key to activate') |
692 | + set_flag("canonical-livepatch.connected") |
693 | + unit_update("blocked", "Service disabled, please set livepatch_key to activate") |
694 | |
695 | |
696 | -@when('canonical-livepatch.connected') |
697 | -@when_not('config.changed.livepatch_key', 'canonical-livepatch.active') |
698 | +@when("canonical-livepatch.connected") |
699 | +@when_not("config.changed.livepatch_key", "canonical-livepatch.active") |
700 | def init_key(): |
701 | + """Activate livepatch.""" |
702 | # If deployed under Trusty before rebooting into the HWE kernel |
703 | # the config-changed hook won't fire post-reboot as the state |
704 | # isn't tracked, but we didn't initialise yet! So, handle it here |
705 | activate_livepatch() |
706 | |
707 | |
708 | -@when('canonical-livepatch.connected', 'config.changed.livepatch_key') |
709 | +@when("canonical-livepatch.connected", "config.changed.livepatch_key") |
710 | def update_key(): |
711 | + """Activate livepatch and write status to disk.""" |
712 | # handle regular config-changed hooks for the livepatch key |
713 | activate_livepatch() |
714 | write_status_to_disk() |
715 | |
716 | |
717 | -@when('snap.installed.canonical-livepatch', 'config.changed.snap_channel') |
718 | +@when("snap.installed.canonical-livepatch", "config.changed.snap_channel") |
719 | def change_channel(): |
720 | + """Update snap channel.""" |
721 | config = hookenv.config() |
722 | - snap_channel = config.get('snap_channel') |
723 | + snap_channel = config.get("snap_channel") |
724 | # refresh to the given channel |
725 | - snap.refresh('canonical-livepatch', **{'channel': snap_channel, }) |
726 | + snap.refresh("canonical-livepatch", **{"channel": snap_channel}) |
727 | |
728 | |
729 | # Set up Nagios checks when the nrpe-external-master subordinate is related |
730 | -@when('nrpe-external-master.available') |
731 | -@when_not('canonical-livepatch.nagios-setup.complete') |
732 | -@when('canonical-livepatch.supported') |
733 | +@when("nrpe-external-master.available") |
734 | +@when_not("canonical-livepatch.nagios-setup.complete") |
735 | +@when("canonical-livepatch.supported") |
736 | def configure_nagios(nagios): |
737 | - if hookenv.hook_name() == 'update-status': |
738 | + """Configure nagios checks.""" |
739 | + if hookenv.hook_name() == "update-status": |
740 | return |
741 | |
742 | # Ask charmhelpers.contrib.charmsupport's nrpe to work out our hostname |
743 | @@ -267,44 +306,43 @@ def configure_nagios(nagios): |
744 | |
745 | # install nagios support files |
746 | file_to_units( |
747 | - 'files/check_canonical-livepatch.cron', |
748 | - '/etc/cron.d/check_canonical-livepatch', |
749 | + "files/check_canonical-livepatch.cron", "/etc/cron.d/check_canonical-livepatch", |
750 | ) |
751 | file_to_units( |
752 | - 'files/check_canonical-livepatch.py', |
753 | - '/usr/lib/nagios/plugins/check_canonical-livepatch.py', |
754 | + "files/check_canonical-livepatch.py", |
755 | + "/usr/lib/nagios/plugins/check_canonical-livepatch.py", |
756 | ) |
757 | |
758 | # write current status to disk to satisfy the nagios check |
759 | write_status_to_disk() |
760 | |
761 | # remove check from previous release with poorly formed name |
762 | - nrpe_setup.remove_check( |
763 | - shortname='check_canonical-livepatch' |
764 | - ) |
765 | + nrpe_setup.remove_check(shortname="check_canonical-livepatch") |
766 | |
767 | # use charmhelpers to create the check |
768 | nrpe_setup.add_check( |
769 | - 'canonical-livepatch', |
770 | - 'Verify canonical-livepatch is working', |
771 | - '/usr/lib/nagios/plugins/check_canonical-livepatch.py' |
772 | + "canonical-livepatch", |
773 | + "Verify canonical-livepatch is working", |
774 | + "/usr/lib/nagios/plugins/check_canonical-livepatch.py", |
775 | ) |
776 | |
777 | nrpe_setup.write() |
778 | |
779 | # Remove obsolete state from older charm, if present |
780 | - clear_flag('canonical-livepatch.nagios-configured') |
781 | + clear_flag("canonical-livepatch.nagios-configured") |
782 | # Set our preferred state |
783 | - set_flag('canonical-livepatch.nagios-setup.complete') |
784 | + set_flag("canonical-livepatch.nagios-setup.complete") |
785 | |
786 | |
787 | -@when('snap.installed.canonical-livepatch', 'canonical-livepatch.active') |
788 | +@when("snap.installed.canonical-livepatch", "canonical-livepatch.active") |
789 | def update_kernel_version(): |
790 | + """Update kernel version.""" |
791 | unit_update() |
792 | |
793 | |
794 | # This is triggered on any config-changed, and after an upgrade-charm - you |
795 | # don't get the latter with @when('config.changed') |
796 | -@hook('config-changed') |
797 | +@hook("config-changed") |
798 | def set_nrpe_flag(): |
799 | - clear_flag('canonical-livepatch.nagios-setup.complete') |
800 | + """Run config changed.""" |
801 | + clear_flag("canonical-livepatch.nagios-setup.complete") |
802 | diff --git a/tests/functional/tests/test_canonical_livepatch.py b/tests/functional/tests/test_canonical_livepatch.py |
803 | index 684cb2a..e87e876 100644 |
804 | --- a/tests/functional/tests/test_canonical_livepatch.py |
805 | +++ b/tests/functional/tests/test_canonical_livepatch.py |
806 | @@ -1,22 +1,24 @@ |
807 | +"""Zaza functional tests.""" |
808 | +import logging |
809 | import unittest |
810 | + |
811 | import yaml |
812 | + |
813 | import zaza.model as model |
814 | -import logging |
815 | |
816 | |
817 | class TestBase(unittest.TestCase): |
818 | - """Base Class for charm functional tests""" |
819 | + """Base Class for charm functional tests.""" |
820 | + |
821 | @classmethod |
822 | def setUpClass(cls): |
823 | - """ Run setup for tests. """ |
824 | + """Run setup for tests.""" |
825 | cls.model_name = model.get_juju_model() |
826 | cls.application_name = "canonical-livepatch" |
827 | cls.lead_unit_name = model.get_lead_unit_name( |
828 | cls.application_name, model_name=cls.model_name |
829 | ) |
830 | - cls.units = model.get_units( |
831 | - cls.application_name, model_name=cls.model_name |
832 | - ) |
833 | + cls.units = model.get_units(cls.application_name, model_name=cls.model_name) |
834 | |
835 | cls.lead_unit = cls.units[0] |
836 | |
837 | @@ -24,9 +26,13 @@ class TestBase(unittest.TestCase): |
838 | |
839 | |
840 | class TestCanonicalLivepatch(TestBase): |
841 | + """Class for charm functional tests.""" |
842 | + |
843 | def test_01_install(self): |
844 | - if self.lead_unit.workload_status_message == ("Livepatch " |
845 | - "is not needed in OS containers"): |
846 | + """Verify that the snap is installed.""" |
847 | + if self.lead_unit.workload_status_message == ( |
848 | + "Livepatch is not needed in OS containers" |
849 | + ): |
850 | model.set_application_config( |
851 | self.application_name, {"enable_container": "true"} |
852 | ) |
853 | @@ -37,6 +43,7 @@ class TestCanonicalLivepatch(TestBase): |
854 | self.assertEqual(code, "0") |
855 | |
856 | def test_02_status(self): |
857 | + """Verify canonical-livepatch status.""" |
858 | # run a status check - we expect this to return 1 due to no access key |
859 | cmd = "sudo canonical-livepatch status" |
860 | result = model.run_on_unit(self.lead_unit_name, cmd) |
861 | @@ -44,10 +51,11 @@ class TestCanonicalLivepatch(TestBase): |
862 | self.assertEqual(code, "1") |
863 | |
864 | def test_03_nagios_init(self): |
865 | + """Verify nagios plugins are rendered.""" |
866 | for path in [ |
867 | - '/etc/cron.d/check_canonical-livepatch', |
868 | - '/usr/lib/nagios/plugins/check_canonical-livepatch.py', |
869 | - '/var/lib/nagios/canonical-livepatch-status.txt' |
870 | + "/etc/cron.d/check_canonical-livepatch", |
871 | + "/usr/lib/nagios/plugins/check_canonical-livepatch.py", |
872 | + "/var/lib/nagios/canonical-livepatch-status.txt", |
873 | ]: |
874 | logging.info("Checking that file '{}' exists".format(path)) |
875 | cmd = "stat {}".format(path) |
876 | @@ -56,20 +64,24 @@ class TestCanonicalLivepatch(TestBase): |
877 | self.assertEqual(code, "0") |
878 | |
879 | def test_04_nagios_context_update(self): |
880 | + """Update nagios context and verify.""" |
881 | test_context_name = "new_context" |
882 | model.set_application_config( |
883 | self.application_name, {"nagios_context": test_context_name} |
884 | ) |
885 | model.block_until_all_units_idle() |
886 | |
887 | - cmd = "grep {} /var/lib/nagios/export/" \ |
888 | - "service__*_check_canonical-livepatch.cfg".format(test_context_name) |
889 | + cmd = ( |
890 | + "grep {} /var/lib/nagios/export/" |
891 | + "service__*_check_canonical-livepatch.cfg".format(test_context_name) |
892 | + ) |
893 | |
894 | result = model.run_on_unit(self.lead_unit_name, cmd) |
895 | code = result.get("Code") |
896 | self.assertEqual(code, "0") |
897 | |
898 | def test_05_channel_update(self): |
899 | + """Update snap channel and verify.""" |
900 | # verify the current channel |
901 | cmd = "snap info canonical-livepatch" |
902 | result = model.run_on_unit(self.lead_unit_name, cmd) |
903 | @@ -83,9 +95,7 @@ class TestCanonicalLivepatch(TestBase): |
904 | self.assertIn("stable", channel) |
905 | |
906 | # change channel to 'beta' |
907 | - model.set_application_config( |
908 | - self.application_name, {"snap_channel": "beta"} |
909 | - ) |
910 | + model.set_application_config(self.application_name, {"snap_channel": "beta"}) |
911 | model.block_until_all_units_idle() |
912 | |
913 | # verify the current channel |
914 | @@ -101,14 +111,17 @@ class TestCanonicalLivepatch(TestBase): |
915 | self.assertIn("beta", channel) |
916 | |
917 | def test_06_nagios_servicegroup_update(self): |
918 | + """Update nagios service group and verify.""" |
919 | test_servicegroup_name = "new_servicegroup" |
920 | model.set_application_config( |
921 | self.application_name, {"nagios_servicegroups": test_servicegroup_name} |
922 | ) |
923 | model.block_until_all_units_idle() |
924 | |
925 | - cmd = "grep {} /var/lib/nagios/export/" \ |
926 | - "service__*_check_canonical-livepatch.cfg".format(test_servicegroup_name) |
927 | + cmd = ( |
928 | + "grep {} /var/lib/nagios/export/" |
929 | + "service__*_check_canonical-livepatch.cfg".format(test_servicegroup_name) |
930 | + ) |
931 | |
932 | result = model.run_on_unit(self.lead_unit_name, cmd) |
933 | code = result.get("Code") |
This merge proposal is being monitored by mergebot. Change the status to Approved to merge.