Merge ~pappacena/turnip:celery-charm into turnip:master

Proposed by Thiago F. Pappacena
Status: Merged
Approved by: Thiago F. Pappacena
Approved revision: e41a73dbaac2ad6a314b3ac96571437ff538f396
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~pappacena/turnip:celery-charm
Merge into: turnip:master
Prerequisite: ~pappacena/turnip:celery-scaffolding
Diff against target: 477 lines (+262/-5)
17 files modified
charm/Makefile (+3/-1)
charm/bundle.yaml.in (+13/-0)
charm/dependencies.txt (+1/-0)
charm/layer/turnip-base/lib/charms/turnip/base.py (+21/-1)
charm/turnip-api/config.yaml (+4/-0)
charm/turnip-api/layer.yaml (+1/-0)
charm/turnip-api/lib/charms/turnip/api.py (+7/-0)
charm/turnip-api/metadata.yaml (+4/-0)
charm/turnip-api/reactive/turnip-api.py (+13/-3)
charm/turnip-api/templates/turnip-api.service.j2 (+1/-0)
charm/turnip-celery/config.yaml (+5/-0)
charm/turnip-celery/layer.yaml (+6/-0)
charm/turnip-celery/lib/charms/turnip/celery.py (+50/-0)
charm/turnip-celery/metadata.yaml (+23/-0)
charm/turnip-celery/reactive/turnip-celery.py (+69/-0)
charm/turnip-celery/templates/logrotate.j2 (+13/-0)
charm/turnip-celery/templates/turnip-celery.service.j2 (+28/-0)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+387689@code.launchpad.net

Commit message

Adding juju charm for celery and rabbitmq

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) wrote :

A few bits to fix up, but this seems pretty close, thanks!

review: Approve
Revision history for this message
Thiago F. Pappacena (pappacena) wrote :

