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 <cjwat...@canonical.com> +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> +
_______________________________________________ Mailing list: https://launchpad.net/~launchpad-reviewers Post to : launchpad-reviewers@lists.launchpad.net Unsubscribe : https://launchpad.net/~launchpad-reviewers More help : https://help.launchpad.net/ListHelp