Merge ~cjwatson/launchpad-layers:cron-support into launchpad-layers:main

Proposed by Colin Watson
Status: Merged
Merged at revision: ead45e3e5f7bbf21e81de102ffee6f98cbbfd317
Proposed branch: ~cjwatson/launchpad-layers:cron-support
Merge into: launchpad-layers:main
Diff against target: 104 lines (+60/-0)
2 files modified
launchpad-base/config.yaml (+4/-0)
launchpad-base/lib/charms/launchpad/base.py (+56/-0)
Reviewer Review Type Date Requested Status
Launchpad code reviewers Pending
Review via email: mp+439398@code.launchpad.net

Commit message

Support crontab handling

Description of the change

Add a `configure_cron` function that sets up a crontab from a template.

Launchpad's crontabs generally include a call to `oops-datedir2amqp` to catch up with publishing OOPSes that were temporarily spooled to disk. This needs a bit of extra support in building the config dictionary, since it needs RabbitMQ credentials in a different form.

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
1diff --git a/launchpad-base/config.yaml b/launchpad-base/config.yaml
2index 868c93b..3fa13a5 100644
3--- a/launchpad-base/config.yaml
4+++ b/launchpad-base/config.yaml
5@@ -43,6 +43,10 @@ options:
6 type: string
7 description: URL of file used to control whether cron scripts run.
8 default: "file:cronscripts.ini"
9+ cron_mailto:
10+ type: string
11+ description: Email address for output from cron jobs.
12+ default: "error-reports@launchpad.test"
13 databases:
14 type: string
15 description: >
16diff --git a/launchpad-base/lib/charms/launchpad/base.py b/launchpad-base/lib/charms/launchpad/base.py
17index 2a182fe..1fb2815 100644
18--- a/launchpad-base/lib/charms/launchpad/base.py
19+++ b/launchpad-base/lib/charms/launchpad/base.py
20@@ -5,10 +5,13 @@ import grp
21 import os.path
22 import pwd
23 import re
24+import subprocess
25 from dataclasses import dataclass
26 from email.utils import parseaddr
27+from urllib.parse import urlparse
28
29 from charmhelpers.core import hookenv, host, templating
30+from charms.reactive import endpoint_from_flag
31 from ols import base
32 from psycopg2.extensions import make_dsn, parse_dsn
33
34@@ -36,6 +39,15 @@ def ensure_lp_directories():
35 host.mkdir(home_dir(), owner=base.user(), group=base.user(), perms=0o755)
36
37
38+def _get_first_rabbitmq_hostname(rabbitmq):
39+ for conversation in rabbitmq.conversations():
40+ for relation_id in conversation.relation_ids:
41+ for unit in hookenv.related_units(relation_id):
42+ return hookenv.relation_get(
43+ "private-address", unit, relation_id
44+ )
45+
46+
47 def get_service_config():
48 config = dict(hookenv.config())
49 config.update(
50@@ -51,6 +63,34 @@ def get_service_config():
51 "var_dir": var_dir(),
52 }
53 )
54+
55+ # oops-datedir2amqp is used in crontabs and needs broken-out RabbitMQ
56+ # credentials. This isn't ideal because it doesn't support high
57+ # availability, but we can tolerate that for fallback OOPS publishing
58+ # for now.
59+ if config["rabbitmq_broker_urls"]:
60+ rabbitmq_url = urlparse(config["rabbitmq_broker_urls"].split()[0])
61+ config.update(
62+ {
63+ "rabbitmq_host": rabbitmq_url.hostname,
64+ "rabbitmq_password": rabbitmq_url.password,
65+ "rabbitmq_username": rabbitmq_url.username,
66+ "rabbitmq_vhost": rabbitmq_url.path.lstrip("/"),
67+ }
68+ )
69+ else:
70+ rabbitmq = endpoint_from_flag("rabbitmq.available")
71+ hostname = _get_first_rabbitmq_hostname(rabbitmq)
72+ if hostname is not None:
73+ config.update(
74+ {
75+ "rabbitmq_host": hostname,
76+ "rabbitmq_password": rabbitmq.password(),
77+ "rabbitmq_username": rabbitmq.username(),
78+ "rabbitmq_vhost": rabbitmq.vhost(),
79+ }
80+ )
81+
82 return config
83
84
85@@ -192,3 +232,19 @@ def strip_dsn_authentication(dsn):
86 parsed_dsn = parse_dsn(dsn)
87 parsed_dsn.pop("password", None)
88 return make_dsn(**parsed_dsn)
89+
90+
91+def configure_cron(config, template):
92+ hookenv.log("Writing crontab.")
93+ crontab_path = os.path.join(home_dir(), "crontab")
94+ templating.render(
95+ template,
96+ crontab_path,
97+ config,
98+ owner=base.user(),
99+ group=base.user(),
100+ perms=0o600,
101+ )
102+ subprocess.run(
103+ ["sudo", "-H", "-u", base.user(), "crontab", crontab_path], check=True
104+ )

Subscribers

People subscribed via source and target branches