Merge ~lgp171188/launchpad:charm-launchpad-codehosting into launchpad:master

Proposed by Guruprasad
Status: Merged
Approved by: Guruprasad
Approved revision: fc2b117e3ec3c5a5f88f19597ed7fa3e14185cbc
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~lgp171188/launchpad:charm-launchpad-codehosting
Merge into: launchpad:master
Diff against target: 1257 lines (+1111/-0)
24 files modified
charm/launchpad-codehosting/README.md (+64/-0)
charm/launchpad-codehosting/actions.yaml (+11/-0)
charm/launchpad-codehosting/actions/actions.py (+53/-0)
charm/launchpad-codehosting/actions/start-services (+1/-0)
charm/launchpad-codehosting/actions/stop-services (+1/-0)
charm/launchpad-codehosting/charmcraft.yaml (+76/-0)
charm/launchpad-codehosting/config.yaml (+110/-0)
charm/launchpad-codehosting/layer.yaml (+15/-0)
charm/launchpad-codehosting/metadata.yaml (+24/-0)
charm/launchpad-codehosting/reactive/launchpad-codehosting.py (+439/-0)
charm/launchpad-codehosting/templates/cleanlogs.j2 (+16/-0)
charm/launchpad-codehosting/templates/crontab.j2 (+29/-0)
charm/launchpad-codehosting/templates/launchpad-bzr-sftp-generator.j2 (+18/-0)
charm/launchpad-codehosting/templates/launchpad-bzr-sftp.service.j2 (+18/-0)
charm/launchpad-codehosting/templates/launchpad-bzr-sftp@.service.j2 (+24/-0)
charm/launchpad-codehosting/templates/launchpad-codehosting-lazr-common.conf.j2 (+27/-0)
charm/launchpad-codehosting/templates/launchpad-codehosting-sftp-lazr.conf.j2 (+21/-0)
charm/launchpad-codehosting/templates/logrotate.conf.j2 (+16/-0)
charm/launchpad-codehosting/templates/rewrite_wrapper.sh.j2 (+3/-0)
charm/launchpad-codehosting/templates/robots.txt (+4/-0)
charm/launchpad-codehosting/templates/vhosts/bazaar_http.conf.j2 (+79/-0)
charm/launchpad-codehosting/templates/vhosts/bazaar_https.conf.j2 (+38/-0)
charm/launchpad-codehosting/templates/vhosts/bazaar_internal_branch_by_id.conf.j2 (+18/-0)
charm/launchpad-codehosting/templates/vhosts/common.conf (+6/-0)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+452140@code.launchpad.net

Commit message

Add a launchpad-codehosting charm

To post a comment you must log in.
Revision history for this message
Guruprasad (lgp171188) :
Revision history for this message
Guruprasad (lgp171188) wrote :

Note: This charm doesn't yet integrate with an haproxy instance that sits in front of the `codehosting-apache2` charm, the `bzr-sftp` services, and the `codebrowse` charm.

Also, I have only tested that the charm deploys without any errors and the services start and run. I have to set up a bzr repository somehow and then try accessing it over HTTP and SSH to verify that everything works as expected. This will need a deployment with many other relevant applications from the bundle deployed and activated.

Revision history for this message
Colin Watson (cjwatson) wrote :

Good start! I deployed my `launchpad-loggerhead` charm alongside it and was able to get them talking to each other with a few fixes and some manual configuration. The suggestions here come from that experiment.

review: Needs Fixing
Revision history for this message
Guruprasad (lgp171188) wrote :

> Note: This charm doesn't yet integrate with an haproxy instance that sits in front of the `codehosting-apache2` charm, the `bzr-sftp` services, and the `codebrowse` charm.

This is no longer the case but I am currently having trouble deploying the charm because `juju` complains about the charm not providing the `loadbalancer` relation.

