Merge ~cjwatson/launchpad:charm-loggerhead into launchpad:master
- Git
- lp:~cjwatson/launchpad
- charm-loggerhead
- Merge into master
Proposed by
Colin Watson
Status: | Merged |
---|---|
Approved by: | Colin Watson |
Approved revision: | 9182602aa4cb8664b6a483df59d9906f7de44f01 |
Merge reported by: | Otto Co-Pilot |
Merged at revision: | not available |
Proposed branch: | ~cjwatson/launchpad:charm-loggerhead |
Merge into: | launchpad:master |
Diff against target: |
498 lines (+438/-0) 10 files modified
charm/launchpad-loggerhead/README.md (+7/-0) charm/launchpad-loggerhead/charmcraft.yaml (+75/-0) charm/launchpad-loggerhead/config.yaml (+43/-0) charm/launchpad-loggerhead/layer.yaml (+5/-0) charm/launchpad-loggerhead/metadata.yaml (+18/-0) charm/launchpad-loggerhead/reactive/launchpad-loggerhead.py (+219/-0) charm/launchpad-loggerhead/templates/crontab.j2 (+10/-0) charm/launchpad-loggerhead/templates/launchpad-loggerhead-lazr.conf (+24/-0) charm/launchpad-loggerhead/templates/launchpad-loggerhead.service.j2 (+22/-0) charm/launchpad-loggerhead/templates/logrotate.conf.j2 (+15/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Guruprasad | Approve | ||
Review via email: mp+453470@code.launchpad.net |
Commit message
charm: Add launchpad-
Description of the change
To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/charm/launchpad-loggerhead/README.md b/charm/launchpad-loggerhead/README.md |
2 | new file mode 100644 |
3 | index 0000000..92efbe0 |
4 | --- /dev/null |
5 | +++ b/charm/launchpad-loggerhead/README.md |
6 | @@ -0,0 +1,7 @@ |
7 | +# Launchpad Bazaar/Breezy code browsing server |
8 | + |
9 | +This charm runs a code browsing server for Bazaar/Breezy branches. |
10 | + |
11 | +You will need the following relations: |
12 | + |
13 | + juju relate launchpad-loggerhead rabbitmq-server |
14 | diff --git a/charm/launchpad-loggerhead/charmcraft.yaml b/charm/launchpad-loggerhead/charmcraft.yaml |
15 | new file mode 100644 |
16 | index 0000000..71dcf7f |
17 | --- /dev/null |
18 | +++ b/charm/launchpad-loggerhead/charmcraft.yaml |
19 | @@ -0,0 +1,75 @@ |
20 | +type: charm |
21 | +bases: |
22 | + - build-on: |
23 | + - name: ubuntu |
24 | + channel: "20.04" |
25 | + architectures: [amd64] |
26 | + run-on: |
27 | + - name: ubuntu |
28 | + channel: "20.04" |
29 | + architectures: [amd64] |
30 | +parts: |
31 | + charm-wheels: |
32 | + source: https://git.launchpad.net/~ubuntuone-hackers/ols-charm-deps/+git/wheels |
33 | + source-commit: "42c89d9c66dbe137139b047fd54aed49b66d1a5e" |
34 | + source-submodules: [] |
35 | + source-type: git |
36 | + plugin: dump |
37 | + organize: |
38 | + "*": charm-wheels/ |
39 | + prime: |
40 | + - "-charm-wheels" |
41 | + ols-layers: |
42 | + source: https://git.launchpad.net/ols-charm-deps |
43 | + source-commit: "9c59a9804f1f40e2a74be7dac9bf18a655a7864f" |
44 | + source-submodules: [] |
45 | + source-type: git |
46 | + plugin: dump |
47 | + organize: |
48 | + "*": layers/ |
49 | + stage: |
50 | + - layers |
51 | + prime: |
52 | + - "-layers" |
53 | + launchpad-layers: |
54 | + after: |
55 | + - ols-layers |
56 | + source: https://git.launchpad.net/launchpad-layers |
57 | + source-commit: "58edb3e5a88794c3baa2274a94e21d3a298a6c79" |
58 | + source-submodules: [] |
59 | + source-type: git |
60 | + plugin: dump |
61 | + organize: |
62 | + launchpad-base: layers/layer/launchpad-base |
63 | + launchpad-payload: layers/layer/launchpad-payload |
64 | + stage: |
65 | + - layers |
66 | + prime: |
67 | + - "-layers" |
68 | + layer-coordinator: |
69 | + source: https://git.launchpad.net/layer-coordinator |
70 | + source-commit: "fa27fc93e0b08000963e83a6bfe49812d890dfcf" |
71 | + source-submodules: [] |
72 | + source-type: git |
73 | + plugin: dump |
74 | + organize: |
75 | + "*": layers/layer/coordinator/ |
76 | + stage: |
77 | + - layers |
78 | + prime: |
79 | + - "-layers" |
80 | + charm: |
81 | + after: |
82 | + - charm-wheels |
83 | + - launchpad-layers |
84 | + - layer-coordinator |
85 | + source: . |
86 | + plugin: reactive |
87 | + build-snaps: [charm] |
88 | + build-packages: [libpq-dev, python3-dev] |
89 | + build-environment: |
90 | + - CHARM_LAYERS_DIR: $CRAFT_STAGE/layers/layer |
91 | + - CHARM_INTERFACES_DIR: $CRAFT_STAGE/layers/interface |
92 | + - PIP_NO_INDEX: "true" |
93 | + - PIP_FIND_LINKS: $CRAFT_STAGE/charm-wheels |
94 | + reactive-charm-build-arguments: [--binary-wheels-from-source] |
95 | diff --git a/charm/launchpad-loggerhead/config.yaml b/charm/launchpad-loggerhead/config.yaml |
96 | new file mode 100644 |
97 | index 0000000..3145bff |
98 | --- /dev/null |
99 | +++ b/charm/launchpad-loggerhead/config.yaml |
100 | @@ -0,0 +1,43 @@ |
101 | +options: |
102 | + haproxy_server_options: |
103 | + type: string |
104 | + description: Options to add to HAProxy "server" lines. |
105 | + default: check inter 10000 rise 2 fall 2 maxconn 15 |
106 | + haproxy_service_options: |
107 | + type: string |
108 | + description: HAProxy options for codebrowse services. |
109 | + default: | |
110 | + - mode http |
111 | + - option httplog |
112 | + - option httpchk GET /robots.txt HTTP/1.0 |
113 | + - option forwardfor |
114 | + - balance leastconn |
115 | + internal_branch_by_id_root: |
116 | + type: string |
117 | + description: | |
118 | + The URL prefix for where branches are served by URLs based on the |
119 | + branch ID. |
120 | + default: |
121 | + nagios_check_branch: |
122 | + type: string |
123 | + description: If set, add Nagios checks for this branch. |
124 | + default: "" |
125 | + port_loggerhead: |
126 | + type: int |
127 | + description: > |
128 | + Port to expose to the public (indirectly; we expect Apache on the |
129 | + Bazaar codehosting system to ProxyPass to this port). This serves |
130 | + both public and private branches, but requests for private branches |
131 | + must be authenticated. |
132 | + default: 10007 |
133 | + port_loggerhead_api: |
134 | + type: int |
135 | + description: > |
136 | + Private port for read-only API requests. This must not be exposed to |
137 | + the public; other parts of Launchpad with access to this port must |
138 | + ensure that the appropriate security checks are performed. |
139 | + default: 10017 |
140 | + session_secret: |
141 | + type: string |
142 | + description: A base64-encoded secret key used to sign session cookies. |
143 | + default: "" |
144 | diff --git a/charm/launchpad-loggerhead/layer.yaml b/charm/launchpad-loggerhead/layer.yaml |
145 | new file mode 100644 |
146 | index 0000000..5743ccb |
147 | --- /dev/null |
148 | +++ b/charm/launchpad-loggerhead/layer.yaml |
149 | @@ -0,0 +1,5 @@ |
150 | +includes: |
151 | + - layer:launchpad-base |
152 | + - layer:coordinator |
153 | + - interface:http |
154 | +repo: https://git.launchpad.net/launchpad |
155 | diff --git a/charm/launchpad-loggerhead/metadata.yaml b/charm/launchpad-loggerhead/metadata.yaml |
156 | new file mode 100644 |
157 | index 0000000..186e666 |
158 | --- /dev/null |
159 | +++ b/charm/launchpad-loggerhead/metadata.yaml |
160 | @@ -0,0 +1,18 @@ |
161 | +name: launchpad-loggerhead |
162 | +display-name: launchpad-loggerhead |
163 | +summary: Launchpad Bazaar/Breezy code browsing server |
164 | +maintainer: Launchpad Developers <launchpad-dev@lists.launchpad.net> |
165 | +description: | |
166 | + Launchpad is an open source suite of tools that help people and teams |
167 | + to work together on software projects. |
168 | + |
169 | + This charm runs a code browsing server for Bazaar/Breezy branches. |
170 | +tags: |
171 | + # https://juju.is/docs/charm-metadata#heading--charm-store-fields |
172 | + - network |
173 | +series: |
174 | + - focal |
175 | +subordinate: false |
176 | +provides: |
177 | + loadbalancer: |
178 | + interface: http |
179 | diff --git a/charm/launchpad-loggerhead/reactive/launchpad-loggerhead.py b/charm/launchpad-loggerhead/reactive/launchpad-loggerhead.py |
180 | new file mode 100644 |
181 | index 0000000..e12dd1a |
182 | --- /dev/null |
183 | +++ b/charm/launchpad-loggerhead/reactive/launchpad-loggerhead.py |
184 | @@ -0,0 +1,219 @@ |
185 | +# Copyright 2023 Canonical Ltd. This software is licensed under the |
186 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
187 | + |
188 | +import base64 |
189 | +import os.path |
190 | +import subprocess |
191 | + |
192 | +import yaml |
193 | +from charmhelpers.core import hookenv, host, templating |
194 | +from charms.coordinator import acquire |
195 | +from charms.launchpad.base import ( |
196 | + get_service_config, |
197 | + lazr_config_files, |
198 | + secrets_dir, |
199 | +) |
200 | +from charms.launchpad.payload import ( |
201 | + config_file_path, |
202 | + configure_cron, |
203 | + configure_lazr, |
204 | +) |
205 | +from charms.reactive import ( |
206 | + clear_flag, |
207 | + endpoint_from_flag, |
208 | + helpers, |
209 | + set_flag, |
210 | + when, |
211 | + when_none, |
212 | + when_not, |
213 | + when_not_all, |
214 | +) |
215 | +from ols import base |
216 | + |
217 | + |
218 | +@host.restart_on_change( |
219 | + { |
220 | + "/lib/systemd/system/launchpad-loggerhead.service": [ |
221 | + "launchpad-loggerhead.service" |
222 | + ], |
223 | + }, |
224 | +) |
225 | +def configure_systemd(config): |
226 | + hookenv.log("Writing systemd service.") |
227 | + templating.render( |
228 | + "launchpad-loggerhead.service.j2", |
229 | + "/lib/systemd/system/launchpad-loggerhead.service", |
230 | + config, |
231 | + ) |
232 | + subprocess.run(["systemctl", "daemon-reload"], check=True) |
233 | + |
234 | + |
235 | +def configure_logrotate(config): |
236 | + hookenv.log("Writing logrotate configuration.") |
237 | + templating.render( |
238 | + "logrotate.conf.j2", |
239 | + "/etc/logrotate.d/loggerhead", |
240 | + config, |
241 | + perms=0o644, |
242 | + ) |
243 | + |
244 | + |
245 | +def session_secret_path(): |
246 | + return os.path.join(secrets_dir(), "cookies.hmac") |
247 | + |
248 | + |
249 | +def configure_session_secret(config): |
250 | + session_secret = base64.b64decode(config["session_secret"].encode()) |
251 | + host.write_file( |
252 | + session_secret_path(), session_secret, group=base.user(), perms=0o440 |
253 | + ) |
254 | + |
255 | + |
256 | +def config_files(): |
257 | + files = [] |
258 | + files.extend(lazr_config_files()) |
259 | + files.append(config_file_path("launchpad-loggerhead/launchpad-lazr.conf")) |
260 | + files.append(session_secret_path()) |
261 | + return files |
262 | + |
263 | + |
264 | +@when( |
265 | + "config.set.domain_bzr", |
266 | + "config.set.session_secret", |
267 | + "launchpad.base.configured", |
268 | +) |
269 | +@when_none("coordinator.requested.restart", "service.configured") |
270 | +def configure(): |
271 | + config = get_service_config() |
272 | + config["cache_dir"] = os.path.join(base.base_dir(), "cache") |
273 | + host.mkdir( |
274 | + config["cache_dir"], owner=base.user(), group=base.user(), perms=0o700 |
275 | + ) |
276 | + configure_lazr( |
277 | + config, |
278 | + "launchpad-loggerhead-lazr.conf", |
279 | + "launchpad-loggerhead/launchpad-lazr.conf", |
280 | + ) |
281 | + configure_systemd(config) |
282 | + configure_logrotate(config) |
283 | + configure_cron(config, "crontab.j2") |
284 | + configure_session_secret(config) |
285 | + |
286 | + if helpers.any_file_changed( |
287 | + [ |
288 | + base.version_info_path(), |
289 | + "/lib/systemd/system/launchpad-loggerhead.service", |
290 | + ] |
291 | + + config_files() |
292 | + ): |
293 | + hookenv.log("Config files changed; waiting for restart lock") |
294 | + acquire("restart") |
295 | + else: |
296 | + hookenv.log("Not restarting, since no config files were changed") |
297 | + set_flag("service.configured") |
298 | + |
299 | + |
300 | +@when("coordinator.granted.restart") |
301 | +def restart(): |
302 | + hookenv.log("Restarting application server") |
303 | + host.service_restart("launchpad-loggerhead.service") |
304 | + set_flag("service.configured") |
305 | + |
306 | + |
307 | +@when("service.configured") |
308 | +def check_is_running(): |
309 | + hookenv.status_set("active", "Ready") |
310 | + |
311 | + |
312 | +@when("service.configured") |
313 | +@when_not_all( |
314 | + "config.set.domain_bzr", |
315 | + "config.set.session_secret", |
316 | + "launchpad.base.configured", |
317 | +) |
318 | +def deconfigure(): |
319 | + clear_flag("service.configured") |
320 | + |
321 | + |
322 | +@when("nrpe-external-master.available", "service.configured") |
323 | +@when_not("launchpad.loggerhead.nrpe-external-master.published") |
324 | +def nrpe_available(): |
325 | + nrpe = endpoint_from_flag("nrpe-external-master.available") |
326 | + config = hookenv.config() |
327 | + if config["nagios_check_branch"]: |
328 | + nrpe.add_check( |
329 | + [ |
330 | + "/usr/lib/nagios/plugins/check_http", |
331 | + "-H", |
332 | + "localhost", |
333 | + "-p", |
334 | + str(config["port_loggerhead"]), |
335 | + "-u", |
336 | + f"{config['nagios_check_branch']}/files", |
337 | + ], |
338 | + name="check_launchpad_loggerhead", |
339 | + description="Launchpad loggerhead", |
340 | + context=config["nagios_context"], |
341 | + ) |
342 | + set_flag("launchpad.loggerhead.nrpe-external-master.published") |
343 | + |
344 | + |
345 | +@when("launchpad.loggerhead.nrpe-external-master.published") |
346 | +@when_not("nrpe-external-master.available") |
347 | +def nrpe_unavailable(): |
348 | + clear_flag("launchpad.loggerhead.nrpe-external-master.published") |
349 | + |
350 | + |
351 | +@when("loadbalancer.available", "service.configured") |
352 | +@when_not("launchpad.loadbalancer.configured") |
353 | +def configure_loadbalancer(): |
354 | + config = hookenv.config() |
355 | + |
356 | + try: |
357 | + service_options = yaml.safe_load(config["haproxy_service_options"]) |
358 | + except yaml.YAMLError: |
359 | + hookenv.log("Could not parse haproxy_service_options YAML") |
360 | + hookenv.status_set( |
361 | + "blocked", "Bad haproxy_service_options YAML configuration" |
362 | + ) |
363 | + return |
364 | + server_options = config["haproxy_server_options"] |
365 | + |
366 | + unit_name = hookenv.local_unit().replace("/", "-") |
367 | + unit_ip = hookenv.unit_private_ip() |
368 | + services = [ |
369 | + { |
370 | + "service_name": "launchpad-loggerhead", |
371 | + "service_port": config["port_loggerhead"], |
372 | + "service_host": "0.0.0.0", |
373 | + "service_options": list(service_options), |
374 | + "servers": [ |
375 | + [ |
376 | + f"public_{unit_name}", |
377 | + unit_ip, |
378 | + config["port_loggerhead"], |
379 | + server_options, |
380 | + ] |
381 | + ], |
382 | + }, |
383 | + { |
384 | + "service_name": "launchpad-loggerhead-api", |
385 | + "service_port": config["port_loggerhead_api"], |
386 | + "service_host": "0.0.0.0", |
387 | + "service_options": list(service_options), |
388 | + "servers": [ |
389 | + [ |
390 | + f"public_{unit_name}", |
391 | + unit_ip, |
392 | + config["port_loggerhead_api"], |
393 | + server_options, |
394 | + ] |
395 | + ], |
396 | + }, |
397 | + ] |
398 | + services_yaml = yaml.dump(services) |
399 | + |
400 | + for rel in hookenv.relations_of_type("loadbalancer"): |
401 | + hookenv.relation_set(rel["__relid__"], services=services_yaml) |
402 | + |
403 | + set_flag("launchpad.loadbalancer.configured") |
404 | diff --git a/charm/launchpad-loggerhead/templates/crontab.j2 b/charm/launchpad-loggerhead/templates/crontab.j2 |
405 | new file mode 100644 |
406 | index 0000000..0a532ba |
407 | --- /dev/null |
408 | +++ b/charm/launchpad-loggerhead/templates/crontab.j2 |
409 | @@ -0,0 +1,10 @@ |
410 | +TZ=UTC |
411 | +MAILTO={{ cron_mailto }} |
412 | + |
413 | +# Clean up cache directory. |
414 | +25 0 * * * find {{ cache_dir }} -maxdepth 1 -type d -mtime +240 -execdir rm -rf {} + |
415 | + |
416 | +# Catch up with publishing OOPSes that were temporarily spooled to disk due |
417 | +# to RabbitMQ being unavailable. |
418 | +*/15 * * * * {{ code_dir }}/bin/datedir2amqp --exchange oopses --host {{ rabbitmq_host }} --username {{ rabbitmq_username }} --password {{ rabbitmq_password }} --vhost {{ rabbitmq_vhost }} --repo {{ oopses_dir }} --key "" |
419 | + |
420 | diff --git a/charm/launchpad-loggerhead/templates/launchpad-loggerhead-lazr.conf b/charm/launchpad-loggerhead/templates/launchpad-loggerhead-lazr.conf |
421 | new file mode 100644 |
422 | index 0000000..d1212e9 |
423 | --- /dev/null |
424 | +++ b/charm/launchpad-loggerhead/templates/launchpad-loggerhead-lazr.conf |
425 | @@ -0,0 +1,24 @@ |
426 | +# Public configuration data. The contents of this file may be freely shared |
427 | +# with developers if needed for debugging. |
428 | + |
429 | +# A schema's sections, keys, and values are automatically inherited, except |
430 | +# for '.optional' sections. Update this config to override key values. |
431 | +# Values are strings, except for numbers that look like ints. The tokens |
432 | +# true, false, and none are treated as True, False, and None. |
433 | + |
434 | +{% from "macros.j2" import opt -%} |
435 | + |
436 | +[meta] |
437 | +extends: ../launchpad-base-lazr.conf |
438 | + |
439 | +[codebrowse] |
440 | +cachepath: {{ cache_dir }} |
441 | +launchpad_root: https://code.{{ domain }}/ |
442 | +log_folder: {{ logs_dir }} |
443 | +port: {{ port_loggerhead }} |
444 | +private_port: {{ port_loggerhead_api }} |
445 | +secret_path: {{ secrets_dir }}/cookies.hmac |
446 | + |
447 | +[codehosting] |
448 | +{{- opt("internal_branch_by_id_root", internal_branch_by_id_root) }} |
449 | + |
450 | diff --git a/charm/launchpad-loggerhead/templates/launchpad-loggerhead.service.j2 b/charm/launchpad-loggerhead/templates/launchpad-loggerhead.service.j2 |
451 | new file mode 100644 |
452 | index 0000000..4a98153 |
453 | --- /dev/null |
454 | +++ b/charm/launchpad-loggerhead/templates/launchpad-loggerhead.service.j2 |
455 | @@ -0,0 +1,22 @@ |
456 | +[Unit] |
457 | +Description=Launchpad Bazaar/Breezy code browsing server |
458 | +After=network.target |
459 | +ConditionPathExists=!{{ code_dir }}/maintenance.txt |
460 | + |
461 | +[Service] |
462 | +Type=notify |
463 | +User=launchpad |
464 | +Group=launchpad |
465 | +WorkingDirectory={{ code_dir }} |
466 | +Environment=BRZ_PLUGIN_PATH=brzplugins |
467 | +Environment=LPCONFIG=launchpad-loggerhead |
468 | +SyslogIdentifier=loggerhead |
469 | +ExecStart={{ code_dir }}/scripts/start-loggerhead.py |
470 | +ExecReload=/bin/kill -HUP $MAINPID |
471 | +KillMode=mixed |
472 | +Restart=on-failure |
473 | +PrivateTmp=true |
474 | + |
475 | +[Install] |
476 | +WantedBy=multi-user.target |
477 | + |
478 | diff --git a/charm/launchpad-loggerhead/templates/logrotate.conf.j2 b/charm/launchpad-loggerhead/templates/logrotate.conf.j2 |
479 | new file mode 100644 |
480 | index 0000000..5bde352 |
481 | --- /dev/null |
482 | +++ b/charm/launchpad-loggerhead/templates/logrotate.conf.j2 |
483 | @@ -0,0 +1,15 @@ |
484 | +{{ logs_dir }}/access.log {{ logs_dir }}/debug.log |
485 | +{ |
486 | + rotate 21 |
487 | + daily |
488 | + dateext |
489 | + delaycompress |
490 | + compress |
491 | + notifempty |
492 | + missingok |
493 | + create 0644 {{ user }} {{ user }} |
494 | + postrotate |
495 | + systemctl restart launchpad-loggerhead.service |
496 | + endscript |
497 | +} |
498 | + |
LGTM 👍