Pushed the requested changes. It worth another round of review, although the diff is not big.

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/charm/Makefile b/charm/Makefile
2index 2b01ef9..147eeb2 100644
3--- a/charm/Makefile
4+++ b/charm/Makefile
5@@ -19,7 +19,8 @@ CHARMS := \
6 turnip-pack-frontend-git \
7 turnip-pack-frontend-ssh \
8 turnip-pack-frontend-http \
9- turnip-api
10+ turnip-api \
11+ turnip-celery
12
13 PUBLISH_REPO_PREFIX := lp:~canonical-launchpad-branches/turnip/+git/charm-build-
14 PUBLISHDIR := $(BUILDDIR)/publish
15@@ -49,6 +50,7 @@ build-turnip-pack-frontend-git: dist/.built-turnip-pack-frontend-git
16 build-turnip-pack-frontend-ssh: dist/.built-turnip-pack-frontend-ssh
17 build-turnip-pack-frontend-http: dist/.built-turnip-pack-frontend-http
18 build-turnip-api: dist/.built-turnip-api
19+build-turnip-celery: dist/.built-turnip-celery
20
21 dist/.built-%: $(CHARM_DEPS) | $(BUILDDIR)
22 @echo "Building $*..."
23diff --git a/charm/bundle.yaml.in b/charm/bundle.yaml.in
24index 8078e4b..f098894 100644
25--- a/charm/bundle.yaml.in
26+++ b/charm/bundle.yaml.in
27@@ -25,6 +25,9 @@ applications:
28 service_port: 9419
29 ssl_cert: "%SSL_CERT%"
30 ssl_key: "%SSL_KEY%"
31+ rabbitmq-server:
32+ charm: cs:rabbitmq-server
33+ num_units: 1
34 turnip-pack-backend:
35 charm: ./dist/builds/turnip-pack-backend
36 num_units: 1
37@@ -74,6 +77,14 @@ applications:
38 build_label: "%BUILD_LABEL%"
39 resources:
40 turnip: "../build/%BUILD_LABEL%/turnip.tar.gz"
41+ turnip-celery:
42+ charm: ./dist/builds/turnip-celery
43+ num_units: 1
44+ to: [turnip-pack-backend]
45+ options:
46+ build_label: "%BUILD_LABEL%"
47+ resources:
48+ turnip: "../build/%BUILD_LABEL%/turnip.tar.gz"
49 relations:
50 - ["haproxy", "turnip-pack-backend"]
51 - ["haproxy", "turnip-pack-virt:turnip-pack-backend"]
52@@ -85,3 +96,5 @@ relations:
53 - ["haproxy", "turnip-pack-frontend-http:turnip-pack-virt"]
54 - ["haproxy", "turnip-pack-frontend-http:turnip-pack-frontend-http"]
55 - ["haproxy", "turnip-api"]
56+ - ['rabbitmq-server:amqp', "turnip-api:amqp"]
57+ - ['rabbitmq-server:amqp', "turnip-celery:amqp"]
58diff --git a/charm/dependencies.txt b/charm/dependencies.txt
59index dc73e4c..b0ee2a5 100644
60--- a/charm/dependencies.txt
61+++ b/charm/dependencies.txt
62@@ -1,4 +1,5 @@
63 interface @
64+interface/rabbitmq git+https://github.com/openstack/charm-interface-rabbitmq;revno=571f486
65 interface/http git+https://github.com/juju-solutions/interface-http;revno=4a232c69
66 interface/mount git+https://github.com/juju-solutions/interface-mount;revno=d5a2526f
67 interface/nrpe-external-master git+https://github.com/cmars/nrpe-external-master-interface;revno=20b2b9fb
68diff --git a/charm/layer/turnip-base/lib/charms/turnip/base.py b/charm/layer/turnip-base/lib/charms/turnip/base.py
69index 7988fd4..dbeada4 100644
70--- a/charm/layer/turnip-base/lib/charms/turnip/base.py
71+++ b/charm/layer/turnip-base/lib/charms/turnip/base.py
72@@ -16,6 +16,7 @@ from charmhelpers.core import (
73 from charmhelpers.fetch import apt_install
74 from charmhelpers.payload import archive
75 from charms.layer import status
76+from charms.reactive import endpoint_from_name
77 import six
78 import yaml
79
80@@ -288,7 +289,9 @@ def configure_service(service_name=None):
81 host.service_stop(service_name)
82 if not host.service_resume(service_name):
83 raise RuntimeError('Failed to start {}'.format(service_name))
84- hookenv.open_port(config['port'])
85+ port = config.get('port')
86+ if port is not None:
87+ hookenv.open_port(port)
88 configure_logrotate(config)
89
90
91@@ -360,3 +363,20 @@ def publish_website(website, name, port):
92 'port': str(port),
93 'services': haproxy_services_yaml,
94 })
95+
96+
97+def get_rabbitmq_url():
98+ rabbitmq = endpoint_from_name('amqp')
99+
100+ vhost = rabbitmq.vhost() if rabbitmq.vhost() else "/"
101+ if not rabbitmq.username():
102+ try:
103+ rabbitmq.request_access(username="turnip", vhost=vhost)
104+ except Exception:
105+ pass
106+
107+ if not rabbitmq.username() or not rabbitmq.password():
108+ return None
109+
110+ user = "%s:%s" % (rabbitmq.username(), rabbitmq.password())
111+ return "pyamqp://%s@%s/%s" % (user, rabbitmq.private_address(), vhost)
112diff --git a/charm/turnip-api/config.yaml b/charm/turnip-api/config.yaml
113index e773d0f..cff36b9 100644
114--- a/charm/turnip-api/config.yaml
115+++ b/charm/turnip-api/config.yaml
116@@ -45,3 +45,7 @@ options:
117 - option httplog
118 - option httpchk /repo
119 - balance leastconn
120+ celery_broker:
121+ type: string
122+ default: pyamqp://guest@localhost//
123+ description: Celery broker URL
124diff --git a/charm/turnip-api/layer.yaml b/charm/turnip-api/layer.yaml
125index 80010c8..3f7b2ef 100644
126--- a/charm/turnip-api/layer.yaml
127+++ b/charm/turnip-api/layer.yaml
128@@ -2,4 +2,5 @@ includes:
129 - layer:status
130 - layer:turnip-base
131 - layer:turnip-storage
132+ - interface:rabbitmq
133 repo: https://git.launchpad.net/turnip
134diff --git a/charm/turnip-api/lib/charms/turnip/api.py b/charm/turnip-api/lib/charms/turnip/api.py
135index f55315a..a7dffbd 100644
136--- a/charm/turnip-api/lib/charms/turnip/api.py
137+++ b/charm/turnip-api/lib/charms/turnip/api.py
138@@ -12,10 +12,12 @@ from charmhelpers.core import (
139 templating,
140 )
141
142+from charms.layer import status
143 from charms.turnip.base import (
144 code_dir,
145 data_dir,
146 data_mount_unit,
147+ get_rabbitmq_url,
148 logs_dir,
149 reload_systemd,
150 venv_dir,
151@@ -23,6 +25,9 @@ from charms.turnip.base import (
152
153
154 def configure_wsgi():
155+ celery_broker = get_rabbitmq_url()
156+ if celery_broker is None:
157+ return host.service_running('turnip-api')
158 config = hookenv.config()
159 context = dict(config)
160 context.update({
161@@ -32,6 +37,7 @@ def configure_wsgi():
162 'data_mount_unit': data_mount_unit(),
163 'logs_dir': logs_dir(),
164 'venv_dir': venv_dir(),
165+ 'celery_broker': celery_broker,
166 })
167 if context['wsgi_workers'] == 0:
168 context['wsgi_workers'] = cpu_count() * 2 + 1
169@@ -46,3 +52,4 @@ def configure_wsgi():
170 host.service_stop('turnip-api')
171 if not host.service_resume('turnip-api'):
172 raise RuntimeError('Failed to start turnip-api')
173+ return True
174diff --git a/charm/turnip-api/metadata.yaml b/charm/turnip-api/metadata.yaml
175index 7a10773..f506fcb 100644
176--- a/charm/turnip-api/metadata.yaml
177+++ b/charm/turnip-api/metadata.yaml
178@@ -19,3 +19,7 @@ provides:
179 nrpe-external-master:
180 interface: nrpe-external-master
181 scope: container
182+requires:
183+ amqp:
184+ interface: rabbitmq
185+ optional: true
186diff --git a/charm/turnip-api/reactive/turnip-api.py b/charm/turnip-api/reactive/turnip-api.py
187index 565ba92..223112d 100644
188--- a/charm/turnip-api/reactive/turnip-api.py
189+++ b/charm/turnip-api/reactive/turnip-api.py
190@@ -11,6 +11,7 @@ from charms.reactive import (
191 set_flag,
192 when,
193 when_not,
194+ when_not_all,
195 )
196
197 from charms.turnip.api import configure_wsgi
198@@ -21,7 +22,8 @@ from charms.turnip.base import (
199 )
200
201
202-@when('turnip.installed', 'turnip.storage.available')
203+@when('turnip.installed', 'turnip.storage.available',
204+ 'turnip.rabbitmq.available')
205 @when_not('turnip.configured')
206 def configure_turnip():
207 configure_wsgi()
208@@ -34,11 +36,19 @@ def configure_turnip():
209
210
211 @when('turnip.configured')
212-@when_not('turnip.storage.available')
213+@when_not_all('turnip.storage.available', 'turnip.rabbitmq.available')
214 def deconfigure_turnip():
215 deconfigure_service('turnip-api')
216 clear_flag('turnip.configured')
217- status.blocked('Waiting for storage to be available')
218+ status.blocked('Waiting for storage and rabbitmq to be available')
219+
220+
221+@when('amqp.connected')
222+def rabbitmq_available():
223+ if configure_wsgi():
224+ set_flag('turnip.rabbitmq.available')
225+ else:
226+ clear_flag('turnip.rabbitmq.available')
227
228
229 @when('nrpe-external-master.available', 'turnip.configured')
230diff --git a/charm/turnip-api/templates/turnip-api.service.j2 b/charm/turnip-api/templates/turnip-api.service.j2
231index 3bb4336..2cecf01 100644
232--- a/charm/turnip-api/templates/turnip-api.service.j2
233+++ b/charm/turnip-api/templates/turnip-api.service.j2
234@@ -13,6 +13,7 @@ WorkingDirectory={{ code_dir }}
235 Environment=PATH={{ venv_dir }}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
236 Environment=REPO_STORE={{ data_dir }}/repos
237 Environment=TURNIP_LOG_DIR={{ logs_dir }}
238+Environment=CELERY_BROKER={{ celery_broker }}
239 ExecStart={{ venv_dir }}/bin/gunicorn --config {{ config_file }} --paste api.ini
240 ExecReload=/bin/kill -s HUP $MAINPID
241 ExecStop=/bin/kill -s TERM $MAINPID
242diff --git a/charm/turnip-celery/config.yaml b/charm/turnip-celery/config.yaml
243new file mode 100644
244index 0000000..9cf765d
245--- /dev/null
246+++ b/charm/turnip-celery/config.yaml
247@@ -0,0 +1,5 @@
248+options:
249+ celery_broker:
250+ type: string
251+ default: pyamqp://guest@localhost//
252+ description: Celery broker URL
253diff --git a/charm/turnip-celery/layer.yaml b/charm/turnip-celery/layer.yaml
254new file mode 100644
255index 0000000..3f7b2ef
256--- /dev/null
257+++ b/charm/turnip-celery/layer.yaml
258@@ -0,0 +1,6 @@
259+includes:
260+ - layer:status
261+ - layer:turnip-base
262+ - layer:turnip-storage
263+ - interface:rabbitmq
264+repo: https://git.launchpad.net/turnip
265diff --git a/charm/turnip-celery/lib/charms/turnip/celery.py b/charm/turnip-celery/lib/charms/turnip/celery.py
266new file mode 100644
267index 0000000..60ed019
268--- /dev/null
269+++ b/charm/turnip-celery/lib/charms/turnip/celery.py
270@@ -0,0 +1,50 @@
271+# Copyright 2018 Canonical Ltd. This software is licensed under the
272+# GNU Affero General Public License version 3 (see the file LICENSE).
273+
274+from __future__ import absolute_import, print_function, unicode_literals
275+
276+
277+from charmhelpers.core import (
278+ hookenv,
279+ host,
280+ templating,
281+ )
282+
283+from charms.turnip.base import (
284+ code_dir,
285+ data_dir,
286+ data_mount_unit,
287+ get_rabbitmq_url,
288+ logs_dir,
289+ reload_systemd,
290+ venv_dir,
291+ )
292+
293+
294+def configure_celery():
295+ """Configure celery service, connecting it to rabbitmq.
296+
297+ :return: True if service is running, False otherwise."""
298+ celery_broker = get_rabbitmq_url()
299+ if celery_broker is None:
300+ return host.service_running('turnip-celery')
301+ config = hookenv.config()
302+ context = dict(config)
303+ context.update({
304+ 'code_dir': code_dir(),
305+ 'data_dir': data_dir(),
306+ 'data_mount_unit': data_mount_unit(),
307+ 'logs_dir': logs_dir(),
308+ 'venv_dir': venv_dir(),
309+ 'celery_broker': celery_broker,
310+ })
311+ templating.render(
312+ 'turnip-celery.service.j2',
313+ '/lib/systemd/system/turnip-celery.service',
314+ context, perms=0o644)
315+ if host.service_running('turnip-celery'):
316+ host.service_stop('turnip-celery')
317+ reload_systemd()
318+ if not host.service_resume('turnip-celery'):
319+ raise RuntimeError('Failed to start turnip-celery')
320+ return True
321diff --git a/charm/turnip-celery/metadata.yaml b/charm/turnip-celery/metadata.yaml
322new file mode 100644
323index 0000000..5105848
324--- /dev/null
325+++ b/charm/turnip-celery/metadata.yaml
326@@ -0,0 +1,23 @@
327+name: turnip-celery
328+display-name: turnip-celery
329+summary: Turnip celery worker
330+maintainer: Colin Watson <cjwatson@canonical.com>
331+description: >
332+ Turnip is a flexible and scalable Git server suite written in Python
333+ using Twisted. This component provides asynchronous processing workers.
334+tags:
335+ # https://docs.jujucharms.com/devel/en/authors-charm-metadata#charm-store-fields
336+ - network
337+ - web_server
338+series:
339+ - bionic
340+ - xenial
341+subordinate: false
342+provides:
343+ nrpe-external-master:
344+ interface: nrpe-external-master
345+ scope: container
346+requires:
347+ amqp:
348+ interface: rabbitmq
349+ optional: true
350diff --git a/charm/turnip-celery/reactive/turnip-celery.py b/charm/turnip-celery/reactive/turnip-celery.py
351new file mode 100644
352index 0000000..357e96b
353--- /dev/null
354+++ b/charm/turnip-celery/reactive/turnip-celery.py
355@@ -0,0 +1,69 @@
356+# Copyright 2018 Canonical Ltd. This software is licensed under the
357+# GNU Affero General Public License version 3 (see the file LICENSE).
358+
359+from __future__ import absolute_import, print_function, unicode_literals
360+
361+from charmhelpers.core import hookenv
362+from charms.layer import status
363+from charms.reactive import (
364+ clear_flag,
365+ endpoint_from_flag,
366+ set_flag,
367+ when,
368+ when_not,
369+ when_not_all,
370+ )
371+
372+from charms.turnip.celery import configure_celery
373+from charms.turnip.base import (
374+ configure_service,
375+ deconfigure_service,
376+ )
377+
378+
379+@when('turnip.installed', 'turnip.storage.available',
380+ 'turnip.rabbitmq.available')
381+@when_not('turnip.configured')
382+def configure_turnip():
383+ configure_service()
384+ set_flag('turnip.configured')
385+ clear_flag('turnip.storage.nrpe-external-master.published')
386+ clear_flag('turnip.nrpe-external-master.published')
387+ clear_flag('turnip.turnip-celery.published')
388+ status.active('Ready')
389+
390+
391+@when('turnip.configured')
392+@when_not_all('turnip.storage.available', 'turnip.rabbitmq.available')
393+def deconfigure_turnip():
394+ deconfigure_service('turnip-celery')
395+ clear_flag('turnip.configured')
396+ status.blocked('Waiting for storage and rabbitmq to be available')
397+
398+
399+@when('amqp.connected')
400+def rabbitmq_available():
401+ if configure_celery():
402+ set_flag('turnip.rabbitmq.available')
403+ else:
404+ clear_flag('turnip.rabbitmq.available')
405+
406+
407+@when('nrpe-external-master.available', 'turnip.configured')
408+@when_not('turnip.nrpe-external-master.published')
409+def nrpe_available():
410+ nagios = endpoint_from_flag('nrpe-external-master.available')
411+ config = hookenv.config()
412+ nagios.add_check(
413+ ['/usr/lib/nagios/plugins/check_procs', '-c', '1:128',
414+ '-C', 'celery', '-a', '-A turnip.tasks worker'],
415+ name='check_celery',
416+ description='Git celery check',
417+ context=config['nagios_context'])
418+ set_flag('turnip.nrpe-external-master.published')
419+
420+
421+@when('turnip.nrpe-external-master.published')
422+@when_not('nrpe-external-master.available')
423+def nrpe_unavailable():
424+ clear_flag('turnip.nrpe-external-master.published')
425diff --git a/charm/turnip-celery/templates/logrotate.j2 b/charm/turnip-celery/templates/logrotate.j2
426new file mode 100644
427index 0000000..5087f4f
428--- /dev/null
429+++ b/charm/turnip-celery/templates/logrotate.j2
430@@ -0,0 +1,13 @@
431+{{ base_dir }}/logs/turnip-celery.log {
432+ rotate 90
433+ daily
434+ dateext
435+ delaycompress
436+ compress
437+ missingok
438+ create 0644 {{ user }} {{ group }}
439+ postrotate
440+ service turnip-celery reload
441+ endscript
442+}
443+
444diff --git a/charm/turnip-celery/templates/turnip-celery.service.j2 b/charm/turnip-celery/templates/turnip-celery.service.j2
445new file mode 100644
446index 0000000..6370157
447--- /dev/null
448+++ b/charm/turnip-celery/templates/turnip-celery.service.j2
449@@ -0,0 +1,28 @@
450+[Unit]
451+Description=Turnip celery server
452+After=network.target
453+{%- if nfs %}
454+BindsTo={{ data_mount_unit }}
455+After={{ data_mount_unit }}
456+{%- endif %}
457+
458+[Service]
459+User={{ user }}
460+Group={{ group }}
461+WorkingDirectory={{ code_dir }}
462+Environment=PATH={{ venv_dir }}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
463+Environment=REPO_STORE={{ data_dir }}/repos
464+Environment=TURNIP_LOG_DIR={{ logs_dir }}
465+Environment=CELERY_BROKER={{ celery_broker }}
466+ExecStart={{ venv_dir }}/bin/celery -A turnip.tasks worker --logfile={{ logs_dir }}/turnip-celery.log --pool=gevent
467+ExecReload=/bin/kill -s HUP $MAINPID
468+ExecStop=/bin/kill -s TERM $MAINPID
469+LimitNOFILE=1048576
470+PrivateTmp=true
471+
472+[Install]
473+WantedBy=multi-user.target
474+{%- if nfs %}
475+WantedBy={{ data_mount_unit }}
476+{%- endif %}
477+

Subscribers

People subscribed via source and target branches