Revision history for this message
Colin Watson (cjwatson) :
review: Approve
Revision history for this message
Colin Watson (cjwatson) :
review: Approve
Revision history for this message
Guruprasad (lgp171188) :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/charm/launchpad-codehosting/README.md b/charm/launchpad-codehosting/README.md
2new file mode 100644
3index 0000000..de715c2
4--- /dev/null
5+++ b/charm/launchpad-codehosting/README.md
6@@ -0,0 +1,64 @@
7+# Launchpad codehosting
8+
9+This charm sets up and runs the Launchpad codehosting service. It is
10+a subordinate charm that should be deployed on the same instance as a
11+primary apache2 charm. Let's call the apache2 charm application
12+`codehosting-apache2` for the purpose of the below explanation.
13+
14+This charm also requires one or more `haproxy` instances reverse proxying
15+to it. Let us call these `codehosting-lb`, which proxies services
16+that have to be exposed to the internet and does TLS termination, and
17+`services-lb`, which proxies internal-only private services.
18+
19+You will need the following relations:
20+
21+ juju relate launchpad-codehosting:db postgresql:db
22+ juju relate launchpad-codehosting rabbitmq-server
23+ juju relate launchpad-codehosting:apache-website codehosting-apache2:apache-website
24+ juju relate launchpad-codehosting:loadbalancer services-lb:reverseproxy
25+ juju relate launchpad-codehosting:frontend-loadbalancer codehosting-lb:reverseproxy
26+
27+In Canonical's deployment of this charm, an additional volume is provisioned and mounted
28+at the directory configured to store the `bzr` repositories.
29+
30+* Create a Ceph volume of an appropriate size using a command like the
31+ one below to create this volume in the `qastaging` environment.
32+
33+ openstack volume create --size 200 \
34+ --description 'Bazaar repositories - lp/qastaging' \
35+ lp-qastaging-bzr-repositories
36+
37+* Attach this Ceph volume to the `launchpad-codehosting` unit using the following
38+ command. The instance ID of the `launchpad-codehosting` unit can be found
39+ from the output of the `juju status` command.
40+
41+ openstack server add volume <instance id> lp-qastaging-bzr-repositories
42+
43+* Log in to the `launchpad-codehosting` unit and perform the following steps.
44+
45+* Create a filesystem on the new volume using `sudo mkfs.ext4 /dev/vdb`. Do
46+ check and verify that this device node matches the new volume.
47+
48+* Create the `/srv/launchpad/data/mirror` directory, if it does not
49+ exist already. Here, `/srv/launchpad` corresponds to the `base_dir`
50+ template context variable.
51+
52+* Add `/dev/vdb /srv/launchpad/data/mirror ext4 defaults 0 2` to
53+ `/etc/fstab`.
54+
55+* Mount the new volume using `sudo mount /srv/launchpad/data/mirror`.
56+
57+* Set the correct permissions on the new volume using the following commands.
58+
59+ sudo chown launchpad: /srv/launchpad/data/mirror
60+ sudo chmod 755 /srv/launchpad/data/mirror
61+
62+## Maintenance actions
63+
64+To stop bzr-sftp workers run:
65+
66+ juju run-action --wait launchpad-codehosting/leader stop-services
67+
68+To start them again once maintenance is complete:
69+
70+ juju run-action --wait launchpad-codehosting/leader start-services
71diff --git a/charm/launchpad-codehosting/actions.yaml b/charm/launchpad-codehosting/actions.yaml
72new file mode 100644
73index 0000000..86aae0a
74--- /dev/null
75+++ b/charm/launchpad-codehosting/actions.yaml
76@@ -0,0 +1,11 @@
77+start-services:
78+ description: |
79+ Start the launchpad-bzr-sftp service. Usually run after
80+ maintenance.
81+stop-services:
82+ description: |
83+ Stop the launchpad-bzr-sftp service. Usually run in preparation
84+ for maintenance. (Note that this does not stop services in a way that
85+ will persist across a reboot. It also doesn't disable cron jobs, since
86+ those are handled by the cron-control mechanism instead; see
87+ lp.services.scripts.base.cronscript_enabled.)
88diff --git a/charm/launchpad-codehosting/actions/actions.py b/charm/launchpad-codehosting/actions/actions.py
89new file mode 100755
90index 0000000..cbac43e
91--- /dev/null
92+++ b/charm/launchpad-codehosting/actions/actions.py
93@@ -0,0 +1,53 @@
94+#! /usr/bin/python3
95+# Copyright 2023 Canonical Ltd. This software is licensed under the
96+# GNU Affero General Public License version 3 (see the file LICENSE).
97+
98+import subprocess
99+import sys
100+import traceback
101+from pathlib import Path
102+
103+sys.path.append("lib")
104+
105+from charms.layer import basic # noqa: E402
106+
107+basic.bootstrap_charm_deps()
108+basic.init_config_states()
109+
110+from charmhelpers.core import hookenv # noqa: E402
111+
112+
113+def start_services():
114+ service = "launchpad-bzr-sftp.service"
115+ hookenv.log(f"Starting {service}.")
116+ subprocess.run(["systemctl", "start", service], check=True)
117+ hookenv.action_set({"result": "Services started"})
118+
119+
120+def stop_services():
121+ service = "launchpad-bzr-sftp.service"
122+ hookenv.log(f"Stopping {service}.")
123+ subprocess.run(["systemctl", "stop", service], check=True)
124+ hookenv.action_set({"result": "Services stopped"})
125+
126+
127+def main(argv):
128+ action = Path(argv[0]).name
129+ try:
130+ if action == "start-services":
131+ start_services()
132+ elif action == "stop-services":
133+ stop_services()
134+ else:
135+ hookenv.action_fail(f"Action {action} not implemented.")
136+ except Exception:
137+ hookenv.action_fail("Unhandled exception")
138+ tb = traceback.format_exc()
139+ hookenv.action_set(dict(traceback=tb))
140+ hookenv.log(f"Unhandled exception in action {action}:")
141+ for line in tb.splitlines():
142+ hookenv.log(line)
143+
144+
145+if __name__ == "__main__":
146+ main(sys.argv)
147diff --git a/charm/launchpad-codehosting/actions/start-services b/charm/launchpad-codehosting/actions/start-services
148new file mode 120000
149index 0000000..405a394
150--- /dev/null
151+++ b/charm/launchpad-codehosting/actions/start-services
152@@ -0,0 +1 @@
153+actions.py
154\ No newline at end of file
155diff --git a/charm/launchpad-codehosting/actions/stop-services b/charm/launchpad-codehosting/actions/stop-services
156new file mode 120000
157index 0000000..405a394
158--- /dev/null
159+++ b/charm/launchpad-codehosting/actions/stop-services
160@@ -0,0 +1 @@
161+actions.py
162\ No newline at end of file
163diff --git a/charm/launchpad-codehosting/charmcraft.yaml b/charm/launchpad-codehosting/charmcraft.yaml
164new file mode 100644
165index 0000000..57c980a
166--- /dev/null
167+++ b/charm/launchpad-codehosting/charmcraft.yaml
168@@ -0,0 +1,76 @@
169+type: charm
170+bases:
171+ - build-on:
172+ - name: ubuntu
173+ channel: "20.04"
174+ architectures: [amd64]
175+ run-on:
176+ - name: ubuntu
177+ channel: "20.04"
178+ architectures: [amd64]
179+parts:
180+ charm-wheels:
181+ source: https://git.launchpad.net/~ubuntuone-hackers/ols-charm-deps/+git/wheels
182+ source-commit: "42c89d9c66dbe137139b047fd54aed49b66d1a5e"
183+ source-submodules: []
184+ source-type: git
185+ plugin: dump
186+ organize:
187+ "*": charm-wheels/
188+ prime:
189+ - "-charm-wheels"
190+ ols-layers:
191+ source: https://git.launchpad.net/ols-charm-deps
192+ source-commit: "9c59a9804f1f40e2a74be7dac9bf18a655a7864f"
193+ source-submodules: []
194+ source-type: git
195+ plugin: dump
196+ organize:
197+ "*": layers/
198+ stage:
199+ - layers
200+ prime:
201+ - "-layers"
202+ launchpad-layers:
203+ after:
204+ - ols-layers
205+ source: https://git.launchpad.net/launchpad-layers
206+ source-commit: "0fd04056ad0ddb366be6ad7d50991efed385ec48"
207+ source-submodules: []
208+ source-type: git
209+ plugin: dump
210+ organize:
211+ launchpad-base: layers/layer/launchpad-base
212+ launchpad-db: layers/layer/launchpad-db
213+ launchpad-payload: layers/layer/launchpad-payload
214+ stage:
215+ - layers
216+ prime:
217+ - "-layers"
218+ interface-apache-website:
219+ source: https://github.com/juju-solutions/interface-apache-website
220+ source-commit: "2f736ebcc90d19ac142a2d898a2ec7e1aafaa13f"
221+ source-submodules: []
222+ source-type: git
223+ plugin: dump
224+ organize:
225+ "*": layers/interface/apache-website/
226+ stage:
227+ - layers
228+ prime:
229+ - "-layers"
230+ charm:
231+ after:
232+ - charm-wheels
233+ - launchpad-layers
234+ - interface-apache-website
235+ source: .
236+ plugin: reactive
237+ build-snaps: [charm]
238+ build-packages: [libpq-dev, python3-dev]
239+ build-environment:
240+ - CHARM_LAYERS_DIR: $CRAFT_STAGE/layers/layer
241+ - CHARM_INTERFACES_DIR: $CRAFT_STAGE/layers/interface
242+ - PIP_NO_INDEX: "true"
243+ - PIP_FIND_LINKS: $CRAFT_STAGE/charm-wheels
244+ reactive-charm-build-arguments: [--binary-wheels-from-source]
245diff --git a/charm/launchpad-codehosting/config.yaml b/charm/launchpad-codehosting/config.yaml
246new file mode 100644
247index 0000000..540b982
248--- /dev/null
249+++ b/charm/launchpad-codehosting/config.yaml
250@@ -0,0 +1,110 @@
251+options:
252+ active:
253+ type: boolean
254+ description: If true, enable jobs that may change the database.
255+ default: true
256+ codehosting_private_ssh_key:
257+ type: string
258+ description: >
259+ Base64-encoded private SSH RSA host key to be used by the codehosting
260+ service. Existing key pair, if any, will be deleted if this is unset.
261+ default: ""
262+ codehosting_public_ssh_key:
263+ type: string
264+ description: >
265+ Base64-encoded public SSH RSA host key to be used by the codehosting
266+ service. Existing key pair, if any, will be deleted if this is unset.
267+ default: ""
268+ domain_bzr_internal:
269+ type: string
270+ description: |
271+ The internal-only domain name to expose the bazaar 'get branch by ID'
272+ service on.
273+ haproxy_fe_server_options:
274+ type: string
275+ description: Options to add to the frontend HAProxy http(s) "server" lines.
276+ default: check inter 5000 rise 2 fall 5 maxconn 16
277+ haproxy_fe_server_options_ssh:
278+ type: string
279+ description: Options to add to the frontend HAProxy ssh "server" lines.
280+ default: check inter 2000 rise 2 fall 5 maxconn 200
281+ haproxy_server_options:
282+ type: string
283+ description: Options to add to the HAProxy http(s) "server" lines.
284+ default: check inter 2000 rise 2 fall 5 maxconn 16
285+ haproxy_service_https_cert_type:
286+ type: string
287+ description: |
288+ The type of certificate to use for the https service. Either "DEFAULT"
289+ or "EXTERNAL".
290+ default: "DEFAULT"
291+ haproxy_service_options_internal_branch_by_id:
292+ type: string
293+ description: |
294+ YAML-encoded list of options for the codehosting internal branch
295+ by ID service.
296+ default: |
297+ - mode http
298+ - option httplog
299+ - option httpchk HEAD / HTTP/1.0
300+ - option forwardfor
301+ - balance leastconn
302+ haproxy_service_options_http:
303+ type: string
304+ description: |
305+ YAML-encoded list of options for the codehosting http service.
306+ default: |
307+ - mode http
308+ - option httplog
309+ - option httpchk HEAD / HTTP/1.0
310+ - option forwardfor
311+ - balance leastconn
312+ haproxy_service_options_https:
313+ type: string
314+ description: |
315+ YAML-encoded list of options for the codehosting https service.
316+ default: |
317+ - mode http
318+ - option httplog
319+ - option httpchk HEAD / HTTP/1.0
320+ - option forwardfor
321+ - balance leastconn
322+ haproxy_service_options_ssh:
323+ type: string
324+ description: |
325+ YAML-encoded list of options for the codehosting ssh service.
326+ default: |
327+ - mode tcp
328+ - balance leastconn
329+ - option httpchk HEAD / HTTP/1.0
330+ - option tcpka
331+ - timeout queue 50
332+ - timeout connect 2000
333+ - "# Increased to 2 hours as per https://portal.admin.canonical.com/C47161"
334+ - timeout client 7200000
335+ - timeout server 7200000
336+ internal_codebrowse_root:
337+ type: string
338+ description: |
339+ The internal-only endpoint at which the codebrowse service is
340+ accessible.
341+ port_bzr_internal:
342+ type: int
343+ description: |
344+ The port to expose the bzr 'get branch by ID' service on.
345+ default: 8081
346+ port_bzr_sftp_base:
347+ type: int
348+ description: Base port number for the bzr-sftp service.
349+ default: 2224
350+ port_web_status_base:
351+ type: int
352+ description: Base port for the web status service.
353+ default: 8024
354+ workers:
355+ type: int
356+ description: >
357+ Number of bzr-sftp worker processes. If set, each worker will listen
358+ on consecutive ports starting from each of `port_bzr_sftp_base` and
359+ `port_web_status`, so make sure there is enough space between those.
360+ default: 1
361diff --git a/charm/launchpad-codehosting/layer.yaml b/charm/launchpad-codehosting/layer.yaml
362new file mode 100644
363index 0000000..35a531b
364--- /dev/null
365+++ b/charm/launchpad-codehosting/layer.yaml
366@@ -0,0 +1,15 @@
367+includes:
368+ - layer:launchpad-db
369+ - interface:apache-website
370+ - interface:http
371+options:
372+ ols-pg:
373+ databases:
374+ db:
375+ name: launchpad_dev
376+ roles:
377+ - branch-rewrite
378+ - reclaim-branch-space
379+ - translationstobranch
380+ - upgrade-branches
381+repo: https://git.launchpad.net/launchpad
382diff --git a/charm/launchpad-codehosting/metadata.yaml b/charm/launchpad-codehosting/metadata.yaml
383new file mode 100644
384index 0000000..2dae4e5
385--- /dev/null
386+++ b/charm/launchpad-codehosting/metadata.yaml
387@@ -0,0 +1,24 @@
388+name: launchpad-codehosting
389+display-name: launchpad-codehosting
390+summary: Launchpad Bazaar codehosting service
391+maintainer: Launchpad Developers <launchpad-dev@lists.launchpad.net>
392+description: |
393+ Launchpad is an open source suite of tools that help people and teams
394+ to work together on software projects.
395+
396+ This charm runs the Launchpad Bazaar codehosting service.
397+tags:
398+ # https://juju.is/docs/charm-metadata#heading--charm-store-fields
399+ - network
400+series:
401+ - focal
402+subordinate: true
403+requires:
404+ apache-website:
405+ interface: apache-website
406+ scope: container
407+provides:
408+ loadbalancer:
409+ interface: http
410+ frontend-loadbalancer:
411+ interface: http
412diff --git a/charm/launchpad-codehosting/reactive/launchpad-codehosting.py b/charm/launchpad-codehosting/reactive/launchpad-codehosting.py
413new file mode 100644
414index 0000000..3176027
415--- /dev/null
416+++ b/charm/launchpad-codehosting/reactive/launchpad-codehosting.py
417@@ -0,0 +1,439 @@
418+# Copyright 2023 Canonical Ltd. This software is licensed under the
419+# GNU Affero General Public License version 3 (see the file LICENSE).
420+
421+import base64
422+import os
423+import subprocess
424+
425+import yaml
426+from charmhelpers.core import hookenv, host, templating
427+from charms.launchpad.base import configure_email, get_service_config
428+from charms.launchpad.db import lazr_config_files
429+from charms.launchpad.payload import (
430+ config_file_path,
431+ configure_cron,
432+ configure_lazr,
433+)
434+from charms.reactive import (
435+ clear_flag,
436+ endpoint_from_flag,
437+ helpers,
438+ remove_state,
439+ set_flag,
440+ set_state,
441+ when,
442+ when_not,
443+ when_not_all,
444+)
445+from ols import base
446+
447+
448+def base64_decode(value):
449+ return base64.b64decode(value.encode("ASCII"))
450+
451+
452+def configure_logrotate(config):
453+ hookenv.log("Writing logrotate configuration.")
454+ templating.render(
455+ "logrotate.conf.j2",
456+ "/etc/logrotate.d/launchpad",
457+ config,
458+ perms=0o644,
459+ )
460+
461+
462+def configure_systemd(config):
463+ hookenv.log("Writing launchpad-bzr-sftp systemd service.")
464+ templating.render(
465+ "launchpad-bzr-sftp.service.j2",
466+ "/lib/systemd/system/launchpad-bzr-sftp.service",
467+ config,
468+ )
469+ templating.render(
470+ "launchpad-bzr-sftp@.service.j2",
471+ "/lib/systemd/system/launchpad-bzr-sftp@.service",
472+ config,
473+ )
474+ templating.render(
475+ "launchpad-bzr-sftp-generator.j2",
476+ "/lib/systemd/system-generators/launchpad-bzr-sftp-generator",
477+ config,
478+ perms=0o555,
479+ )
480+ subprocess.run(["systemctl", "daemon-reload"])
481+
482+
483+def config_files():
484+ files = []
485+ files.extend(lazr_config_files())
486+ config = get_service_config()
487+ files.append(config_file_path("launchpad-codehosting/launchpad-lazr.conf"))
488+ for i in range(config["workers"]):
489+ files.append(
490+ config_file_path(
491+ f"launchpad-codehosting{i + 1}/launchpad-lazr.conf"
492+ )
493+ )
494+ return files
495+
496+
497+def configure_scripts(config):
498+ hookenv.log(
499+ f"Creating {config['scripts_dir']}, if it doesn't exist already."
500+ )
501+ host.mkdir(
502+ config["scripts_dir"],
503+ owner=config["user"],
504+ group=config["user"],
505+ perms=0o755,
506+ force=True,
507+ )
508+ hookenv.log("Writing the cleanlogs script.")
509+ templating.render(
510+ "cleanlogs.j2",
511+ f"{config['scripts_dir']}/cleanlogs",
512+ config,
513+ perms=0o755,
514+ )
515+ host.mkdir(
516+ f"{config['logs_dir']}/sftp-logs",
517+ owner=config["user"],
518+ group=config["user"],
519+ perms=0o755,
520+ force=True,
521+ )
522+ hookenv.log("Writing the branch rewrite wrapper script.")
523+ templating.render(
524+ "rewrite_wrapper.sh.j2",
525+ f"{config['scripts_dir']}/rewrite_wrapper.sh",
526+ config,
527+ owner=config["user"],
528+ group=config["user"],
529+ perms=0o755,
530+ )
531+
532+
533+def configure_ssh_keys(config):
534+ host_key_pair_path = f"{config['base_dir']}/keys"
535+ hookenv.log(f"Creating {host_key_pair_path} to store the SSH host keys.")
536+ user = config["user"]
537+ host.mkdir(
538+ host_key_pair_path,
539+ owner=user,
540+ group=user,
541+ perms=0o755,
542+ force=True,
543+ )
544+ ssh_private_key_file = f"{host_key_pair_path}/ssh_host_key_rsa"
545+ ssh_public_key_file = f"{ssh_private_key_file}.pub"
546+ if (
547+ config["codehosting_private_ssh_key"]
548+ and config["codehosting_public_ssh_key"]
549+ ):
550+ hookenv.log("Writing the SSH host key pair.")
551+ host.write_file(
552+ ssh_private_key_file,
553+ base64_decode(config["codehosting_private_ssh_key"]),
554+ owner=user,
555+ group=user,
556+ perms=0o600,
557+ )
558+ host.write_file(
559+ ssh_public_key_file,
560+ base64_decode(config["codehosting_public_ssh_key"]),
561+ owner=user,
562+ group=user,
563+ perms=0o644,
564+ )
565+ else:
566+ hookenv.log(
567+ "SSH key pair not configured. Deleting existing keys, if present."
568+ )
569+ for path in (ssh_private_key_file, ssh_public_key_file):
570+ if os.path.exists(path):
571+ os.unlink(path)
572+
573+
574+def configure_codehosting_lazr_config(config):
575+ hookenv.log("Writing lazr configuration.")
576+ # XXX lgp171188: 2023-10-26: This template recreates the value of
577+ # config["bzr_repositories_root"] since it can't use it directly
578+ # due to it being in a separate handler that is executed much later.
579+ # Fix this to unify the definition and usage of this configuration
580+ # value.
581+ configure_lazr(
582+ config,
583+ "launchpad-codehosting-lazr-common.conf.j2",
584+ "launchpad-codehosting/launchpad-lazr.conf",
585+ )
586+ for i in range(config["workers"]):
587+ config["service_access_log_file"] = f"codehosting-{i + 1}-access.log"
588+ config["service_sftp_port"] = config["port_bzr_sftp_base"] + i
589+ config["service_web_status_port"] = config["port_web_status_base"] + i
590+ config["service_oops_prefix"] = f"{config['oops_prefix']}{i + 1}"
591+ configure_lazr(
592+ config,
593+ "launchpad-codehosting-sftp-lazr.conf.j2",
594+ f"launchpad-codehosting{i + 1}/launchpad-lazr.conf",
595+ )
596+
597+
598+@when("launchpad.db.configured")
599+@when_not("service.configured")
600+def configure():
601+ config = get_service_config()
602+ configure_codehosting_lazr_config(config)
603+ configure_email(config, "launchpad-codehosting")
604+ configure_logrotate(config)
605+ configure_cron(config, "crontab.j2")
606+ configure_scripts(config)
607+ configure_ssh_keys(config)
608+ configure_systemd(config)
609+ if config["active"]:
610+ if helpers.any_file_changed(
611+ [
612+ base.version_info_path(),
613+ "/lib/systemd/system/launchpad-bzr-sftp.service",
614+ "/lib/systemd/system/launchpad-bzr-sftp@.service",
615+ "/lib/systemd/system-generators/launchpad-bzr-sftp-generator",
616+ ]
617+ + config_files()
618+ ):
619+ hookenv.log(
620+ "Config files changed; restarting"
621+ " the launchpad-bzr-sftp service."
622+ )
623+ for i in range(config["workers"]):
624+ host.service_restart(f"launchpad-bzr-sftp@{i + 1}")
625+ else:
626+ hookenv.log("Not restarting since no config files were changed.")
627+ host.service_resume("launchpad-bzr-sftp.service")
628+
629+ set_state("service.configured")
630+
631+
632+def get_vhost_config(config):
633+ hookenv.log("Rendering the virtual hosts configuration.")
634+ return "\n".join(
635+ [
636+ templating.render("vhosts/common.conf", None, config),
637+ templating.render("vhosts/bazaar_http.conf.j2", None, config),
638+ templating.render("vhosts/bazaar_https.conf.j2", None, config),
639+ templating.render(
640+ "vhosts/bazaar_internal_branch_by_id.conf.j2", None, config
641+ ),
642+ ]
643+ )
644+
645+
646+def configure_document_root(config):
647+ hookenv.log("Configuring the document root.")
648+ user = config["user"]
649+ document_root = f"{config['base_dir']}/www"
650+ hookenv.log(f"Creating the document root directory {document_root}.")
651+ host.mkdir(
652+ document_root,
653+ owner=user,
654+ group=user,
655+ perms=0o755,
656+ force=True,
657+ )
658+ data_dir = f"{config['base_dir']}/data"
659+ hookenv.log(f"Creating the data directory {data_dir}")
660+ host.mkdir(data_dir, owner=user, group=user, perms=0o755, force=True)
661+ config["bzr_repositories_root"] = f"{data_dir}/mirrors"
662+ host.mkdir(
663+ config["bzr_repositories_root"],
664+ owner=user,
665+ group=user,
666+ perms=0o755,
667+ force=True,
668+ )
669+ code_dir = config["code_dir"]
670+ site_packages_dir = (
671+ subprocess.check_output(
672+ [
673+ f"{code_dir}/env/bin/python",
674+ "-c",
675+ "import sysconfig; print(sysconfig.get_path('purelib'))",
676+ ]
677+ )
678+ .strip()
679+ .decode("utf-8")
680+ )
681+ assert site_packages_dir.startswith(f"{code_dir}/env")
682+ config["loggerhead_static_dir"] = f"{site_packages_dir}/loggerhead/static"
683+
684+
685+@when(
686+ "service.configured",
687+ "config.set.domain_bzr",
688+ "config.set.domain_bzr_internal",
689+ "apache-website.available",
690+)
691+@when_not("service.apache-website.configured")
692+def configure_apache_website():
693+ apache_website = endpoint_from_flag("apache-website.available")
694+ config = get_service_config()
695+ configure_document_root(config)
696+ apache_website.set_remote(
697+ domain=config["domain_bzr"],
698+ enabled="true",
699+ ports=f"80 8080 {config['port_bzr_internal']}",
700+ site_config=get_vhost_config(config),
701+ site_modules="headers proxy proxy_http rewrite",
702+ )
703+ hookenv.status_set("active", "Ready")
704+ set_flag("service.apache-website.configured")
705+
706+
707+@when("service.apache-website.configured")
708+@when_not_all("service.configured", "apache-website.available")
709+def apache_deconfigured():
710+ hookenv.status_set("blocked", "Website not yet configured")
711+ clear_flag("service.apache-website.configured")
712+
713+
714+@when("service.configured")
715+@when_not("launchpad.db.configured")
716+def deconfigure():
717+ remove_state("service.configured")
718+
719+
720+@when("frontend-loadbalancer.available", "service.configured")
721+@when_not("launchpad-codehosting.frontend-loadbalancer.configured")
722+def configure_frontend_loadbalancer():
723+ config = hookenv.config()
724+ try:
725+ service_options_http = yaml.safe_load(
726+ config["haproxy_service_options_http"]
727+ )
728+ except yaml.YAMLError:
729+ hookenv.log("Could not parse haproxy_service_options_http yaml.")
730+ hookenv.status_set("blocked", "Bad haproxy_service_options_http value")
731+ return
732+ try:
733+ service_options_https = yaml.safe_load(
734+ config["haproxy_service_options_https"]
735+ )
736+ except yaml.YAMLError:
737+ hookenv.log("Could not parse haproxy_service_options_https yaml.")
738+ hookenv.status_set(
739+ "blocked", "Bad haproxy_service_options_https value"
740+ )
741+ return
742+ try:
743+ service_options_ssh = yaml.safe_load(
744+ config["haproxy_service_options_ssh"]
745+ )
746+ except yaml.YAMLError:
747+ hookenv.log("Could not parse haproxy_service_options_ssh yaml.")
748+ hookenv.status_set(
749+ "blocked", "Bad codehosting_lb_service_options_ssh value"
750+ )
751+ return
752+
753+ server_options = config["haproxy_fe_server_options"]
754+ ssh_server_options = config["haproxy_fe_server_options_ssh"]
755+
756+ unit_name = hookenv.local_unit().replace("/", "-")
757+ unit_ip = hookenv.unit_private_ip()
758+ services = [
759+ {
760+ "service_name": "launchpad-codehosting-http",
761+ "service_port": 80,
762+ "service_host": "0.0.0.0",
763+ "service_options": list(service_options_http),
764+ "servers": [
765+ [
766+ f"http_{unit_name}",
767+ unit_ip,
768+ 80,
769+ server_options,
770+ ],
771+ ],
772+ },
773+ {
774+ "service_name": "launchpad-codehosting-https",
775+ "service_port": 443,
776+ "service_host": "0.0.0.0",
777+ "service_options": [
778+ 'http-request set-header X-Forwarded-Scheme "https"'
779+ ]
780+ + list(service_options_https),
781+ "servers": [
782+ [
783+ f"https_{unit_name}",
784+ unit_ip,
785+ 8080,
786+ server_options,
787+ ],
788+ ],
789+ "crts": [config["haproxy_service_https_cert_type"]],
790+ },
791+ {
792+ "service_name": "launchpad-codehosting-ssh",
793+ "service_port": 922,
794+ "service_host": "0.0.0.0",
795+ "service_options": list(service_options_ssh),
796+ "servers": [
797+ [
798+ f"ssh_{unit_name}_{i + 1}",
799+ unit_ip,
800+ config["port_bzr_sftp_base"] + i,
801+ f"port {config['port_web_status_base'] + i} "
802+ + ssh_server_options,
803+ ]
804+ for i in range(config["workers"])
805+ ],
806+ },
807+ ]
808+ services_yaml = yaml.dump(services)
809+ for rel in hookenv.relations_of_type("frontend-loadbalancer"):
810+ hookenv.relation_set(rel["__relid__"], services=services_yaml)
811+
812+ set_state("launchpad-codehosting.frontend-loadbalancer.configured")
813+
814+
815+@when("loadbalancer.available", "service.configured")
816+@when_not("launchpad-codehosting.loadbalancer.configured")
817+def configure_loadbalancer():
818+ config = hookenv.config()
819+ try:
820+ service_options = yaml.safe_load(
821+ config["haproxy_service_options_internal_branch_by_id"]
822+ )
823+ except yaml.YAMLError:
824+ hookenv.log(
825+ "Could not parse "
826+ "haproxy_service_options_internal_branch_by_id yaml."
827+ )
828+ hookenv.status_set(
829+ "blocked",
830+ "Bad haproxy_service_options_internal_branch_by_id value",
831+ )
832+ return
833+ server_options = config["haproxy_server_options"]
834+ unit_name = hookenv.local_unit().replace("/", "-")
835+ unit_ip = hookenv.unit_private_ip()
836+ services = [
837+ {
838+ "service_name": "launchpad-codehosting-internal-branch-by-id",
839+ "service_port": config["port_bzr_internal"],
840+ "service_host": "0.0.0.0",
841+ "service_options": list(service_options),
842+ "servers": [
843+ [
844+ f"http_{unit_name}",
845+ unit_ip,
846+ config["port_bzr_internal"],
847+ server_options,
848+ ],
849+ ],
850+ },
851+ ]
852+ services_yaml = yaml.dump(services)
853+ for rel in hookenv.relations_of_type("loadbalancer"):
854+ hookenv.relation_set(rel["__relid__"], services=services_yaml)
855+
856+ set_state("launchpad-codehosting.loadbalancer.configured")
857diff --git a/charm/launchpad-codehosting/templates/cleanlogs.j2 b/charm/launchpad-codehosting/templates/cleanlogs.j2
858new file mode 100644
859index 0000000..30cbbe2
860--- /dev/null
861+++ b/charm/launchpad-codehosting/templates/cleanlogs.j2
862@@ -0,0 +1,16 @@
863+#!/bin/bash
864+
865+ROOTDIR={{ logs_dir }}/sftp-logs
866+DEST=$ROOTDIR/archives/$(date +%F)
867+
868+mkdir -p $DEST
869+find -L $ROOTDIR -maxdepth 1 -type f -name 'bzr-sftp?.log.*' -exec mv {} $DEST/ \;
870+
871+cd $DEST
872+for i in $(ls)
873+do
874+ gzip $i
875+done
876+
877+# Remove older than 90 days
878+find -L $ROOTDIR/archives -maxdepth 1 -type d -mtime +90 -print0 | xargs -0r rm -r
879diff --git a/charm/launchpad-codehosting/templates/crontab.j2 b/charm/launchpad-codehosting/templates/crontab.j2
880new file mode 100644
881index 0000000..b6ae48d
882--- /dev/null
883+++ b/charm/launchpad-codehosting/templates/crontab.j2
884@@ -0,0 +1,29 @@
885+TZ=UTC
886+MAILFROM={{ bounce_address }}
887+MAILTO={{ cron_mailto }}
888+LPCONFIG=launchpad-codehosting
889+
890+{%- if active %}
891+
892+* * * * * {% if http_proxy %}http_proxy={{ http_proxy }} https_proxy={{ http_proxy }}{% endif %}{{ code_dir }}/cronscripts/supermirror-pull.py -q --log-file=INFO:{{ logs_dir }}/puller.log
893+
894+# remove from disk, deleted branches
895+10 0 * * * {{ code_dir }}/cronscripts/process-job-source.py IReclaimBranchSpaceJobSource -q --log-file=DEBUG:{{ logs_dir }}/process-job-source.IReclaimBranchSpaceJobSource.log
896+
897+# Archive supermirror SFTP log files per https://portal.admin.canonical.com/C26974
898+5 0 * * * {{ scripts_dir }}/cleanlogs
899+
900+# Translations to branch script https://portal.admin.canonical.com/C35040
901+30 04 * * * {{ code_dir }}/cronscripts/translations-export-to-branch.py -q --log-file=DEBUG:{{ logs_dir }}/translations-export-to-branch.log
902+
903+# Upgrade branches script
904+*/10 * * * * {{ code_dir }}/cronscripts/process-job-source.py IBranchUpgradeJobSource -q --log-file=DEBUG:{{ logs_dir }}/process-job-source.IBranchUpgradeJobSource.log
905+
906+# cleanup old crud in /tmp cf. https://bugs.launchpad.net/launchpad/+bug/979511
907+6 4 * * * find /tmp -maxdepth 1 -name 'bzr-index-*' -type f -mtime +15 -delete
908+
909+{%- endif %}
910+
911+# OOPS amqp
912+*/15 * * * * {{ code_dir }}/bin/datedir2amqp --exchange oopses --host {{ rabbitmq_host }} --username {{ rabbitmq_username }} --password {{ rabbitmq_password }} --vhost {{ rabbitmq_vhost }} --repo {{ oopses_dir }} --key ""
913+
914diff --git a/charm/launchpad-codehosting/templates/launchpad-bzr-sftp-generator.j2 b/charm/launchpad-codehosting/templates/launchpad-bzr-sftp-generator.j2
915new file mode 100644
916index 0000000..af3670b
917--- /dev/null
918+++ b/charm/launchpad-codehosting/templates/launchpad-bzr-sftp-generator.j2
919@@ -0,0 +1,18 @@
920+#! /bin/sh
921+# Copyright 2023 Canonical Ltd. This software is licensed under the
922+# GNU Affero General Public License version 3 (see the file LICENSE).
923+
924+# Part of the launchpad-codehosting Juju charm.
925+
926+set -e
927+
928+wantdir="$1/launchpad-bzr-sftp.service.wants"
929+template=/lib/systemd/system/launchpad-bzr-sftp@.service
930+
931+# Generate systemd unit dependency symlinks for all configured
932+# launchpad-bzr-sftp instances.
933+mkdir -p "$wantdir"
934+for i in $(seq {{ workers }}); do
935+ ln -s "$template" "$wantdir/launchpad-bzr-sftp@$i.service"
936+done
937+
938diff --git a/charm/launchpad-codehosting/templates/launchpad-bzr-sftp.service.j2 b/charm/launchpad-codehosting/templates/launchpad-bzr-sftp.service.j2
939new file mode 100644
940index 0000000..36dec59
941--- /dev/null
942+++ b/charm/launchpad-codehosting/templates/launchpad-bzr-sftp.service.j2
943@@ -0,0 +1,18 @@
944+# This service is really a systemd target, but we use a service since
945+# targets cannot be reloaded. Starting, stopping, or reloading this service
946+# causes all the individual instances (defined in
947+# launchpad-bzr-sftp@.service) to be started, stopped, or reloaded
948+# respectively.
949+
950+[Unit]
951+Description=Launchpad bzr sftp
952+
953+[Service]
954+Type=oneshot
955+RemainAfterExit=yes
956+ExecStart=/bin/true
957+ExecReload=/bin/true
958+
959+[Install]
960+WantedBy=multi-user.target
961+
962diff --git a/charm/launchpad-codehosting/templates/launchpad-bzr-sftp@.service.j2 b/charm/launchpad-codehosting/templates/launchpad-bzr-sftp@.service.j2
963new file mode 100644
964index 0000000..d3c9b57
965--- /dev/null
966+++ b/charm/launchpad-codehosting/templates/launchpad-bzr-sftp@.service.j2
967@@ -0,0 +1,24 @@
968+[Unit]
969+Description=Launchpad bzr sftp service (%i)
970+PartOf=launchpad-bzr-sftp.service
971+Before=launchpad-bzr-sftp.service
972+ReloadPropagatedFrom=launchpad-bzr-sftp.service
973+After=network.target
974+ConditionPathExists=!{{ code_dir }}/maintenance.txt
975+
976+[Service]
977+User=launchpad
978+Group=launchpad
979+WorkingDirectory={{ code_dir }}
980+# https://portal.admin.canonical.com/C44221
981+Environment=LPCONFIG=launchpad-codehosting%i
982+SyslogIdentifier=bzr-sftp
983+ExecStart={{ code_dir }}/bin/twistd --python daemons/sftp.tac --pidfile {{ var_dir }}/bzr-sftp%i.pid --prefix bzr-sftp --logfile {{ logs_dir }}/sftp-logs/bzr-sftp%i.log --nodaemon
984+ExecReload=/bin/kill -USR1 $MAINPID
985+KillMode=mixed
986+Restart=on-failure
987+PrivateTmp=true
988+
989+[Install]
990+WantedBy=multi-user.target
991+
992diff --git a/charm/launchpad-codehosting/templates/launchpad-codehosting-lazr-common.conf.j2 b/charm/launchpad-codehosting/templates/launchpad-codehosting-lazr-common.conf.j2
993new file mode 100644
994index 0000000..63d59bd
995--- /dev/null
996+++ b/charm/launchpad-codehosting/templates/launchpad-codehosting-lazr-common.conf.j2
997@@ -0,0 +1,27 @@
998+# Public configuration data. The contents of this file may be freely shared
999+# with developers if needed for debugging.
1000+
1001+# A schema's sections, keys, and values are automatically inherited,
1002+# except for '.optional' sections. Update this config to override key
1003+# values. Values are strings, except for numbers that look like ints.
1004+# The tokens true, false, and none are treated as True, False, and None.
1005+
1006+{% from "macros.j2" import opt -%}
1007+
1008+[meta]
1009+extends: ../launchpad-db-lazr.conf
1010+
1011+[launchpad]
1012+launch: False
1013+
1014+[codehosting]
1015+blacklisted_hostnames: localhost,127.0.0.1
1016+host_key_pair_path: {{ base_dir }}/keys/
1017+{{- opt("internal_branch_by_id_root", internal_branch_by_id_root) }}
1018+{{- opt("internal_codebrowse_root", internal_codebrowse_root) }}
1019+mirrored_branches_root: {{ base_dir }}/data/mirrors
1020+rewrite_script_log_file: {{ logs_dir }}/rewrite.log
1021+
1022+[error_reports]
1023+error_dir: {{ var_dir }}/local-oopses-codehost
1024+
1025diff --git a/charm/launchpad-codehosting/templates/launchpad-codehosting-sftp-lazr.conf.j2 b/charm/launchpad-codehosting/templates/launchpad-codehosting-sftp-lazr.conf.j2
1026new file mode 100644
1027index 0000000..b4a9500
1028--- /dev/null
1029+++ b/charm/launchpad-codehosting/templates/launchpad-codehosting-sftp-lazr.conf.j2
1030@@ -0,0 +1,21 @@
1031+# Public configuration data. The contents of this file may be freely shared
1032+# with developers if needed for debugging.
1033+
1034+# A schema's sections, keys, and values are automatically inherited,
1035+# except for '.optional' sections. Update this config to override key
1036+# values. Values are strings, except for numbers that look like ints.
1037+# The tokens true, false, and none are treated as True, False, and None.
1038+
1039+{% from "macros.j2" import opt -%}
1040+
1041+[meta]
1042+extends: ../launchpad-codehosting/launchpad-lazr.conf
1043+
1044+[error_reports]
1045+oops_prefix: {{ service_oops_prefix }}
1046+
1047+[codehosting]
1048+access_log: {{ logs_dir }}/{{ service_access_log_file }}
1049+port: tcp:{{ service_sftp_port }}
1050+web_status_port: tcp:{{ service_web_status_port }}
1051+
1052diff --git a/charm/launchpad-codehosting/templates/logrotate.conf.j2 b/charm/launchpad-codehosting/templates/logrotate.conf.j2
1053new file mode 100644
1054index 0000000..1b87f30
1055--- /dev/null
1056+++ b/charm/launchpad-codehosting/templates/logrotate.conf.j2
1057@@ -0,0 +1,16 @@
1058+{{ logs_dir }}/puller.log {{ logs_dir }}/rewrite.log {
1059+ rotate 30
1060+ daily
1061+ dateext
1062+ compress
1063+ delaycompress
1064+}
1065+
1066+{{ logs_dir }}/codehosting-*-access.log {{ logs_dir }}/process-job-source.*.log {
1067+ rotate 90
1068+ daily
1069+ dateext
1070+ compress
1071+ delaycompress
1072+}
1073+
1074diff --git a/charm/launchpad-codehosting/templates/rewrite_wrapper.sh.j2 b/charm/launchpad-codehosting/templates/rewrite_wrapper.sh.j2
1075new file mode 100644
1076index 0000000..fab9c16
1077--- /dev/null
1078+++ b/charm/launchpad-codehosting/templates/rewrite_wrapper.sh.j2
1079@@ -0,0 +1,3 @@
1080+#!/bin/sh
1081+sudo -u {{ user }} LPCONFIG=launchpad-codehosting {{ code_dir }}/scripts/branch-rewrite.py
1082+
1083diff --git a/charm/launchpad-codehosting/templates/robots.txt b/charm/launchpad-codehosting/templates/robots.txt
1084new file mode 100644
1085index 0000000..e62cb38
1086--- /dev/null
1087+++ b/charm/launchpad-codehosting/templates/robots.txt
1088@@ -0,0 +1,4 @@
1089+# go away
1090+User-agent: *
1091+Disallow: /
1092+
1093diff --git a/charm/launchpad-codehosting/templates/vhosts/bazaar_http.conf.j2 b/charm/launchpad-codehosting/templates/vhosts/bazaar_http.conf.j2
1094new file mode 100644
1095index 0000000..69e65ce
1096--- /dev/null
1097+++ b/charm/launchpad-codehosting/templates/vhosts/bazaar_http.conf.j2
1098@@ -0,0 +1,79 @@
1099+<VirtualHost *:80>
1100+ ServerName {{ domain_bzr }}
1101+
1102+ CustomLog /var/log/apache2/{{ domain_bzr }}-access.log combined_timer
1103+ ErrorLog /var/log/apache2/{{ domain_bzr }}-error.log
1104+
1105+ ProxyRequests off
1106+ <Proxy *>
1107+ <RequireAll>
1108+ Require all granted
1109+ # Cowboy to stop Sogou web spider smashing bazaar, 13-03-18
1110+ Require expr %{HTTP_USER_AGENT} !~ /^Sogou web spider/
1111+ </RequireAll>
1112+
1113+ ErrorDocument 500 /offline.html
1114+ ErrorDocument 502 /offline.html
1115+ ErrorDocument 503 /offline.html
1116+ </Proxy>
1117+ ProxyTimeout 20
1118+
1119+ Alias /robots.txt {{ code_dir }}/lib/launchpad_loggerhead/static/robots.txt
1120+ Alias /favicon.ico {{ loggerhead_static_dir }}/images/favicon.ico
1121+ Alias /offline.html {{ code_dir }}/lib/canonical/launchpad/offline-unplanned.html
1122+ Alias /static {{ loggerhead_static_dir }}
1123+
1124+ <Directory {{ code_dir }}/lib/launchpad_loggerhead/static>
1125+ Options -Indexes +SymLinksIfOwnerMatch
1126+ AllowOverride None
1127+ Require all granted
1128+ </Directory>
1129+
1130+ <Directory {{ loggerhead_static_dir }}>
1131+ Options -Indexes +SymLinksIfOwnerMatch
1132+ AllowOverride None
1133+ Require all granted
1134+ </Directory>
1135+
1136+ <Directory {{ code_dir }}/lib/canonical/launchpad/>
1137+ Options -Indexes +SymLinksIfOwnerMatch
1138+ AllowOverride None
1139+ Require all granted
1140+ </Directory>
1141+
1142+ # Rewrite Logic Flow:
1143+ # (1) People hitting the frontpage (i.e. /) are redirected to the
1144+ # Launchpad frontpage
1145+ # (2) The /favicon.ico file is served normally.
1146+ # (3) The /robots.txt file is served normally.
1147+ # (4) The /offline.html file is served normally.
1148+ # (5) Any /static/* files are served normally.
1149+ # (6) Everything else is passed through the branch-rewrite wrapper script
1150+
1151+ RewriteEngine On
1152+ RewriteMap branch-rewrite prg:{{ scripts_dir }}/rewrite_wrapper.sh
1153+
1154+ # (1) / -> Launchpad frontpage
1155+ RewriteRule ^/$ https://{{ domain }} [L]
1156+ # (2) The /favicon.ico file is served normally.
1157+ RewriteRule ^/favicon.ico$ - [L]
1158+ # (3) The /robots.txt file is served normally.
1159+ RewriteRule ^/robots.txt$ - [L]
1160+ # (4) The /offline.html file is served normally.
1161+ RewriteRule ^/offline.html$ - [L]
1162+ # (5) Any /static/* files are served normally.
1163+ RewriteRule ^/static/(.*)$ - [L]
1164+
1165+ # (6) The branch-rewrite wrapper script. This sets the appropriate ENV
1166+ # setting(s) necessary.
1167+ RewriteMap escape int:escape
1168+ RewriteMap unescape int:unescape
1169+ RewriteRule ^(/.*)$ ${unescape:${branch-rewrite:${escape:$1}}} [L,P]
1170+
1171+ # https://portal.admin.canonical.com/C79782 yes this looks mad but it
1172+ # is just making NOOP proxypass commands so
1173+ # the rewrite [P] lines have persistent pools to work with.
1174+ ProxyPass / !
1175+ ProxyPass / http://{{ domain_bzr }}/
1176+</VirtualHost>
1177+
1178diff --git a/charm/launchpad-codehosting/templates/vhosts/bazaar_https.conf.j2 b/charm/launchpad-codehosting/templates/vhosts/bazaar_https.conf.j2
1179new file mode 100644
1180index 0000000..e531b50
1181--- /dev/null
1182+++ b/charm/launchpad-codehosting/templates/vhosts/bazaar_https.conf.j2
1183@@ -0,0 +1,38 @@
1184+<VirtualHost *:8080>
1185+ ServerName {{ domain_bzr }}
1186+ ServerAdmin webmaster@launchpad.net
1187+
1188+ CustomLog /var/log/apache2/{{ domain_bzr }}-access.log combined_timer
1189+ ErrorLog /var/log/apache2/{{ domain_bzr }}-error.log
1190+
1191+ # This virtual host serves a few files/statics only; all else is reverse
1192+ # proxy to codebrowse.
1193+ DocumentRoot {{ loggerhead_static_dir }}
1194+
1195+ <Directory {{ loggerhead_static_dir }}>
1196+ Options -Indexes +SymLinksIfOwnerMatch
1197+ Require all granted
1198+ </Directory>
1199+
1200+ ProxyRequests off
1201+ <Proxy *>
1202+ <RequireAll>
1203+ Require all granted
1204+ # Cowboy to stop Sogou web spider smashing bazaar, 13-03-18
1205+ Require expr %{HTTP_USER_AGENT} !~ /^Sogou web spider/
1206+ </RequireAll>
1207+ ErrorDocument 500 /offline.html
1208+ ErrorDocument 502 /offline.html
1209+ ErrorDocument 503 /offline.html
1210+ </Proxy>
1211+ ProxyTimeout 20
1212+ ProxyPassReverse / {{ internal_codebrowse_root }}
1213+
1214+ RewriteEngine On
1215+ RewriteRule ^/offline.html$ - [L]
1216+ RewriteRule ^/robots.txt$ - [L]
1217+ RewriteRule ^/favicon.ico$ {{ loggerhead_static_dir }}/images/favicon.ico [L]
1218+ RewriteRule ^/static/(.*)$ /$1 [L]
1219+ RewriteRule ^/(.*)$ {{ codebrowse_internal_endpoint }}/$1 [P,L]
1220+</VirtualHost>
1221+
1222diff --git a/charm/launchpad-codehosting/templates/vhosts/bazaar_internal_branch_by_id.conf.j2 b/charm/launchpad-codehosting/templates/vhosts/bazaar_internal_branch_by_id.conf.j2
1223new file mode 100644
1224index 0000000..84b68d3
1225--- /dev/null
1226+++ b/charm/launchpad-codehosting/templates/vhosts/bazaar_internal_branch_by_id.conf.j2
1227@@ -0,0 +1,18 @@
1228+<VirtualHost *:{{ port_bzr_internal }}>
1229+ ServerName {{ domain_bzr_internal }}
1230+
1231+ CustomLog /var/log/apache2/{{ domain_bzr_internal }}-access.log combined_timer
1232+ ErrorLog /var/log/apache2/{{ domain_bzr_internal }}-error.log
1233+
1234+ # Default location is for all the .bzr repositories in mirrors/*
1235+ # We serve .bzr/* via http or bzr+ssh; not https
1236+ DocumentRoot {{ bzr_repositories_root }}
1237+
1238+ <Directory {{ bzr_repositories_root }}>
1239+ Options SymLinksIfOwnerMatch Indexes
1240+ AllowOverride None
1241+ Require all granted
1242+ </Directory>
1243+
1244+</VirtualHost>
1245+
1246diff --git a/charm/launchpad-codehosting/templates/vhosts/common.conf b/charm/launchpad-codehosting/templates/vhosts/common.conf
1247new file mode 100644
1248index 0000000..bea1e98
1249--- /dev/null
1250+++ b/charm/launchpad-codehosting/templates/vhosts/common.conf
1251@@ -0,0 +1,6 @@
1252+# Needed outside VirtualHost config
1253+LogFormat "%h %D %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined_timer
1254+
1255+# For debugging ONLY - do not turn on in normal operation
1256+# LogLevel alert rewrite:trace4
1257+

Subscribers

People subscribed via source and target branches

to status/vote changes: