launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #29990
[Merge] ~cjwatson/launchpad:charm-assets into launchpad:master
Colin Watson has proposed merging ~cjwatson/launchpad:charm-assets into launchpad:master.
Commit message:
charm: Add a launchpad-assets charm
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/442679
This extracts the parts of our current frontend configuration that need access to a Launchpad payload. Once this is deployed, we can adjust the frontend configuration to point to it using `ProxyPass`.
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:charm-assets into launchpad:master.
diff --git a/charm/Makefile b/charm/Makefile
index 6c4f214..d1e4611 100644
--- a/charm/Makefile
+++ b/charm/Makefile
@@ -16,6 +16,7 @@ ASSET = ../build/$(BUILD_LABEL)/$(TARBALL)
CHARMS := \
launchpad-admin \
launchpad-appserver \
+ launchpad-assets \
launchpad-librarian
all: ## alias to build
diff --git a/charm/launchpad-assets/README.md b/charm/launchpad-assets/README.md
new file mode 100644
index 0000000..34e4fa6
--- /dev/null
+++ b/charm/launchpad-assets/README.md
@@ -0,0 +1,4 @@
+# Launchpad assets
+
+This charm publishes assets (CSS, JavaScript, and API documentation) so that
+they can be proxied by frontends.
diff --git a/charm/launchpad-assets/charmcraft.yaml b/charm/launchpad-assets/charmcraft.yaml
new file mode 100644
index 0000000..c3b7cf8
--- /dev/null
+++ b/charm/launchpad-assets/charmcraft.yaml
@@ -0,0 +1,72 @@
+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: "59b32ae07f98051385c96d6d8e7e02ca4f197fe5"
+ 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: "56d219f60a293a6c73759b8439ef5fdb11e19d1f"
+ 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: "42a4b4c4f62936b1d050c775e84f7364dfb5efc0"
+ source-submodules: []
+ source-type: git
+ plugin: dump
+ organize:
+ launchpad-payload: layers/layer/launchpad-payload
+ stage:
+ - layers
+ prime:
+ - "-layers"
+ interface-apache-website:
+ source: https://github.com/juju-solutions/interface-apache-website
+ source-commit: "2f736ebcc90d19ac142a2d898a2ec7e1aafaa13f"
+ source-submodules: []
+ source-type: git
+ plugin: dump
+ organize:
+ "*": layers/interface/apache-website/
+ stage:
+ - layers
+ prime:
+ - "-layers"
+ launchpad-assets:
+ after:
+ - charm-wheels
+ - launchpad-layers
+ - interface-apache-website
+ source: .
+ plugin: reactive
+ build-snaps: [charm/2.x/stable]
+ 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
diff --git a/charm/launchpad-assets/config.yaml b/charm/launchpad-assets/config.yaml
new file mode 100644
index 0000000..bab57b5
--- /dev/null
+++ b/charm/launchpad-assets/config.yaml
@@ -0,0 +1,9 @@
+options:
+ domain:
+ type: string
+ description: Domain name for this instance.
+ default: "launchpad.test"
+ port_assets:
+ type: int
+ description: Port for the assets website.
+ default: 8009
diff --git a/charm/launchpad-assets/layer.yaml b/charm/launchpad-assets/layer.yaml
new file mode 100644
index 0000000..4410c41
--- /dev/null
+++ b/charm/launchpad-assets/layer.yaml
@@ -0,0 +1,11 @@
+includes:
+ - layer:launchpad-payload
+ - interface:apache-website
+repo: https://git.launchpad.net/launchpad
+options:
+ apt:
+ packages:
+ - nodejs
+ - python3-convoy
+ launchpad-payload:
+ build_target: build
diff --git a/charm/launchpad-assets/metadata.yaml b/charm/launchpad-assets/metadata.yaml
new file mode 100644
index 0000000..745ccde
--- /dev/null
+++ b/charm/launchpad-assets/metadata.yaml
@@ -0,0 +1,20 @@
+name: launchpad-assets
+display-name: launchpad-assets
+summary: Launchpad assets
+maintainer: Colin Watson <cjwatson@xxxxxxxxxxxxx>
+description: |
+ Launchpad is an open source suite of tools that help people and teams
+ to work together on software projects.
+
+ This charm publishes Launchpad assets (CSS, JavaScript, and API
+ documentation) so that they can be proxied by frontends.
+tags:
+ # https://juju.is/docs/charm-metadata#heading--charm-store-fields
+ - network
+series:
+ - focal
+subordinate: true
+requires:
+ apache-website:
+ interface: apache-website
+ scope: container
diff --git a/charm/launchpad-assets/reactive/launchpad-assets.py b/charm/launchpad-assets/reactive/launchpad-assets.py
new file mode 100644
index 0000000..dce1243
--- /dev/null
+++ b/charm/launchpad-assets/reactive/launchpad-assets.py
@@ -0,0 +1,105 @@
+# Copyright 2023 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+import os.path
+import subprocess
+from pathlib import Path
+
+from charmhelpers.core import hookenv, host, templating
+from charms.reactive import (
+ clear_flag,
+ endpoint_from_flag,
+ set_flag,
+ when,
+ when_all,
+ when_not,
+ when_not_all,
+)
+from ols import base
+
+
+@host.restart_on_change(
+ {
+ "/lib/systemd/system/convoy.service": ["convoy.service"],
+ "/lib/systemd/system/convoy.socket": ["convoy.socket"],
+ }
+)
+def configure_convoy(config):
+ hookenv.log("Writing convoy configuration.")
+
+ # Update convoy symlinks.
+ build_label = config["build_label"]
+ convoy_path = Path(base.base_dir()) / "convoy"
+ convoy_path.mkdir(parents=True, exist_ok=True)
+ link_path = convoy_path / f"rev{build_label}"
+ if not link_path.is_symlink():
+ link_path.symlink_to(Path(base.code_dir()) / "build" / "js")
+ for link_path in convoy_path.iterdir():
+ if link_path.name.startswith("rev") and link_path.is_symlink():
+ if not link_path.exists():
+ link_path.unlink()
+
+ templating.render(
+ "convoy.service.j2", "/lib/systemd/system/convoy.service", config
+ )
+ templating.render(
+ "convoy.socket.j2", "/lib/systemd/system/convoy.socket", config
+ )
+ subprocess.run(["systemctl", "daemon-reload"])
+
+
+def get_service_config():
+ config = hookenv.config()
+ config.update(
+ {
+ "base_dir": base.base_dir(),
+ "code_dir": base.code_dir(),
+ "logs_dir": base.logs_dir(),
+ "payloads_dir": base.payloads_dir(),
+ }
+ )
+ return config
+
+
+def config_file_path(name):
+ return os.path.join(base.code_dir(), "production-configs", name)
+
+
+@when("ols.configured")
+@when_not("service.configured")
+def configure():
+ config = get_service_config()
+ hookenv.log("Writing launchpad-assets/launchpad-lazr.conf.")
+ templating.render(
+ "launchpad-assets-lazr.conf",
+ config_file_path("launchpad-assets/launchpad-lazr.conf"),
+ config,
+ owner="root",
+ group=base.user(),
+ perms=0o444,
+ )
+ configure_convoy(config)
+ set_flag("service.configured")
+
+
+@when_all("service.configured", "apache-website.available")
+@when_not("service.apache-configured")
+def send_apache_website():
+ apache = endpoint_from_flag("apache-website.available")
+ config = get_service_config()
+ apache.send_domain(f"assets.{config['domain']}")
+ apache.send_site_config(templating.render("vhost.conf.j2", None, config))
+ # interface-apache incorrectly sets `modules`, not `site_modules`. Work
+ # around this.
+ apache.set_remote(site_modules="headers proxy proxy_http")
+ apache.send_ports([config["port_assets"]])
+ apache.send_enabled()
+ hookenv.status_set("active", "Ready")
+ set_flag("service.apache-configured")
+
+
+@when("service.apache-configured")
+@when_not_all("service.configured", "apache-website.available")
+def apache_deconfigured():
+ hookenv.status_set("blocked", "Website not yet configured")
+ clear_flag("service.apache-configured")
diff --git a/charm/launchpad-assets/templates/convoy.service.j2 b/charm/launchpad-assets/templates/convoy.service.j2
new file mode 100644
index 0000000..70cb525
--- /dev/null
+++ b/charm/launchpad-assets/templates/convoy.service.j2
@@ -0,0 +1,21 @@
+[Unit]
+Description=Launchpad CSS/JavaScript combo loader
+Requires=convoy.socket
+After=network.target
+
+[Service]
+Type=notify
+User=launchpad
+Group=launchpad
+Restart=on-failure
+Environment=CONVOY_ROOT={{ base_dir }}/convoy
+ExecStart={{ code_dir }}/bin/gunicorn --bind unix:/run/convoy.socket --log-file {{ logs_dir }}/convoy.log --log-level debug --workers=4 convoy.wsgi
+ExecReload=/bin/kill -USR1 $MAINPID
+PrivateTmp=true
+PrivateDevices=true
+ProtectSystem=true
+NoNewPrivileges=true
+
+[Install]
+WantedBy=multi-user.target
+
diff --git a/charm/launchpad-assets/templates/convoy.socket.j2 b/charm/launchpad-assets/templates/convoy.socket.j2
new file mode 100644
index 0000000..e4d032d
--- /dev/null
+++ b/charm/launchpad-assets/templates/convoy.socket.j2
@@ -0,0 +1,9 @@
+[Unit]
+Description=Launchpad CSS/JavaScript combo loader
+
+[Socket]
+ListenStream=/run/convoy.socket
+
+[Install]
+WantedBy=sockets.target
+
diff --git a/charm/launchpad-assets/templates/launchpad-assets-lazr.conf b/charm/launchpad-assets/templates/launchpad-assets-lazr.conf
new file mode 100644
index 0000000..24637f3
--- /dev/null
+++ b/charm/launchpad-assets/templates/launchpad-assets-lazr.conf
@@ -0,0 +1,14 @@
+# 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.
+
+[meta]
+extends: ../lib/lp/services/config/schema-lazr.conf
+
+[vhost.api]
+hostname: api.{{ domain }}
+
diff --git a/charm/launchpad-assets/templates/logrotate.conf.j2 b/charm/launchpad-assets/templates/logrotate.conf.j2
new file mode 100644
index 0000000..6f98e3e
--- /dev/null
+++ b/charm/launchpad-assets/templates/logrotate.conf.j2
@@ -0,0 +1,15 @@
+{{ logs_dir }}/convoy.log
+{
+ rotate 21
+ daily
+ dateext
+ delaycompress
+ compress
+ notifempty
+ missingok
+ create 0644 syslog adm
+ postrotate
+ systemctl reload convoy.service
+ endscript
+}
+
diff --git a/charm/launchpad-assets/templates/vhost.conf.j2 b/charm/launchpad-assets/templates/vhost.conf.j2
new file mode 100644
index 0000000..65bc07f
--- /dev/null
+++ b/charm/launchpad-assets/templates/vhost.conf.j2
@@ -0,0 +1,37 @@
+<VirtualHost *:{{ port_assets }}>
+ ServerName assets.{{ domain }}
+
+ ErrorLog /var/log/apache2/assets.{{ domain }}-error.log
+ CustomLog /var/log/apache2/assets.{{ domain }}-access.log combined
+
+ <Location "/+apidoc/">
+ Header set Cache-Control "public,max-age=5184000"
+ Require all granted
+ </Location>
+ Alias "/+apidoc/" "{{ code_dir }}/lib/canonical/launchpad/apidoc/"
+
+ <Location "/+combo/">
+ Header set Cache-Control "public,max-age=5184000"
+ Require all granted
+ ProxyPass "unix:/run/convoy.socket|http://localhost/"
+ </Location>
+
+ <LocationMatch "^/\+icing/rev(?<commit>[0-9a-f]+)/">
+ Header set Cache-Control "public,max-age=5184000"
+ Require all granted
+ </LocationMatch>
+ AliasMatch "^/\+icing/rev([0-9a-f]+)/(.*)" "{{ payloads_dir }}/$1/lib/canonical/launchpad/icing/$2"
+ <Location "/+icing/">
+ Header set Cache-Control "public,max-age=5184000"
+ Require all granted
+ </Location>
+ Alias "/+icing/" "{{ code_dir }}/lib/canonical/launchpad/icing/"
+
+ <Location "/@@/">
+ Options MultiViews
+ Header set Cache-Control "public,max-age=5184000"
+ Require all granted
+ </Location>
+ Alias "/@@/" "{{ code_dir }}/lib/canonical/launchpad/images/"
+</VirtualHost>
+