← Back to team overview

launchpad-reviewers team mailing list archive

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

 

Colin Watson has proposed merging ~cjwatson/launchpad-layers:cron-support into launchpad-layers:main.

Commit message:
Support crontab handling

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad-layers/+git/launchpad-layers/+merge/439398

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.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad-layers:cron-support into launchpad-layers:main.
diff --git a/launchpad-base/config.yaml b/launchpad-base/config.yaml
index 868c93b..3fa13a5 100644
--- a/launchpad-base/config.yaml
+++ b/launchpad-base/config.yaml
@@ -43,6 +43,10 @@ options:
     type: string
     description: URL of file used to control whether cron scripts run.
     default: "file:cronscripts.ini"
+  cron_mailto:
+    type: string
+    description: Email address for output from cron jobs.
+    default: "error-reports@xxxxxxxxxxxxxx"
   databases:
     type: string
     description: >
diff --git a/launchpad-base/lib/charms/launchpad/base.py b/launchpad-base/lib/charms/launchpad/base.py
index 2a182fe..1fb2815 100644
--- a/launchpad-base/lib/charms/launchpad/base.py
+++ b/launchpad-base/lib/charms/launchpad/base.py
@@ -5,10 +5,13 @@ import grp
 import os.path
 import pwd
 import re
+import subprocess
 from dataclasses import dataclass
 from email.utils import parseaddr
+from urllib.parse import urlparse
 
 from charmhelpers.core import hookenv, host, templating
+from charms.reactive import endpoint_from_flag
 from ols import base
 from psycopg2.extensions import make_dsn, parse_dsn
 
@@ -36,6 +39,15 @@ def ensure_lp_directories():
     host.mkdir(home_dir(), owner=base.user(), group=base.user(), perms=0o755)
 
 
+def _get_first_rabbitmq_hostname(rabbitmq):
+    for conversation in rabbitmq.conversations():
+        for relation_id in conversation.relation_ids:
+            for unit in hookenv.related_units(relation_id):
+                return hookenv.relation_get(
+                    "private-address", unit, relation_id
+                )
+
+
 def get_service_config():
     config = dict(hookenv.config())
     config.update(
@@ -51,6 +63,34 @@ def get_service_config():
             "var_dir": var_dir(),
         }
     )
+
+    # oops-datedir2amqp is used in crontabs and needs broken-out RabbitMQ
+    # credentials.  This isn't ideal because it doesn't support high
+    # availability, but we can tolerate that for fallback OOPS publishing
+    # for now.
+    if config["rabbitmq_broker_urls"]:
+        rabbitmq_url = urlparse(config["rabbitmq_broker_urls"].split()[0])
+        config.update(
+            {
+                "rabbitmq_host": rabbitmq_url.hostname,
+                "rabbitmq_password": rabbitmq_url.password,
+                "rabbitmq_username": rabbitmq_url.username,
+                "rabbitmq_vhost": rabbitmq_url.path.lstrip("/"),
+            }
+        )
+    else:
+        rabbitmq = endpoint_from_flag("rabbitmq.available")
+        hostname = _get_first_rabbitmq_hostname(rabbitmq)
+        if hostname is not None:
+            config.update(
+                {
+                    "rabbitmq_host": hostname,
+                    "rabbitmq_password": rabbitmq.password(),
+                    "rabbitmq_username": rabbitmq.username(),
+                    "rabbitmq_vhost": rabbitmq.vhost(),
+                }
+            )
+
     return config
 
 
@@ -192,3 +232,19 @@ def strip_dsn_authentication(dsn):
     parsed_dsn = parse_dsn(dsn)
     parsed_dsn.pop("password", None)
     return make_dsn(**parsed_dsn)
+
+
+def configure_cron(config, template):
+    hookenv.log("Writing crontab.")
+    crontab_path = os.path.join(home_dir(), "crontab")
+    templating.render(
+        template,
+        crontab_path,
+        config,
+        owner=base.user(),
+        group=base.user(),
+        perms=0o600,
+    )
+    subprocess.run(
+        ["sudo", "-H", "-u", base.user(), "crontab", crontab_path], check=True
+    )

Follow ups