launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #30602
[Merge] ~cjwatson/launchpad:charm-loggerhead into launchpad:master
Colin Watson has proposed merging ~cjwatson/launchpad:charm-loggerhead into launchpad:master.
Commit message:
charm: Add launchpad-loggerhead
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/453470
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:charm-loggerhead into launchpad:master.
diff --git a/charm/launchpad-loggerhead/README.md b/charm/launchpad-loggerhead/README.md
new file mode 100644
index 0000000..92efbe0
--- /dev/null
+++ b/charm/launchpad-loggerhead/README.md
@@ -0,0 +1,7 @@
+# Launchpad Bazaar/Breezy code browsing server
+
+This charm runs a code browsing server for Bazaar/Breezy branches.
+
+You will need the following relations:
+
+ juju relate launchpad-loggerhead rabbitmq-server
diff --git a/charm/launchpad-loggerhead/charmcraft.yaml b/charm/launchpad-loggerhead/charmcraft.yaml
new file mode 100644
index 0000000..71dcf7f
--- /dev/null
+++ b/charm/launchpad-loggerhead/charmcraft.yaml
@@ -0,0 +1,75 @@
+type: charm
+bases:
+ - build-on:
+ - name: ubuntu
+ channel: "20.04"
+ architectures: [amd64]
+ run-on:
+ - name: ubuntu
+ channel: "20.04"
+ architectures: [amd64]
+parts:
+ charm-wheels:
+ source: https://git.launchpad.net/~ubuntuone-hackers/ols-charm-deps/+git/wheels
+ source-commit: "42c89d9c66dbe137139b047fd54aed49b66d1a5e"
+ source-submodules: []
+ source-type: git
+ plugin: dump
+ organize:
+ "*": charm-wheels/
+ prime:
+ - "-charm-wheels"
+ ols-layers:
+ source: https://git.launchpad.net/ols-charm-deps
+ source-commit: "9c59a9804f1f40e2a74be7dac9bf18a655a7864f"
+ source-submodules: []
+ source-type: git
+ plugin: dump
+ organize:
+ "*": layers/
+ stage:
+ - layers
+ prime:
+ - "-layers"
+ launchpad-layers:
+ after:
+ - ols-layers
+ source: https://git.launchpad.net/launchpad-layers
+ source-commit: "58edb3e5a88794c3baa2274a94e21d3a298a6c79"
+ source-submodules: []
+ source-type: git
+ plugin: dump
+ organize:
+ launchpad-base: layers/layer/launchpad-base
+ launchpad-payload: layers/layer/launchpad-payload
+ stage:
+ - layers
+ prime:
+ - "-layers"
+ layer-coordinator:
+ source: https://git.launchpad.net/layer-coordinator
+ source-commit: "fa27fc93e0b08000963e83a6bfe49812d890dfcf"
+ source-submodules: []
+ source-type: git
+ plugin: dump
+ organize:
+ "*": layers/layer/coordinator/
+ stage:
+ - layers
+ prime:
+ - "-layers"
+ charm:
+ after:
+ - charm-wheels
+ - launchpad-layers
+ - layer-coordinator
+ source: .
+ plugin: reactive
+ build-snaps: [charm]
+ build-packages: [libpq-dev, python3-dev]
+ build-environment:
+ - CHARM_LAYERS_DIR: $CRAFT_STAGE/layers/layer
+ - CHARM_INTERFACES_DIR: $CRAFT_STAGE/layers/interface
+ - PIP_NO_INDEX: "true"
+ - PIP_FIND_LINKS: $CRAFT_STAGE/charm-wheels
+ reactive-charm-build-arguments: [--binary-wheels-from-source]
diff --git a/charm/launchpad-loggerhead/config.yaml b/charm/launchpad-loggerhead/config.yaml
new file mode 100644
index 0000000..3145bff
--- /dev/null
+++ b/charm/launchpad-loggerhead/config.yaml
@@ -0,0 +1,43 @@
+options:
+ haproxy_server_options:
+ type: string
+ description: Options to add to HAProxy "server" lines.
+ default: check inter 10000 rise 2 fall 2 maxconn 15
+ haproxy_service_options:
+ type: string
+ description: HAProxy options for codebrowse services.
+ default: |
+ - mode http
+ - option httplog
+ - option httpchk GET /robots.txt HTTP/1.0
+ - option forwardfor
+ - balance leastconn
+ internal_branch_by_id_root:
+ type: string
+ description: |
+ The URL prefix for where branches are served by URLs based on the
+ branch ID.
+ default:
+ nagios_check_branch:
+ type: string
+ description: If set, add Nagios checks for this branch.
+ default: ""
+ port_loggerhead:
+ type: int
+ description: >
+ Port to expose to the public (indirectly; we expect Apache on the
+ Bazaar codehosting system to ProxyPass to this port). This serves
+ both public and private branches, but requests for private branches
+ must be authenticated.
+ default: 10007
+ port_loggerhead_api:
+ type: int
+ description: >
+ Private port for read-only API requests. This must not be exposed to
+ the public; other parts of Launchpad with access to this port must
+ ensure that the appropriate security checks are performed.
+ default: 10017
+ session_secret:
+ type: string
+ description: A base64-encoded secret key used to sign session cookies.
+ default: ""
diff --git a/charm/launchpad-loggerhead/layer.yaml b/charm/launchpad-loggerhead/layer.yaml
new file mode 100644
index 0000000..5743ccb
--- /dev/null
+++ b/charm/launchpad-loggerhead/layer.yaml
@@ -0,0 +1,5 @@
+includes:
+ - layer:launchpad-base
+ - layer:coordinator
+ - interface:http
+repo: https://git.launchpad.net/launchpad
diff --git a/charm/launchpad-loggerhead/metadata.yaml b/charm/launchpad-loggerhead/metadata.yaml
new file mode 100644
index 0000000..186e666
--- /dev/null
+++ b/charm/launchpad-loggerhead/metadata.yaml
@@ -0,0 +1,18 @@
+name: launchpad-loggerhead
+display-name: launchpad-loggerhead
+summary: Launchpad Bazaar/Breezy code browsing server
+maintainer: Launchpad Developers <launchpad-dev@xxxxxxxxxxxxxxxxxxx>
+description: |
+ Launchpad is an open source suite of tools that help people and teams
+ to work together on software projects.
+
+ This charm runs a code browsing server for Bazaar/Breezy branches.
+tags:
+ # https://juju.is/docs/charm-metadata#heading--charm-store-fields
+ - network
+series:
+ - focal
+subordinate: false
+provides:
+ loadbalancer:
+ interface: http
diff --git a/charm/launchpad-loggerhead/reactive/launchpad-loggerhead.py b/charm/launchpad-loggerhead/reactive/launchpad-loggerhead.py
new file mode 100644
index 0000000..5d9347c
--- /dev/null
+++ b/charm/launchpad-loggerhead/reactive/launchpad-loggerhead.py
@@ -0,0 +1,225 @@
+# Copyright 2023 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+import base64
+import os.path
+import subprocess
+
+import yaml
+from charmhelpers.core import hookenv, host, templating
+from charms.coordinator import acquire
+from charms.launchpad.base import (
+ get_service_config,
+ lazr_config_files,
+ secrets_dir,
+)
+from charms.launchpad.payload import (
+ config_file_path,
+ configure_cron,
+ configure_lazr,
+)
+from charms.reactive import (
+ clear_flag,
+ endpoint_from_flag,
+ helpers,
+ set_flag,
+ when,
+ when_none,
+ when_not,
+ when_not_all,
+)
+from ols import base
+
+
+def reload_or_restart(service):
+ subprocess.run(["systemctl", "reload-or-restart", service], check=True)
+
+
+@host.restart_on_change(
+ {
+ "/lib/systemd/system/launchpad-loggerhead.service": [
+ "launchpad-loggerhead.service"
+ ],
+ },
+)
+def configure_systemd(config):
+ hookenv.log("Writing systemd service.")
+ config = dict(config)
+ templating.render(
+ "launchpad-loggerhead.service.j2",
+ "/lib/systemd/system/launchpad-loggerhead.service",
+ config,
+ )
+ subprocess.run(["systemctl", "daemon-reload"], check=True)
+ host.add_user_to_group("syslog", base.user())
+
+
+def configure_logrotate(config):
+ hookenv.log("Writing logrotate configuration.")
+ templating.render(
+ "logrotate.conf.j2",
+ "/etc/logrotate.d/loggerhead",
+ config,
+ perms=0o644,
+ )
+
+
+def session_secret_path():
+ return os.path.join(secrets_dir(), "cookies.hmac")
+
+
+def configure_session_secret(config):
+ session_secret = base64.b64decode(config["session_secret"].encode())
+ host.write_file(
+ session_secret_path(), session_secret, group=base.user(), perms=0o440
+ )
+
+
+def config_files():
+ files = []
+ files.extend(lazr_config_files())
+ files.append(config_file_path("launchpad-loggerhead/launchpad-lazr.conf"))
+ files.append(session_secret_path())
+ return files
+
+
+@when(
+ "config.set.domain_bzr",
+ "config.set.session_secret",
+ "launchpad.base.configured",
+)
+@when_none("coordinator.requested.restart", "service.configured")
+def configure():
+ config = get_service_config()
+ config["cache_dir"] = os.path.join(base.base_dir(), "cache")
+ host.mkdir(
+ config["cache_dir"], owner=base.user(), group=base.user(), perms=0o700
+ )
+ configure_lazr(
+ config,
+ "launchpad-loggerhead-lazr.conf",
+ "launchpad-loggerhead/launchpad-lazr.conf",
+ )
+ configure_systemd(config)
+ configure_logrotate(config)
+ configure_cron(config, "crontab.j2")
+ configure_session_secret(config)
+
+ if helpers.any_file_changed(
+ [
+ base.version_info_path(),
+ "/lib/systemd/system/launchpad-loggerhead.service",
+ ]
+ + config_files()
+ ):
+ hookenv.log("Config files changed; waiting for restart lock")
+ acquire("restart")
+ else:
+ hookenv.log("Not restarting, since no config files were changed")
+ set_flag("service.configured")
+
+
+@when("coordinator.granted.restart")
+def restart():
+ hookenv.log("Restarting application server")
+ host.service_restart("launchpad-loggerhead.service")
+ set_flag("service.configured")
+
+
+@when("service.configured")
+def check_is_running():
+ hookenv.status_set("active", "Ready")
+
+
+@when("service.configured")
+@when_not_all(
+ "config.set.domain_bzr",
+ "config.set.session_secret",
+ "launchpad.base.configured",
+)
+def deconfigure():
+ clear_flag("service.configured")
+
+
+@when("nrpe-external-master.available", "service.configured")
+@when_not("launchpad.loggerhead.nrpe-external-master.published")
+def nrpe_available():
+ nrpe = endpoint_from_flag("nrpe-external-master.available")
+ config = hookenv.config()
+ if config["nagios_check_branch"]:
+ nrpe.add_check(
+ [
+ "/usr/lib/nagios/plugins/check_http",
+ "-H",
+ "localhost",
+ "-p",
+ str(config["port_loggerhead"]),
+ "-u",
+ f"{config['nagios_check_branch']}/files",
+ ],
+ name="check_launchpad_loggerhead",
+ description="Launchpad loggerhead",
+ context=config["nagios_context"],
+ )
+ set_flag("launchpad.loggerhead.nrpe-external-master.published")
+
+
+@when("launchpad.loggerhead.nrpe-external-master.published")
+@when_not("nrpe-external-master.available")
+def nrpe_unavailable():
+ clear_flag("launchpad.loggerhead.nrpe-external-master.published")
+
+
+@when("loadbalancer.available", "service.configured")
+@when_not("launchpad.loadbalancer.configured")
+def configure_loadbalancer():
+ config = hookenv.config()
+
+ try:
+ service_options = yaml.safe_load(config["haproxy_service_options"])
+ except yaml.YAMLError:
+ hookenv.log("Could not parse haproxy_service_options YAML")
+ hookenv.status_set(
+ "blocked", "Bad haproxy_service_options YAML configuration"
+ )
+ return
+ server_options = config["haproxy_server_options"]
+
+ unit_name = hookenv.local_unit().replace("/", "-")
+ unit_ip = hookenv.unit_private_ip()
+ services = [
+ {
+ "service_name": "launchpad-loggerhead",
+ "service_port": config["port_loggerhead"],
+ "service_host": "0.0.0.0",
+ "service_options": list(service_options),
+ "servers": [
+ [
+ f"public_{unit_name}",
+ unit_ip,
+ config["port_loggerhead"],
+ server_options,
+ ]
+ ],
+ },
+ {
+ "service_name": "launchpad-loggerhead-api",
+ "service_port": config["port_loggerhead_api"],
+ "service_host": "0.0.0.0",
+ "service_options": list(service_options),
+ "servers": [
+ [
+ f"public_{unit_name}",
+ unit_ip,
+ config["port_loggerhead_api"],
+ server_options,
+ ]
+ ],
+ },
+ ]
+ services_yaml = yaml.dump(services)
+
+ for rel in hookenv.relations_of_type("loadbalancer"):
+ hookenv.relation_set(rel["__relid__"], services=services_yaml)
+
+ set_flag("launchpad.loadbalancer.configured")
diff --git a/charm/launchpad-loggerhead/templates/crontab.j2 b/charm/launchpad-loggerhead/templates/crontab.j2
new file mode 100644
index 0000000..0a532ba
--- /dev/null
+++ b/charm/launchpad-loggerhead/templates/crontab.j2
@@ -0,0 +1,10 @@
+TZ=UTC
+MAILTO={{ cron_mailto }}
+
+# Clean up cache directory.
+25 0 * * * find {{ cache_dir }} -maxdepth 1 -type d -mtime +240 -execdir rm -rf {} +
+
+# Catch up with publishing OOPSes that were temporarily spooled to disk due
+# to RabbitMQ being unavailable.
+*/15 * * * * {{ code_dir }}/bin/datedir2amqp --exchange oopses --host {{ rabbitmq_host }} --username {{ rabbitmq_username }} --password {{ rabbitmq_password }} --vhost {{ rabbitmq_vhost }} --repo {{ oopses_dir }} --key ""
+
diff --git a/charm/launchpad-loggerhead/templates/launchpad-loggerhead-lazr.conf b/charm/launchpad-loggerhead/templates/launchpad-loggerhead-lazr.conf
new file mode 100644
index 0000000..d1212e9
--- /dev/null
+++ b/charm/launchpad-loggerhead/templates/launchpad-loggerhead-lazr.conf
@@ -0,0 +1,24 @@
+# Public configuration data. The contents of this file may be freely shared
+# with developers if needed for debugging.
+
+# A schema's sections, keys, and values are automatically inherited, except
+# for '.optional' sections. Update this config to override key values.
+# Values are strings, except for numbers that look like ints. The tokens
+# true, false, and none are treated as True, False, and None.
+
+{% from "macros.j2" import opt -%}
+
+[meta]
+extends: ../launchpad-base-lazr.conf
+
+[codebrowse]
+cachepath: {{ cache_dir }}
+launchpad_root: https://code.{{ domain }}/
+log_folder: {{ logs_dir }}
+port: {{ port_loggerhead }}
+private_port: {{ port_loggerhead_api }}
+secret_path: {{ secrets_dir }}/cookies.hmac
+
+[codehosting]
+{{- opt("internal_branch_by_id_root", internal_branch_by_id_root) }}
+
diff --git a/charm/launchpad-loggerhead/templates/launchpad-loggerhead.service.j2 b/charm/launchpad-loggerhead/templates/launchpad-loggerhead.service.j2
new file mode 100644
index 0000000..4a98153
--- /dev/null
+++ b/charm/launchpad-loggerhead/templates/launchpad-loggerhead.service.j2
@@ -0,0 +1,22 @@
+[Unit]
+Description=Launchpad Bazaar/Breezy code browsing server
+After=network.target
+ConditionPathExists=!{{ code_dir }}/maintenance.txt
+
+[Service]
+Type=notify
+User=launchpad
+Group=launchpad
+WorkingDirectory={{ code_dir }}
+Environment=BRZ_PLUGIN_PATH=brzplugins
+Environment=LPCONFIG=launchpad-loggerhead
+SyslogIdentifier=loggerhead
+ExecStart={{ code_dir }}/scripts/start-loggerhead.py
+ExecReload=/bin/kill -HUP $MAINPID
+KillMode=mixed
+Restart=on-failure
+PrivateTmp=true
+
+[Install]
+WantedBy=multi-user.target
+
diff --git a/charm/launchpad-loggerhead/templates/logrotate.conf.j2 b/charm/launchpad-loggerhead/templates/logrotate.conf.j2
new file mode 100644
index 0000000..5bde352
--- /dev/null
+++ b/charm/launchpad-loggerhead/templates/logrotate.conf.j2
@@ -0,0 +1,15 @@
+{{ logs_dir }}/access.log {{ logs_dir }}/debug.log
+{
+ rotate 21
+ daily
+ dateext
+ delaycompress
+ compress
+ notifempty
+ missingok
+ create 0644 {{ user }} {{ user }}
+ postrotate
+ systemctl restart launchpad-loggerhead.service
+ endscript
+}
+