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 <[email protected]> +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) + + [email protected]_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 +} +
_______________________________________________ Mailing list: https://launchpad.net/~launchpad-reviewers Post to : [email protected] Unsubscribe : https://launchpad.net/~launchpad-reviewers More help : https://help.launchpad.net/ListHelp

