Merge ~lgp171188/launchpad:charm-launchpad-codehosting into launchpad:master
- Git
- lp:~lgp171188/launchpad
- charm-launchpad-codehosting
- Merge into 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) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Colin Watson (community) | Approve | ||
Review via email: mp+452140@code.launchpad.net |
Commit message
Add a launchpad-
Description of the change
To post a comment you must log in.
Revision history for this message
Guruprasad (lgp171188) : | # |
Revision history for this message
Guruprasad (lgp171188) wrote : | # |
Revision history for this message
Colin Watson (cjwatson) wrote : | # |
Good start! I deployed my `launchpad-
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-
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
1 | diff --git a/charm/launchpad-codehosting/README.md b/charm/launchpad-codehosting/README.md |
2 | new file mode 100644 |
3 | index 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 |
71 | diff --git a/charm/launchpad-codehosting/actions.yaml b/charm/launchpad-codehosting/actions.yaml |
72 | new file mode 100644 |
73 | index 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.) |
88 | diff --git a/charm/launchpad-codehosting/actions/actions.py b/charm/launchpad-codehosting/actions/actions.py |
89 | new file mode 100755 |
90 | index 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) |
147 | diff --git a/charm/launchpad-codehosting/actions/start-services b/charm/launchpad-codehosting/actions/start-services |
148 | new file mode 120000 |
149 | index 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 |
155 | diff --git a/charm/launchpad-codehosting/actions/stop-services b/charm/launchpad-codehosting/actions/stop-services |
156 | new file mode 120000 |
157 | index 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 |
163 | diff --git a/charm/launchpad-codehosting/charmcraft.yaml b/charm/launchpad-codehosting/charmcraft.yaml |
164 | new file mode 100644 |
165 | index 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] |
245 | diff --git a/charm/launchpad-codehosting/config.yaml b/charm/launchpad-codehosting/config.yaml |
246 | new file mode 100644 |
247 | index 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 |
361 | diff --git a/charm/launchpad-codehosting/layer.yaml b/charm/launchpad-codehosting/layer.yaml |
362 | new file mode 100644 |
363 | index 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 |
382 | diff --git a/charm/launchpad-codehosting/metadata.yaml b/charm/launchpad-codehosting/metadata.yaml |
383 | new file mode 100644 |
384 | index 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 |
412 | diff --git a/charm/launchpad-codehosting/reactive/launchpad-codehosting.py b/charm/launchpad-codehosting/reactive/launchpad-codehosting.py |
413 | new file mode 100644 |
414 | index 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") |
857 | diff --git a/charm/launchpad-codehosting/templates/cleanlogs.j2 b/charm/launchpad-codehosting/templates/cleanlogs.j2 |
858 | new file mode 100644 |
859 | index 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 |
879 | diff --git a/charm/launchpad-codehosting/templates/crontab.j2 b/charm/launchpad-codehosting/templates/crontab.j2 |
880 | new file mode 100644 |
881 | index 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 | + |
914 | diff --git a/charm/launchpad-codehosting/templates/launchpad-bzr-sftp-generator.j2 b/charm/launchpad-codehosting/templates/launchpad-bzr-sftp-generator.j2 |
915 | new file mode 100644 |
916 | index 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 | + |
938 | diff --git a/charm/launchpad-codehosting/templates/launchpad-bzr-sftp.service.j2 b/charm/launchpad-codehosting/templates/launchpad-bzr-sftp.service.j2 |
939 | new file mode 100644 |
940 | index 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 | + |
962 | diff --git a/charm/launchpad-codehosting/templates/launchpad-bzr-sftp@.service.j2 b/charm/launchpad-codehosting/templates/launchpad-bzr-sftp@.service.j2 |
963 | new file mode 100644 |
964 | index 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 | + |
992 | diff --git a/charm/launchpad-codehosting/templates/launchpad-codehosting-lazr-common.conf.j2 b/charm/launchpad-codehosting/templates/launchpad-codehosting-lazr-common.conf.j2 |
993 | new file mode 100644 |
994 | index 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 | + |
1025 | diff --git a/charm/launchpad-codehosting/templates/launchpad-codehosting-sftp-lazr.conf.j2 b/charm/launchpad-codehosting/templates/launchpad-codehosting-sftp-lazr.conf.j2 |
1026 | new file mode 100644 |
1027 | index 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 | + |
1052 | diff --git a/charm/launchpad-codehosting/templates/logrotate.conf.j2 b/charm/launchpad-codehosting/templates/logrotate.conf.j2 |
1053 | new file mode 100644 |
1054 | index 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 | + |
1074 | diff --git a/charm/launchpad-codehosting/templates/rewrite_wrapper.sh.j2 b/charm/launchpad-codehosting/templates/rewrite_wrapper.sh.j2 |
1075 | new file mode 100644 |
1076 | index 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 | + |
1083 | diff --git a/charm/launchpad-codehosting/templates/robots.txt b/charm/launchpad-codehosting/templates/robots.txt |
1084 | new file mode 100644 |
1085 | index 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 | + |
1093 | diff --git a/charm/launchpad-codehosting/templates/vhosts/bazaar_http.conf.j2 b/charm/launchpad-codehosting/templates/vhosts/bazaar_http.conf.j2 |
1094 | new file mode 100644 |
1095 | index 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 | + |
1178 | diff --git a/charm/launchpad-codehosting/templates/vhosts/bazaar_https.conf.j2 b/charm/launchpad-codehosting/templates/vhosts/bazaar_https.conf.j2 |
1179 | new file mode 100644 |
1180 | index 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 | + |
1222 | diff --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 |
1223 | new file mode 100644 |
1224 | index 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 | + |
1246 | diff --git a/charm/launchpad-codehosting/templates/vhosts/common.conf b/charm/launchpad-codehosting/templates/vhosts/common.conf |
1247 | new file mode 100644 |
1248 | index 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 | + |
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.