Merge lp:~patrick-hetu/charms/precise/gunicorn/python-rewrite into lp:~charmers/charms/precise/gunicorn/trunk
- Precise Pangolin (12.04)
- python-rewrite
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Mark Mims |
Approved revision: | 33 |
Merged at revision: | 27 |
Proposed branch: | lp:~patrick-hetu/charms/precise/gunicorn/python-rewrite |
Merge into: | lp:~charmers/charms/precise/gunicorn/trunk |
Diff against target: |
1324 lines (+974/-212) 14 files modified
README (+0/-38) README.rst (+52/-0) config.yaml (+18/-17) hooks/config-changed (+0/-6) hooks/hooks.py (+491/-0) hooks/install (+0/-24) hooks/start (+0/-4) hooks/stop (+0/-4) hooks/upgrade-charm (+0/-11) hooks/wsgi-file-relation-joined (+0/-102) icon.svg (+377/-0) metadata.yaml (+1/-5) revision (+1/-1) templates/upstart.tmpl (+34/-0) |
To merge this branch: | bzr merge lp:~patrick-hetu/charms/precise/gunicorn/python-rewrite |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mark Mims (community) | Approve | ||
James Page | Needs Resubmitting | ||
charmers | Pending | ||
Review via email: mp+167088@code.launchpad.net |
Commit message
Description of the change
Python rewrite and compatibility with the new Django Charm.
- 30. By Patrick Hetu
-
add an icon
- 31. By Patrick Hetu
-
add a category
Patrick Hetu (patrick-hetu) wrote : | # |
Hi Mark,
I've added the icon and the category to the metadata.
I've not added fake start/stop hook since I don't want them
to be list in the juju store if they don't do anything.
The website relation will be handled in the python-django charm,
where I've tried to "centralize" all the interactions.
Patrick Hetu (patrick-hetu) wrote : | # |
I just realize that the open/close port commands are in Gunicorn.
I'll move it to python-django so that the expose command will we run
on the Django charm also.
- 32. By Patrick Hetu
-
move ports control to the python-django charm
- 33. By Patrick Hetu
-
modify the docs for the port exposition changes
Patrick Hetu (patrick-hetu) wrote : | # |
This review is ready for a second round of checks. I'm not sure
if I need to change something here to reflect that.
James Page (james-page) : | # |
Mark Mims (mark-mims) wrote : | # |
ok, this looks good... I like the idea of gunicorn as a sub to django, but having django delegate down to gunicorn instead of gunicorn implementing website relations directly.
Taking it now b/c it works, but please address the following related bugs when you get a chance:
https:/
https:/
https:/
Thanks!
Preview Diff
1 | === removed file 'README' |
2 | --- README 2012-10-04 12:14:32 +0000 |
3 | +++ README 1970-01-01 00:00:00 +0000 |
4 | @@ -1,38 +0,0 @@ |
5 | -Juju charm gunicorn |
6 | -=================== |
7 | - |
8 | -:Author: Patrick Hetu <patrick@koumbit.org> |
9 | - |
10 | -How to configure the charm |
11 | --------------------------- |
12 | - |
13 | -To deploy a charm with this subordinate it must minimaly: |
14 | - |
15 | - 1. Provide the wsgi interface. |
16 | - 2. Set the `working_dir` relation variable in the wsgi hook. |
17 | - |
18 | -The configuration of Gunicorn will use the variable pass by |
19 | -the relation hook first. If there are not define it will |
20 | -fallback to the global configuration of the charm. |
21 | - |
22 | -Example deployment |
23 | ------------------- |
24 | - |
25 | - |
26 | - |
27 | -1. Deployment with python-moinmoin for example:: |
28 | - |
29 | - juju bootstrap |
30 | - juju deploy --config mywiki_with_wsgi_settings.yaml python-moinmoin |
31 | - juju deploy gunicorn |
32 | - juju add-relation gunicorn python-moinmoin |
33 | - juju expose gunicorn |
34 | - |
35 | -2. Accessing your new wiki should be ready at:: |
36 | - |
37 | - http://<machine-addr>:8080/ |
38 | - |
39 | - To find out the public address of gunicorn/python-moinmoin, look for it in |
40 | - the output of the `juju status` command. |
41 | - I recommend using a reverse proxy like Nginx in front of Gunicorn. |
42 | - |
43 | |
44 | === added file 'README.rst' |
45 | --- README.rst 1970-01-01 00:00:00 +0000 |
46 | +++ README.rst 2013-06-05 17:10:31 +0000 |
47 | @@ -0,0 +1,52 @@ |
48 | +Juju charm gunicorn |
49 | +=================== |
50 | + |
51 | +:Author: Patrick Hetu <patrick@koumbit.org> |
52 | + |
53 | +How to configure the charm |
54 | +-------------------------- |
55 | + |
56 | +To deploy a charm with this subordinate it must minimaly: |
57 | + |
58 | + 1. Provide the wsgi interface. |
59 | + 2. Set the `working_dir` relation variable in the wsgi hook. |
60 | + |
61 | +The configuration of Gunicorn will use the variable pass by |
62 | +the relation hook first. If there are not define it will |
63 | +fallback to the global configuration of the charm. |
64 | + |
65 | +Example deployment |
66 | +------------------ |
67 | + |
68 | +1. Deployment with python-moinmoin for example:: |
69 | + |
70 | + juju bootstrap |
71 | + juju deploy --config mywiki_with_wsgi_settings.yaml python-moinmoin |
72 | + juju deploy gunicorn |
73 | + juju add-relation gunicorn python-moinmoin |
74 | + juju expose python-moinmoin |
75 | + |
76 | +2. Accessing your new wiki should be ready at:: |
77 | + |
78 | + http://<machine-addr>:8080/ |
79 | + |
80 | + To find out the public address of gunicorn/python-moinmoin, look for it in |
81 | + the output of the `juju status` command. |
82 | + I recommend using a reverse proxy like Nginx in front of Gunicorn. |
83 | + |
84 | +Changelog |
85 | +--------- |
86 | +3: |
87 | + |
88 | + Notable changes: |
89 | + |
90 | + * Rewrite the charm using python instead of BASH scripts |
91 | + * add listen_ip configuration variable |
92 | + |
93 | + Backwards incompatible changes: |
94 | + * Remove the Django mode since Gunicorn is not recommending it anymore. |
95 | + * Use Upstart to manage daemons |
96 | + * no start/stop hook anymore use related charms instead. |
97 | + * no configuration change directly on the charm anymore, use related charms instead. |
98 | + * no access logging by default |
99 | + * exposing a port must now be done in the linked charm instead of this one |
100 | |
101 | === modified file 'config.yaml' |
102 | --- config.yaml 2012-10-03 17:57:11 +0000 |
103 | +++ config.yaml 2013-06-05 17:10:31 +0000 |
104 | @@ -1,15 +1,16 @@ |
105 | options: |
106 | wsgi_wsgi_file: |
107 | type: string |
108 | + default: "wsgi" |
109 | description: "The name of the WSGI application." |
110 | wsgi_workers: |
111 | type: int |
112 | - default: 2 |
113 | - description: "The number of worker process for handling requests." |
114 | + default: 0 |
115 | + description: "The number of worker process for handling requests. 0 for count(cpu) + 1" |
116 | wsgi_worker_class: |
117 | type: string |
118 | default: "sync" |
119 | - description: "Gunicorn workers type." |
120 | + description: "Gunicorn workers type. Can be: sync, eventlet, gevent, tornado" |
121 | wsgi_worker_connections: |
122 | type: int |
123 | default: 1000 |
124 | @@ -25,11 +26,11 @@ |
125 | wsgi_timeout: |
126 | type: int |
127 | default: 30 |
128 | - description: "" |
129 | + description: "Timeout of a request in seconds." |
130 | wsgi_keep_alive: |
131 | type: int |
132 | default: 2 |
133 | - description: "" |
134 | + description: "Keep alive time in seconds." |
135 | wsgi_umask: |
136 | type: string |
137 | default: "0" |
138 | @@ -45,36 +46,36 @@ |
139 | wsgi_log_file: |
140 | type: string |
141 | default: "-" |
142 | - description: "The log file to write to. If empty the file would be <your_application_dir>/gunicorn.log" |
143 | + description: "The log file to write to. If empty the logs would be handle by upstart." |
144 | wsgi_log_level: |
145 | type: string |
146 | default: "info" |
147 | description: "The granularity of Error log outputs." |
148 | wsgi_access_logfile: |
149 | type: string |
150 | - default: "-" |
151 | + default: "" |
152 | description: "The Access log file to write to." |
153 | wsgi_access_logformat: |
154 | type: string |
155 | - default: '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"' |
156 | - description: "The Access log format ." |
157 | + default: "" |
158 | + description: "The Access log format. Don't forget to escape all quotes and round brackets." |
159 | wsgi_extra: |
160 | type: string |
161 | default: "" |
162 | - description: "Extra settings. For example: {--debug}," |
163 | + description: "Space separated extra settings. For example: --debug" |
164 | wsgi_timestamp: |
165 | type: string |
166 | default: "" |
167 | - description: "The variable to modify to trigger Gunicorn reload" |
168 | - django_settings: |
169 | - type: string |
170 | - default: "" |
171 | - description: "The Python path to a Django settings module." |
172 | + description: "The variable to modify to trigger Gunicorn reload." |
173 | python_path: |
174 | type: string |
175 | default: "" |
176 | - description: "Set PYTHONPATH environment variable" |
177 | + description: "Set an additionnal PYTHONPATH to the project." |
178 | + listen_ip: |
179 | + type: string |
180 | + default: "0.0.0.0" |
181 | + description: "IP adresses that Gunicorn will listen on. By default we listen on all of them." |
182 | port: |
183 | type: int |
184 | default: 8080 |
185 | - description: "Default listen port" |
186 | + description: "Port the application will be listenning." |
187 | |
188 | === removed file 'hooks/config-changed' |
189 | --- hooks/config-changed 2012-07-06 16:06:15 +0000 |
190 | +++ hooks/config-changed 1970-01-01 00:00:00 +0000 |
191 | @@ -1,6 +0,0 @@ |
192 | -#!/bin/bash |
193 | -set -e |
194 | - |
195 | -juju-log "reloading Gunicorn" |
196 | - |
197 | -service gunicorn reload || true |
198 | |
199 | === removed symlink 'hooks/django-settings-relation-changed' |
200 | === target was u'wsgi-file-relation-joined' |
201 | === removed symlink 'hooks/django-settings-relation-joined' |
202 | === target was u'wsgi-file-relation-joined' |
203 | === added file 'hooks/hooks.py' |
204 | --- hooks/hooks.py 1970-01-01 00:00:00 +0000 |
205 | +++ hooks/hooks.py 2013-06-05 17:10:31 +0000 |
206 | @@ -0,0 +1,491 @@ |
207 | +#!/usr/bin/env python |
208 | +# vim: et ai ts=4 sw=4: |
209 | + |
210 | +import json |
211 | +import os |
212 | +import re |
213 | +import subprocess |
214 | +import sys |
215 | +import time |
216 | +from pwd import getpwnam |
217 | +from grp import getgrnam |
218 | + |
219 | +CHARM_PACKAGES = ["gunicorn"] |
220 | + |
221 | +INJECTED_WARNING = """ |
222 | +#------------------------------------------------------------------------------ |
223 | +# The following is the import code for the settings directory injected by Juju |
224 | +#------------------------------------------------------------------------------ |
225 | +""" |
226 | + |
227 | + |
228 | +############################################################################### |
229 | +# Supporting functions |
230 | +############################################################################### |
231 | +MSG_CRITICAL = "CRITICAL" |
232 | +MSG_DEBUG = "DEBUG" |
233 | +MSG_INFO = "INFO" |
234 | +MSG_ERROR = "ERROR" |
235 | +MSG_WARNING = "WARNING" |
236 | + |
237 | + |
238 | +def juju_log(level, msg): |
239 | + subprocess.call(['juju-log', '-l', level, msg]) |
240 | + |
241 | +#------------------------------------------------------------------------------ |
242 | +# run: Run a command, return the output |
243 | +#------------------------------------------------------------------------------ |
244 | +def run(command, exit_on_error=True, cwd=None): |
245 | + try: |
246 | + juju_log(MSG_DEBUG, command) |
247 | + return subprocess.check_output( |
248 | + command, stderr=subprocess.STDOUT, shell=True, cwd=cwd) |
249 | + except subprocess.CalledProcessError, e: |
250 | + juju_log(MSG_ERROR, "status=%d, output=%s" % (e.returncode, e.output)) |
251 | + if exit_on_error: |
252 | + sys.exit(e.returncode) |
253 | + else: |
254 | + raise |
255 | + |
256 | + |
257 | +#------------------------------------------------------------------------------ |
258 | +# install_file: install a file resource. overwites existing files. |
259 | +#------------------------------------------------------------------------------ |
260 | +def install_file(contents, dest, owner="root", group="root", mode=0600): |
261 | + uid = getpwnam(owner)[2] |
262 | + gid = getgrnam(group)[2] |
263 | + dest_fd = os.open(dest, os.O_WRONLY | os.O_TRUNC | os.O_CREAT, mode) |
264 | + os.fchown(dest_fd, uid, gid) |
265 | + with os.fdopen(dest_fd, 'w') as destfile: |
266 | + destfile.write(str(contents)) |
267 | + |
268 | + |
269 | +#------------------------------------------------------------------------------ |
270 | +# install_dir: create a directory |
271 | +#------------------------------------------------------------------------------ |
272 | +def install_dir(dirname, owner="root", group="root", mode=0700): |
273 | + command = \ |
274 | + '/usr/bin/install -o {} -g {} -m {} -d {}'.format(owner, group, oct(mode), |
275 | + dirname) |
276 | + return run(command) |
277 | + |
278 | +#------------------------------------------------------------------------------ |
279 | +# config_get: Returns a dictionary containing all of the config information |
280 | +# Optional parameter: scope |
281 | +# scope: limits the scope of the returned configuration to the |
282 | +# desired config item. |
283 | +#------------------------------------------------------------------------------ |
284 | +def config_get(scope=None): |
285 | + try: |
286 | + config_cmd_line = ['config-get'] |
287 | + if scope is not None: |
288 | + config_cmd_line.append(scope) |
289 | + config_cmd_line.append('--format=json') |
290 | + config_data = json.loads(subprocess.check_output(config_cmd_line)) |
291 | + except: |
292 | + config_data = None |
293 | + finally: |
294 | + return(config_data) |
295 | + |
296 | + |
297 | +#------------------------------------------------------------------------------ |
298 | +# relation_json: Returns json-formatted relation data |
299 | +# Optional parameters: scope, relation_id |
300 | +# scope: limits the scope of the returned data to the |
301 | +# desired item. |
302 | +# unit_name: limits the data ( and optionally the scope ) |
303 | +# to the specified unit |
304 | +# relation_id: specify relation id for out of context usage. |
305 | +#------------------------------------------------------------------------------ |
306 | +def relation_json(scope=None, unit_name=None, relation_id=None): |
307 | + command = ['relation-get', '--format=json'] |
308 | + if relation_id is not None: |
309 | + command.extend(('-r', relation_id)) |
310 | + if scope is not None: |
311 | + command.append(scope) |
312 | + else: |
313 | + command.append('-') |
314 | + if unit_name is not None: |
315 | + command.append(unit_name) |
316 | + output = subprocess.check_output(command, stderr=subprocess.STDOUT) |
317 | + return output or None |
318 | + |
319 | + |
320 | +#------------------------------------------------------------------------------ |
321 | +# relation_get: Returns a dictionary containing the relation information |
322 | +# Optional parameters: scope, relation_id |
323 | +# scope: limits the scope of the returned data to the |
324 | +# desired item. |
325 | +# unit_name: limits the data ( and optionally the scope ) |
326 | +# to the specified unit |
327 | +#------------------------------------------------------------------------------ |
328 | +def relation_get(scope=None, unit_name=None, relation_id=None): |
329 | + j = relation_json(scope, unit_name, relation_id) |
330 | + if j: |
331 | + return json.loads(j) |
332 | + else: |
333 | + return None |
334 | + |
335 | + |
336 | +def relation_set(keyvalues, relation_id=None): |
337 | + args = [] |
338 | + if relation_id: |
339 | + args.extend(['-r', relation_id]) |
340 | + args.extend(["{}='{}'".format(k, v or '') for k, v in keyvalues.items()]) |
341 | + run("relation-set {}".format(' '.join(args))) |
342 | + |
343 | + ## Posting json to relation-set doesn't seem to work as documented? |
344 | + ## Bug #1116179 |
345 | + ## |
346 | + ## cmd = ['relation-set'] |
347 | + ## if relation_id: |
348 | + ## cmd.extend(['-r', relation_id]) |
349 | + ## p = Popen( |
350 | + ## cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, |
351 | + ## stderr=subprocess.PIPE) |
352 | + ## (out, err) = p.communicate(json.dumps(keyvalues)) |
353 | + ## if p.returncode: |
354 | + ## juju_log(MSG_ERROR, err) |
355 | + ## sys.exit(1) |
356 | + ## juju_log(MSG_DEBUG, "relation-set {}".format(repr(keyvalues))) |
357 | + |
358 | + |
359 | +def relation_list(relation_id=None): |
360 | + """Return the list of units participating in the relation.""" |
361 | + if relation_id is None: |
362 | + relation_id = os.environ['JUJU_RELATION_ID'] |
363 | + cmd = ['relation-list', '--format=json', '-r', relation_id] |
364 | + json_units = subprocess.check_output(cmd).strip() |
365 | + if json_units: |
366 | + return json.loads(subprocess.check_output(cmd)) |
367 | + return [] |
368 | + |
369 | + |
370 | +#------------------------------------------------------------------------------ |
371 | +# relation_ids: Returns a list of relation ids |
372 | +# optional parameters: relation_type |
373 | +# relation_type: return relations only of this type |
374 | +#------------------------------------------------------------------------------ |
375 | +def relation_ids(relation_types=('db',)): |
376 | + # accept strings or iterators |
377 | + if isinstance(relation_types, basestring): |
378 | + reltypes = [relation_types, ] |
379 | + else: |
380 | + reltypes = relation_types |
381 | + relids = [] |
382 | + for reltype in reltypes: |
383 | + relid_cmd_line = ['relation-ids', '--format=json', reltype] |
384 | + json_relids = subprocess.check_output(relid_cmd_line).strip() |
385 | + if json_relids: |
386 | + relids.extend(json.loads(json_relids)) |
387 | + return relids |
388 | + |
389 | + |
390 | +#------------------------------------------------------------------------------ |
391 | +# relation_get_all: Returns a dictionary containing the relation information |
392 | +# optional parameters: relation_type |
393 | +# relation_type: limits the scope of the returned data to the |
394 | +# desired item. |
395 | +#------------------------------------------------------------------------------ |
396 | +def relation_get_all(*args, **kwargs): |
397 | + relation_data = [] |
398 | + relids = relation_ids(*args, **kwargs) |
399 | + for relid in relids: |
400 | + units_cmd_line = ['relation-list', '--format=json', '-r', relid] |
401 | + json_units = subprocess.check_output(units_cmd_line).strip() |
402 | + if json_units: |
403 | + for unit in json.loads(json_units): |
404 | + unit_data = \ |
405 | + json.loads(relation_json(relation_id=relid, |
406 | + unit_name=unit)) |
407 | + for key in unit_data: |
408 | + if key.endswith('-list'): |
409 | + unit_data[key] = unit_data[key].split() |
410 | + unit_data['relation-id'] = relid |
411 | + unit_data['unit'] = unit |
412 | + relation_data.append(unit_data) |
413 | + return relation_data |
414 | + |
415 | +def apt_get_update(): |
416 | + cmd_line = ['apt-get', 'update'] |
417 | + return(subprocess.call(cmd_line)) |
418 | + |
419 | + |
420 | +#------------------------------------------------------------------------------ |
421 | +# apt_get_install( packages ): Installs package(s) |
422 | +#------------------------------------------------------------------------------ |
423 | +def apt_get_install(packages=None): |
424 | + if packages is None: |
425 | + return(False) |
426 | + cmd_line = ['apt-get', '-y', 'install', '-qq'] |
427 | + if isinstance(packages, list): |
428 | + cmd_line.extend(packages) |
429 | + else: |
430 | + cmd_line.append(packages) |
431 | + return(subprocess.call(cmd_line)) |
432 | + |
433 | + |
434 | +#------------------------------------------------------------------------------ |
435 | +# pip_install( package ): Installs a python package |
436 | +#------------------------------------------------------------------------------ |
437 | +def pip_install(packages=None, upgrade=False): |
438 | + cmd_line = ['pip', 'install'] |
439 | + if packages is None: |
440 | + return(False) |
441 | + if upgrade: |
442 | + cmd_line.append('-u') |
443 | + if packages.startswith('svn+') or packages.startswith('git+') or \ |
444 | + packages.startswith('hg+') or packages.startswith('bzr+'): |
445 | + cmd_line.append('-e') |
446 | + cmd_line.append(packages) |
447 | + return run(cmd_line) |
448 | + |
449 | +#------------------------------------------------------------------------------ |
450 | +# pip_install_req( path ): Installs a requirements file |
451 | +#------------------------------------------------------------------------------ |
452 | +def pip_install_req(path=None, upgrade=False): |
453 | + cmd_line = ['pip', 'install'] |
454 | + if path is None: |
455 | + return(False) |
456 | + if upgrade: |
457 | + cmd_line.append('-u') |
458 | + cmd_line.append('-r') |
459 | + cmd_line.append(path) |
460 | + cwd = os.path.dirname(path) |
461 | + return run(cmd_line) |
462 | + |
463 | +#------------------------------------------------------------------------------ |
464 | +# open_port: Convenience function to open a port in juju to |
465 | +# expose a service |
466 | +#------------------------------------------------------------------------------ |
467 | +def open_port(port=None, protocol="TCP"): |
468 | + if port is None: |
469 | + return(None) |
470 | + return(subprocess.call(['open-port', "%d/%s" % |
471 | + (int(port), protocol)])) |
472 | + |
473 | + |
474 | +#------------------------------------------------------------------------------ |
475 | +# close_port: Convenience function to close a port in juju to |
476 | +# unexpose a service |
477 | +#------------------------------------------------------------------------------ |
478 | +def close_port(port=None, protocol="TCP"): |
479 | + if port is None: |
480 | + return(None) |
481 | + return(subprocess.call(['close-port', "%d/%s" % |
482 | + (int(port), protocol)])) |
483 | + |
484 | + |
485 | +#------------------------------------------------------------------------------ |
486 | +# update_service_ports: Convenience function that evaluate the old and new |
487 | +# service ports to decide which ports need to be |
488 | +# opened and which to close |
489 | +#------------------------------------------------------------------------------ |
490 | +def update_service_port(old_service_port=None, new_service_port=None): |
491 | + if old_service_port is None or new_service_port is None: |
492 | + return(None) |
493 | + if new_service_port != old_service_port: |
494 | + close_port(old_service_port) |
495 | + open_port(new_service_port) |
496 | + |
497 | +# |
498 | +# Utils |
499 | +# |
500 | + |
501 | +def install_or_append(contents, dest, owner="root", group="root", mode=0600): |
502 | + if os.path.exists(dest): |
503 | + uid = getpwnam(owner)[2] |
504 | + gid = getgrnam(group)[2] |
505 | + dest_fd = os.open(dest, os.O_APPEND, mode) |
506 | + os.fchown(dest_fd, uid, gid) |
507 | + with os.fdopen(dest_fd, 'a') as destfile: |
508 | + destfile.write(str(contents)) |
509 | + else: |
510 | + install_file(contents, dest, owner, group, mode) |
511 | + |
512 | +def token_sql_safe(value): |
513 | + # Only allow alphanumeric + underscore in database identifiers |
514 | + if re.search('[^A-Za-z0-9_]', value): |
515 | + return False |
516 | + return True |
517 | + |
518 | +def sanitize(s): |
519 | + s = s.replace(':', '_') |
520 | + s = s.replace('-', '_') |
521 | + s = s.replace('/', '_') |
522 | + s = s.replace('"', '_') |
523 | + s = s.replace("'", '_') |
524 | + return s |
525 | + |
526 | +def user_name(relid, remote_unit, admin=False, schema=False): |
527 | + components = [sanitize(relid), sanitize(remote_unit)] |
528 | + if admin: |
529 | + components.append("admin") |
530 | + elif schema: |
531 | + components.append("schema") |
532 | + return "_".join(components) |
533 | + |
534 | +def get_relation_host(): |
535 | + remote_host = run("relation-get ip") |
536 | + if not remote_host: |
537 | + # remote unit $JUJU_REMOTE_UNIT uses deprecated 'ip=' component of |
538 | + # interface. |
539 | + remote_host = run("relation-get private-address") |
540 | + return remote_host |
541 | + |
542 | + |
543 | +def get_unit_host(): |
544 | + this_host = run("unit-get private-address") |
545 | + return this_host.strip() |
546 | + |
547 | +def process_template(template_name, template_vars, destination): |
548 | + # --- exported service configuration file |
549 | + from jinja2 import Environment, FileSystemLoader |
550 | + template_env = Environment( |
551 | + loader=FileSystemLoader(os.path.join(os.environ['CHARM_DIR'], |
552 | + 'templates'))) |
553 | + |
554 | + template = \ |
555 | + template_env.get_template(template_name).render(template_vars) |
556 | + |
557 | + with open(destination, 'w') as inject_file: |
558 | + inject_file.write(str(template)) |
559 | + |
560 | +def append_template(template_name, template_vars, path, try_append=False): |
561 | + |
562 | + # --- exported service configuration file |
563 | + from jinja2 import Environment, FileSystemLoader |
564 | + template_env = Environment( |
565 | + loader=FileSystemLoader(os.path.join(os.environ['CHARM_DIR'], |
566 | + 'templates'))) |
567 | + |
568 | + template = \ |
569 | + template_env.get_template(template_name).render(template_vars) |
570 | + |
571 | + append = False |
572 | + if os.path.exists(path): |
573 | + with open(path, 'r') as inject_file: |
574 | + if not str(template) in inject_file: |
575 | + append = True |
576 | + else: |
577 | + append = True |
578 | + |
579 | + if append == True: |
580 | + with open(path, 'a') as inject_file: |
581 | + inject_file.write(INJECTED_WARNING) |
582 | + inject_file.write(str(template)) |
583 | + |
584 | + |
585 | + |
586 | +############################################################################### |
587 | +# Hook functions |
588 | +############################################################################### |
589 | +def install(): |
590 | + |
591 | + for retry in xrange(0,24): |
592 | + if apt_get_install(CHARM_PACKAGES): |
593 | + time.sleep(10) |
594 | + else: |
595 | + break |
596 | + |
597 | +def upgrade(): |
598 | + |
599 | + apt_get_update() |
600 | + for retry in xrange(0,24): |
601 | + if apt_get_install(CHARM_PACKAGES): |
602 | + time.sleep(10) |
603 | + else: |
604 | + break |
605 | + |
606 | +def wsgi_file_relation_joined_changed(): |
607 | + wsgi_config = config_data |
608 | + relation_id = os.environ['JUJU_RELATION_ID'] |
609 | + juju_log(MSG_INFO, "JUJU_RELATION_ID: %s".format(relation_id)) |
610 | + |
611 | + remote_unit_name = sanitize(os.environ['JUJU_REMOTE_UNIT'].split('/')[0]) |
612 | + juju_log(MSG_INFO, "JUJU_REMOTE_UNIT: %s".format(remote_unit_name)) |
613 | + wsgi_config['unit_name'] = remote_unit_name |
614 | + |
615 | + project_conf = '/etc/init/%s.conf' % remote_unit_name |
616 | + |
617 | + working_dir = relation_get('working_dir') |
618 | + if not working_dir: |
619 | + return |
620 | + |
621 | + wsgi_config['working_dir'] = working_dir |
622 | + wsgi_config['project_name'] = remote_unit_name |
623 | + |
624 | + for v in wsgi_config.keys(): |
625 | + if v.startswith('wsgi_') or v in ['python_path', 'listen_ip', 'port']: |
626 | + upstream_value = relation_get(v) |
627 | + if upstream_value: |
628 | + wsgi_config[v] = upstream_value |
629 | + |
630 | + if wsgi_config['wsgi_worker_class'] == 'eventlet': |
631 | + apt_get_install('python-eventlet') |
632 | + elif wsgi_config['wsgi_worker_class'] == 'gevent': |
633 | + apt_get_install('python-gevent') |
634 | + elif wsgi_config['wsgi_worker_class'] == 'tornado': |
635 | + apt_get_install('python-tornado') |
636 | + |
637 | + if wsgi_config['wsgi_workers'] == 0: |
638 | + res = run('python -c "import multiprocessing ; print(multiprocessing.cpu_count())"') |
639 | + wsgi_config['wsgi_workers'] = int(res) + 1 |
640 | + |
641 | + if wsgi_config['wsgi_access_logfile']: |
642 | + wsgi_config['wsgi_extra'] = " ".join([ |
643 | + wsgi_config['wsgi_extra'], |
644 | + '--access-logformat=%s' % wsgi_config['wsgi_access_logfile'], |
645 | + '--access-logformat="%s"' % wsgi_config['wsgi_access_logformat'] |
646 | + ]) |
647 | + |
648 | + wsgi_config['wsgi_wsgi_file'] = wsgi_config['wsgi_wsgi_file'] |
649 | + |
650 | + process_template('upstart.tmpl', wsgi_config, project_conf) |
651 | + |
652 | + |
653 | + # We need this because when the contained charm configuration or code changed |
654 | + # Gunicorn needs to restart to run the new code. |
655 | + run("service %s restart || service %s start" % (remote_unit_name, remote_unit_name)) |
656 | + |
657 | + |
658 | +def wsgi_file_relation_broken(): |
659 | + remote_unit_name = sanitize(os.environ['JUJU_REMOTE_UNIT'].split('/')[0]) |
660 | + |
661 | + run('service %s stop' % remote_unit_name) |
662 | + run('rm /etc/init/%s.conf' % remote_unit_name) |
663 | + |
664 | + |
665 | +############################################################################### |
666 | +# Global variables |
667 | +############################################################################### |
668 | +config_data = config_get() |
669 | +juju_log(MSG_DEBUG, "got config: %s" % str(config_data)) |
670 | + |
671 | +unit_name = os.environ['JUJU_UNIT_NAME'].split('/')[0] |
672 | + |
673 | +hook_name = os.path.basename(sys.argv[0]) |
674 | + |
675 | +############################################################################### |
676 | +# Main section |
677 | +############################################################################### |
678 | +def main(): |
679 | + juju_log(MSG_INFO, "Running {} hook".format(hook_name)) |
680 | + if hook_name == "install": |
681 | + install() |
682 | + |
683 | + elif hook_name == "upgrade-charm": |
684 | + upgrade() |
685 | + |
686 | + elif hook_name in ["wsgi-file-relation-joined", "wsgi-file-relation-changed"]: |
687 | + wsgi_file_relation_joined_changed() |
688 | + |
689 | + elif hook_name == "wsgi-file-relation-broken": |
690 | + wsgi_file_relation_broken() |
691 | + |
692 | + else: |
693 | + print "Unknown hook {}".format(hook_name) |
694 | + raise SystemExit(1) |
695 | + |
696 | +if __name__ == '__main__': |
697 | + raise SystemExit(main()) |
698 | |
699 | === modified file 'hooks/install' |
700 | --- hooks/install 2012-11-30 11:50:39 +0000 |
701 | +++ hooks/install 1970-01-01 00:00:00 +0000 |
702 | @@ -1,24 +0,0 @@ |
703 | -#!/bin/bash |
704 | -set -e |
705 | - |
706 | -# Suboridnate charm hooks can run in parallel with other charm hooks |
707 | -# on the same unit Bug #1068624. So a hook may fail to get a apt-get lock. |
708 | -COUNTER=0 |
709 | -APT_LOCKED=1 |
710 | -while true; do |
711 | - lsof /var/lib/dpkg/lock > /dev/null 2>&1 || APT_LOCKED=0 |
712 | - if [[ $APT_LOCKED -eq 0 ]]; then |
713 | - apt-get install --no-install-recommends -q -y gunicorn \ |
714 | - python-eventlet \ |
715 | - python-gevent \ |
716 | - python-tornado |
717 | - break |
718 | - fi |
719 | - if [[ $COUNTER -gt 5 ]]; then |
720 | - echo "Failed to obtain apt lock to install python-amqplib" |
721 | - exit 1 |
722 | - fi |
723 | - sleep 60 |
724 | - COUNTER=$[$COUNTER+1] |
725 | -done |
726 | - |
727 | |
728 | === target is u'hooks.py' |
729 | === removed file 'hooks/start' |
730 | --- hooks/start 2012-07-06 16:06:15 +0000 |
731 | +++ hooks/start 1970-01-01 00:00:00 +0000 |
732 | @@ -1,4 +0,0 @@ |
733 | -#!/bin/bash |
734 | -set -e |
735 | - |
736 | -service gunicorn start || service gunicorn restart |
737 | |
738 | === removed file 'hooks/stop' |
739 | --- hooks/stop 2012-07-06 16:06:15 +0000 |
740 | +++ hooks/stop 1970-01-01 00:00:00 +0000 |
741 | @@ -1,4 +0,0 @@ |
742 | -#!/bin/bash |
743 | -set -e |
744 | - |
745 | -service gunicorn stop || true |
746 | |
747 | === modified file 'hooks/upgrade-charm' |
748 | --- hooks/upgrade-charm 2012-07-08 16:35:05 +0000 |
749 | +++ hooks/upgrade-charm 1970-01-01 00:00:00 +0000 |
750 | @@ -1,11 +0,0 @@ |
751 | -#!/bin/bash |
752 | -set -e |
753 | - |
754 | -home=`dirname $0` |
755 | - |
756 | -juju-log "Upgrading charm by running install hook again." |
757 | -$home/install |
758 | - |
759 | -juju-log "Upgrading charm, running config-changed hook again." |
760 | -$home/config-changed |
761 | - |
762 | |
763 | === target is u'hooks.py' |
764 | === added symlink 'hooks/wsgi-file-relation-broken' |
765 | === target is u'hooks.py' |
766 | === modified symlink 'hooks/wsgi-file-relation-changed' |
767 | === target changed u'wsgi-file-relation-joined' => u'hooks.py' |
768 | === modified file 'hooks/wsgi-file-relation-joined' |
769 | --- hooks/wsgi-file-relation-joined 2013-01-24 06:06:09 +0000 |
770 | +++ hooks/wsgi-file-relation-joined 1970-01-01 00:00:00 +0000 |
771 | @@ -1,102 +0,0 @@ |
772 | -#!/bin/bash |
773 | -set -e |
774 | - |
775 | -unit_name=${JUJU_REMOTE_UNIT//\//-} |
776 | -working_dir=$(relation-get working_dir) |
777 | - |
778 | -if [ -z "$working_dir" ] ; then |
779 | - juju-log "No working_dir yet: skipping." |
780 | - exit 0 # wait for future handshake |
781 | -fi |
782 | - |
783 | -variables="wsgi_wsgi_file wsgi_workers wsgi_worker_class wsgi_worker_connections wsgi_max_requests wsgi_timeout wsgi_backlog wsgi_keep_alive wsgi_extra wsgi_user wsgi_group wsgi_umask wsgi_log_file wsgi_log_level wsgi_access_logfile wsgi_access_logformat env_extra django_settings python_path port" |
784 | - |
785 | -if [[ $JUJU_RELATION_ID =~ django.* ]]; then |
786 | - mode=django |
787 | -elif [[ $JUJU_RELATION_ID =~ wsgi.* ]]; then |
788 | - mode=wsgi |
789 | -else |
790 | - juju-log "Unknown mode ($JUJU_RELATION_ID)" |
791 | - exit 1 |
792 | -fi |
793 | - |
794 | -juju-log "Running in ${mode} mode" |
795 | - |
796 | - |
797 | -declare -A VAR |
798 | -for v in $variables;do |
799 | - VAR[$v]=$(relation-get $v) |
800 | - if [ -z "${VAR[$v]}" ] ; then |
801 | - VAR[$v]=$(config-get $v) |
802 | - fi |
803 | -done |
804 | - |
805 | -juju-log "Got variables: ${VAR[@]}" |
806 | - |
807 | - |
808 | -if [ -z "$wsgi_error_logfile" ] ; then |
809 | - error_logfile="${working_dir}/gunicorn.log" |
810 | -fi |
811 | - |
812 | -if [ -n "$VAR[django_settings]" ] ; then |
813 | - django_settings="'django_settings': '${VAR[django_settings]}'," |
814 | -fi |
815 | - |
816 | -# If running in wsgi mode then set a default wsgi file |
817 | -if [[ $mode -eq "wsgi" ]] && [[ $(relation-get wsgi_wsgi_file) -eq "" ]] ; then |
818 | - VAR[wsgi_wsgi_file]="wsgi" |
819 | -fi |
820 | - |
821 | -if [[ -n ${VAR[wsgi_wsgi_file]} ]] ; then |
822 | - wsgi_wsgi_file="'${VAR[wsgi_wsgi_file]}'," |
823 | -fi |
824 | - |
825 | -if [[ -z ${VAR[python_path]} ]] ; then |
826 | - python_path=${working_dir} |
827 | -else |
828 | - python_path="${VAR[python_path]}" |
829 | -fi |
830 | - |
831 | -juju-log "Writing config file: /etc/gunicorn.d/${unit_name}.conf" |
832 | - |
833 | -cat > /etc/gunicorn.d/${unit_name}.conf <<EOF |
834 | -CONFIG = { |
835 | - 'mode': '${mode}', |
836 | - 'environment': { |
837 | - 'PYTHONPATH': '${python_path}', |
838 | - ${VAR[env_extra]} |
839 | - }, |
840 | - ${django_settings} |
841 | - 'working_dir': '${working_dir}', |
842 | - 'user': '${VAR[wsgi_user]}', |
843 | - 'group': '${VAR[wsgi_group]}', |
844 | - 'args': ( |
845 | - '--name=${unit_name}', |
846 | - '--workers=${VAR[wsgi_workers]}', |
847 | - '--worker-class=${VAR[wsgi_worker_class]}', |
848 | - '--worker-connections=${VAR[wsgi_worker_connections]}', |
849 | - '--max-requests=${VAR[wsgi_max_requests]}', |
850 | - '--backlog=${VAR[wsgi_backlog]}', |
851 | - '--timeout=${VAR[wsgi_timeout]}', |
852 | - '--keep-alive=${VAR[wsgi_keep_alive]}', |
853 | - '--umask=${VAR[wsgi_umask]}', |
854 | - '--bind=0.0.0.0:${VAR[port]}', |
855 | - '--log-file=${VAR[wsgi_log_file]}', |
856 | - '--log-level=${VAR[wsgi_log_level]}', |
857 | - '--access-logfile=${VAR[wsgi_access_logfile]}', |
858 | - '--access-logformat=${VAR[wsgi_access_logformat]}', |
859 | - ${VAR[wsgi_extra]} |
860 | - ${wsgi_wsgi_file} |
861 | - ), |
862 | -} |
863 | -EOF |
864 | - |
865 | - |
866 | -juju-log "start/restart gunicorn" |
867 | - |
868 | -# We need this because when the contained charm configuration or code changed |
869 | -# Gunicorn needs to restart to run the new code. |
870 | -service gunicorn restart |
871 | - |
872 | -juju-log "Opening port: ${VAR[port]}" |
873 | -open-port "${VAR[port]}/tcp" |
874 | |
875 | === target is u'hooks.py' |
876 | === added file 'icon.svg' |
877 | --- icon.svg 1970-01-01 00:00:00 +0000 |
878 | +++ icon.svg 2013-06-05 17:10:31 +0000 |
879 | @@ -0,0 +1,377 @@ |
880 | +<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
881 | +<!-- Created with Inkscape (http://www.inkscape.org/) --> |
882 | + |
883 | +<svg |
884 | + xmlns:dc="http://purl.org/dc/elements/1.1/" |
885 | + xmlns:cc="http://creativecommons.org/ns#" |
886 | + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" |
887 | + xmlns:svg="http://www.w3.org/2000/svg" |
888 | + xmlns="http://www.w3.org/2000/svg" |
889 | + xmlns:xlink="http://www.w3.org/1999/xlink" |
890 | + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" |
891 | + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" |
892 | + width="96" |
893 | + height="96" |
894 | + id="svg6517" |
895 | + version="1.1" |
896 | + inkscape:version="0.48.4 r9939" |
897 | + sodipodi:docname="icon.svg"> |
898 | + <defs |
899 | + id="defs6519"> |
900 | + <linearGradient |
901 | + inkscape:collect="always" |
902 | + xlink:href="#Background" |
903 | + id="linearGradient6461" |
904 | + gradientUnits="userSpaceOnUse" |
905 | + x1="0" |
906 | + y1="970.29498" |
907 | + x2="144" |
908 | + y2="970.29498" |
909 | + gradientTransform="matrix(0,-0.66666669,0.6660448,0,-866.25992,731.29077)" /> |
910 | + <linearGradient |
911 | + id="Background"> |
912 | + <stop |
913 | + id="stop4178" |
914 | + offset="0" |
915 | + style="stop-color:#574c4a;stop-opacity:1" /> |
916 | + <stop |
917 | + id="stop4180" |
918 | + offset="1" |
919 | + style="stop-color:#80716d;stop-opacity:1" /> |
920 | + </linearGradient> |
921 | + <filter |
922 | + style="color-interpolation-filters:sRGB;" |
923 | + inkscape:label="Inner Shadow" |
924 | + id="filter1121"> |
925 | + <feFlood |
926 | + flood-opacity="0.59999999999999998" |
927 | + flood-color="rgb(0,0,0)" |
928 | + result="flood" |
929 | + id="feFlood1123" /> |
930 | + <feComposite |
931 | + in="flood" |
932 | + in2="SourceGraphic" |
933 | + operator="out" |
934 | + result="composite1" |
935 | + id="feComposite1125" /> |
936 | + <feGaussianBlur |
937 | + in="composite1" |
938 | + stdDeviation="1" |
939 | + result="blur" |
940 | + id="feGaussianBlur1127" /> |
941 | + <feOffset |
942 | + dx="0" |
943 | + dy="2" |
944 | + result="offset" |
945 | + id="feOffset1129" /> |
946 | + <feComposite |
947 | + in="offset" |
948 | + in2="SourceGraphic" |
949 | + operator="atop" |
950 | + result="composite2" |
951 | + id="feComposite1131" /> |
952 | + </filter> |
953 | + <filter |
954 | + style="color-interpolation-filters:sRGB;" |
955 | + inkscape:label="Drop Shadow" |
956 | + id="filter950"> |
957 | + <feFlood |
958 | + flood-opacity="0.25" |
959 | + flood-color="rgb(0,0,0)" |
960 | + result="flood" |
961 | + id="feFlood952" /> |
962 | + <feComposite |
963 | + in="flood" |
964 | + in2="SourceGraphic" |
965 | + operator="in" |
966 | + result="composite1" |
967 | + id="feComposite954" /> |
968 | + <feGaussianBlur |
969 | + in="composite1" |
970 | + stdDeviation="1" |
971 | + result="blur" |
972 | + id="feGaussianBlur956" /> |
973 | + <feOffset |
974 | + dx="0" |
975 | + dy="1" |
976 | + result="offset" |
977 | + id="feOffset958" /> |
978 | + <feComposite |
979 | + in="SourceGraphic" |
980 | + in2="offset" |
981 | + operator="over" |
982 | + result="composite2" |
983 | + id="feComposite960" /> |
984 | + </filter> |
985 | + <clipPath |
986 | + clipPathUnits="userSpaceOnUse" |
987 | + id="clipPath873"> |
988 | + <g |
989 | + transform="matrix(0,-0.66666667,0.66604479,0,-258.25992,677.00001)" |
990 | + id="g875" |
991 | + inkscape:label="Layer 1" |
992 | + style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline"> |
993 | + <path |
994 | + style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline" |
995 | + d="m 46.702703,898.22775 50.594594,0 C 138.16216,898.22775 144,904.06497 144,944.92583 l 0,50.73846 c 0,40.86071 -5.83784,46.69791 -46.702703,46.69791 l -50.594594,0 C 5.8378378,1042.3622 0,1036.525 0,995.66429 L 0,944.92583 C 0,904.06497 5.8378378,898.22775 46.702703,898.22775 Z" |
996 | + id="path877" |
997 | + inkscape:connector-curvature="0" |
998 | + sodipodi:nodetypes="sssssssss" /> |
999 | + </g> |
1000 | + </clipPath> |
1001 | + <filter |
1002 | + inkscape:collect="always" |
1003 | + id="filter891" |
1004 | + inkscape:label="Badge Shadow"> |
1005 | + <feGaussianBlur |
1006 | + inkscape:collect="always" |
1007 | + stdDeviation="0.71999962" |
1008 | + id="feGaussianBlur893" /> |
1009 | + </filter> |
1010 | + <clipPath |
1011 | + clipPathUnits="userSpaceOnUse" |
1012 | + id="clipPath874"> |
1013 | + <path |
1014 | + sodipodi:nodetypes="cccssczcssccccscc" |
1015 | + inkscape:connector-curvature="0" |
1016 | + id="path876" |
1017 | + d="m -414.0975,764.53909 c -7.8125,17.9106 -1.95313,49.75167 -1.95313,49.75167 l 12.69531,0 c 0,-2.9851 -0.83592,-4.55148 -1.19017,-6.62319 -3.77705,-22.08828 -2.54859,-29.19801 -0.76295,-29.19801 1.95312,0 10.74219,24.87583 10.74219,24.87583 0,0 1.95313,-0.49751 3.90625,-0.49751 1.95312,0 3.90625,0.49751 3.90625,0.49751 0,0 8.78906,-24.87583 10.74218,-24.87583 1.78565,0 3.01411,7.10973 -0.76293,29.19801 -0.35426,2.07171 -1.19019,3.63809 -1.19019,6.62319 l 12.69532,0 c 0,0 5.85937,-31.84107 -1.95314,-49.75167 l -11.71874,0 c -3.3378,-0.20005 -10.74219,14.9255 -11.71875,14.9255 C -391.63657,779.46459 -399.04095,764.33904 -402.37875,764.53909 Z" |
1018 | + style="opacity:0.47400004;color:#000000;fill:#ff00ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> |
1019 | + </clipPath> |
1020 | + <clipPath |
1021 | + clipPathUnits="userSpaceOnUse" |
1022 | + id="clipPath896"> |
1023 | + <path |
1024 | + sodipodi:nodetypes="cccssczcssccccscc" |
1025 | + inkscape:connector-curvature="0" |
1026 | + id="path898" |
1027 | + d="m -414.0975,764.53909 c -7.8125,17.9106 -1.95313,49.75167 -1.95313,49.75167 l 12.69531,0 c 0,-2.9851 -0.83592,-4.55148 -1.19017,-6.62319 -3.77705,-22.08828 -2.54859,-29.19801 -0.76295,-29.19801 1.95312,0 10.74219,24.87583 10.74219,24.87583 0,0 1.95313,-0.49751 3.90625,-0.49751 1.95312,0 3.90625,0.49751 3.90625,0.49751 0,0 8.78906,-24.87583 10.74218,-24.87583 1.78565,0 3.01411,7.10973 -0.76293,29.19801 -0.35426,2.07171 -1.19019,3.63809 -1.19019,6.62319 l 12.69532,0 c 0,0 5.85937,-31.84107 -1.95314,-49.75167 l -11.71874,0 c -3.3378,-0.20005 -10.74219,14.9255 -11.71875,14.9255 C -391.63657,779.46459 -399.04095,764.33904 -402.37875,764.53909 Z" |
1028 | + style="color:#000000;fill:#ff00ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> |
1029 | + </clipPath> |
1030 | + <linearGradient |
1031 | + id="linearGradient3354-9"> |
1032 | + <stop |
1033 | + id="stop3356-9" |
1034 | + offset="0" |
1035 | + style="stop-color:#959595;stop-opacity:1;" /> |
1036 | + <stop |
1037 | + id="stop3358-9" |
1038 | + offset="1" |
1039 | + style="stop-color:#cccccc;stop-opacity:1;" /> |
1040 | + </linearGradient> |
1041 | + <linearGradient |
1042 | + y2="-32.881535" |
1043 | + x2="-560.61346" |
1044 | + y1="-40.681377" |
1045 | + x1="-403.07309" |
1046 | + gradientUnits="userSpaceOnUse" |
1047 | + id="linearGradient4343" |
1048 | + xlink:href="#linearGradient3354-9" |
1049 | + inkscape:collect="always" /> |
1050 | + <inkscape:perspective |
1051 | + sodipodi:type="inkscape:persp3d" |
1052 | + inkscape:vp_x="0 : 0.5 : 1" |
1053 | + inkscape:vp_y="0 : 1000 : 0" |
1054 | + inkscape:vp_z="1 : 0.5 : 1" |
1055 | + inkscape:persp3d-origin="0.5 : 0.33333333 : 1" |
1056 | + id="perspective4393" /> |
1057 | + <inkscape:perspective |
1058 | + id="perspective4383" |
1059 | + inkscape:persp3d-origin="372.04724 : 350.78739 : 1" |
1060 | + inkscape:vp_z="744.09448 : 526.18109 : 1" |
1061 | + inkscape:vp_y="0 : 1000 : 0" |
1062 | + inkscape:vp_x="0 : 526.18109 : 1" |
1063 | + sodipodi:type="inkscape:persp3d" /> |
1064 | + <linearGradient |
1065 | + inkscape:collect="always" |
1066 | + xlink:href="#linearGradient3354-9" |
1067 | + id="linearGradient3164" |
1068 | + gradientUnits="userSpaceOnUse" |
1069 | + x1="-403.07309" |
1070 | + y1="-40.681377" |
1071 | + x2="-560.61346" |
1072 | + y2="-32.881535" /> |
1073 | + </defs> |
1074 | + <sodipodi:namedview |
1075 | + id="base" |
1076 | + pagecolor="#ffffff" |
1077 | + bordercolor="#666666" |
1078 | + borderopacity="1.0" |
1079 | + inkscape:pageopacity="0.0" |
1080 | + inkscape:pageshadow="2" |
1081 | + inkscape:zoom="2.6077032" |
1082 | + inkscape:cx="-17.529322" |
1083 | + inkscape:cy="74.347537" |
1084 | + inkscape:document-units="px" |
1085 | + inkscape:current-layer="layer1" |
1086 | + showgrid="false" |
1087 | + fit-margin-top="0" |
1088 | + fit-margin-left="0" |
1089 | + fit-margin-right="0" |
1090 | + fit-margin-bottom="0" |
1091 | + inkscape:window-width="1920" |
1092 | + inkscape:window-height="1056" |
1093 | + inkscape:window-x="0" |
1094 | + inkscape:window-y="24" |
1095 | + inkscape:window-maximized="1" |
1096 | + showborder="true" |
1097 | + showguides="false" |
1098 | + inkscape:guide-bbox="true" |
1099 | + inkscape:showpageshadow="false" |
1100 | + inkscape:snap-global="false" |
1101 | + inkscape:snap-bbox="true" |
1102 | + inkscape:bbox-paths="true" |
1103 | + inkscape:bbox-nodes="true" |
1104 | + inkscape:snap-bbox-edge-midpoints="true" |
1105 | + inkscape:snap-bbox-midpoints="true" |
1106 | + inkscape:snap-intersection-paths="true" |
1107 | + inkscape:object-paths="true" |
1108 | + inkscape:object-nodes="true" |
1109 | + inkscape:snap-smooth-nodes="true" |
1110 | + inkscape:snap-midpoints="true" |
1111 | + inkscape:snap-object-midpoints="false" |
1112 | + inkscape:snap-center="false" |
1113 | + inkscape:snap-grids="false" |
1114 | + inkscape:snap-to-guides="false"> |
1115 | + <inkscape:grid |
1116 | + type="xygrid" |
1117 | + id="grid821" /> |
1118 | + <sodipodi:guide |
1119 | + orientation="1,0" |
1120 | + position="16,48" |
1121 | + id="guide823" /> |
1122 | + <sodipodi:guide |
1123 | + orientation="0,1" |
1124 | + position="64,80" |
1125 | + id="guide825" /> |
1126 | + <sodipodi:guide |
1127 | + orientation="1,0" |
1128 | + position="80,40" |
1129 | + id="guide827" /> |
1130 | + <sodipodi:guide |
1131 | + orientation="0,1" |
1132 | + position="64,16" |
1133 | + id="guide829" /> |
1134 | + </sodipodi:namedview> |
1135 | + <metadata |
1136 | + id="metadata6522"> |
1137 | + <rdf:RDF> |
1138 | + <cc:Work |
1139 | + rdf:about=""> |
1140 | + <dc:format>image/svg+xml</dc:format> |
1141 | + <dc:type |
1142 | + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> |
1143 | + <dc:title /> |
1144 | + </cc:Work> |
1145 | + </rdf:RDF> |
1146 | + </metadata> |
1147 | + <g |
1148 | + inkscape:label="BACKGROUND" |
1149 | + inkscape:groupmode="layer" |
1150 | + id="layer1" |
1151 | + transform="translate(268,-635.29076)" |
1152 | + style="display:inline"> |
1153 | + <path |
1154 | + style="fill:url(#linearGradient6461);fill-opacity:1;stroke:none;display:inline;filter:url(#filter1121)" |
1155 | + d="m -268,700.15563 0,-33.72973 c 0,-27.24324 3.88785,-31.13513 31.10302,-31.13513 l 33.79408,0 c 27.21507,0 31.1029,3.89189 31.1029,31.13513 l 0,33.72973 c 0,27.24325 -3.88783,31.13514 -31.1029,31.13514 l -33.79408,0 C -264.11215,731.29077 -268,727.39888 -268,700.15563 Z" |
1156 | + id="path6455" |
1157 | + inkscape:connector-curvature="0" |
1158 | + sodipodi:nodetypes="sssssssss" /> |
1159 | + <g |
1160 | + id="g3160" |
1161 | + transform="matrix(2.5999788,0,0,2.5999788,820.10006,-1019.0997)"> |
1162 | + <path |
1163 | + transform="matrix(0.13863554,0,0,0.13863554,-336.25896,668.28059)" |
1164 | + d="m -339.89735,-32.881535 c 0,4.693003 -53.80552,8.497434 -120.178,8.497434 -66.37247,0 -120.17799,-3.804431 -120.17799,-8.497434 0,-4.693003 53.80552,-8.497433 120.17799,-8.497433 66.37248,0 120.178,3.80443 120.178,8.497433 z" |
1165 | + sodipodi:ry="8.4974337" |
1166 | + sodipodi:rx="120.17799" |
1167 | + sodipodi:cy="-32.881535" |
1168 | + sodipodi:cx="-460.07535" |
1169 | + id="path3423-1" |
1170 | + style="opacity:0.26353838;fill:url(#linearGradient3164);fill-opacity:1;stroke:none" |
1171 | + sodipodi:type="arc" /> |
1172 | + <path |
1173 | + sodipodi:nodetypes="cssssssssssssscccsssssssssssssssssssssssssssssssssscccssssssssssssscccscccssc" |
1174 | + style="fill:#499848;fill-opacity:1" |
1175 | + d="m -402.29334,664.1964 c -0.32255,-0.10267 -0.3237,-0.14801 -0.0265,-1.05026 0.29676,-0.90103 0.16075,-1.73779 -0.38936,-2.39552 -0.74807,-0.89441 -1.10829,-1.6576 -1.22312,-2.59137 -0.0986,-0.80166 -0.12982,-0.86522 -0.4562,-0.92788 -0.21891,-0.042 -0.52133,0.0412 -1.24209,0.34186 -1.04863,0.43742 -1.29839,0.62949 -1.54583,1.18878 -0.12999,0.29381 -0.14561,0.46981 -0.0719,0.80967 0.09,0.41499 0.45901,1.08302 0.59869,1.08391 0.0365,2.5e-4 0.23784,0.13817 0.44752,0.30652 0.34793,0.27934 0.37182,0.32788 0.27336,0.55559 -0.21392,0.49478 -0.72219,1.09466 -0.92748,1.09466 -0.11518,0 -0.58554,-0.32946 -1.10329,-0.77279 -1.01695,-0.87078 -1.05958,-0.95808 -1.16462,-2.38541 -0.12532,-1.70303 0.29346,-2.49783 2.15866,-4.09684 l 1.16985,-1.00289 0.0747,-0.60834 c 0.077,-0.62712 -0.0316,-2.12038 -0.21414,-2.94465 -0.10786,-0.48695 -0.4638,-0.95075 -0.80158,-1.04446 -0.36801,-0.1021 -0.58375,0.10856 -0.86371,0.84341 -0.30207,0.79287 -0.59076,1.06948 -1.1162,1.06948 -0.32728,0 -0.6327,-0.20936 -0.84359,-0.57827 -0.15111,-0.26434 -0.23883,-1.85237 -0.14849,-2.68831 0.0434,-0.40211 0.0459,-0.80924 0.006,-0.90473 -0.0658,-0.15538 -0.63198,-0.49149 -1.96689,-1.16755 -0.24267,-0.1229 -0.52558,-0.28837 -0.62868,-0.36772 -0.10311,-0.0794 -0.37065,-0.22525 -0.59455,-0.32421 -0.50715,-0.22415 -1.82947,-0.99732 -1.70567,-0.99732 0.23419,0 1.22669,0.30547 1.75505,0.54017 0.32253,0.14327 0.63128,0.2605 0.6861,0.2605 0.0548,0 0.67664,0.20016 1.38183,0.44482 0.70519,0.24464 1.34972,0.44481 1.4323,0.44481 0.19613,0 0.22689,-0.16968 0.16404,-0.90503 -0.0285,-0.33404 -0.0273,-0.60735 0.003,-0.60735 0.0301,0 0.16004,0.0645 0.28877,0.14332 0.39331,0.24081 1.23931,0.3883 2.23366,0.38941 0.95078,0.001 2.19183,0.1897 2.66616,0.40525 0.68043,0.30922 1.46971,1.27502 2.47094,3.02357 0.69607,1.21563 1.444,1.73037 2.51588,1.73149 0.43331,5.1e-4 1.58939,-0.17815 3.15493,-0.48741 1.29085,-0.25501 2.99494,-0.31232 3.6564,-0.12298 0.80694,0.23098 1.87391,0.84848 2.75609,1.59507 0.82568,0.69877 0.95284,0.8539 1.46004,1.78116 0.60531,1.10661 1.27338,2.67936 1.86747,4.39633 0.0847,0.24465 0.26345,0.65618 0.39732,0.91452 0.3572,0.68927 0.3394,0.73131 -0.30971,0.73131 -0.70453,0 -1.03494,-0.1111 -1.49679,-0.50328 -0.44295,-0.37615 -0.55866,-0.60628 -1.25198,-2.48999 -0.35513,-0.96487 -0.70083,-1.54387 -0.92179,-1.54387 -0.15139,0 -0.45783,1.03396 -0.45783,1.54481 0,0.36283 0.22737,0.85298 0.81521,1.75738 0.95494,1.4692 1.45727,3.54612 1.2562,5.19382 l -0.0869,0.71171 -0.63641,0.0265 c -1.00197,0.0417 -0.99194,0.0556 -0.99194,-1.37711 0,-1.52531 -0.10388,-1.85564 -0.77699,-2.47079 -0.27959,-0.25551 -0.90686,-0.98147 -1.39393,-1.61323 -0.48708,-0.63176 -0.95354,-1.14866 -1.03657,-1.14866 -0.19081,0 -0.28595,0.51953 -0.29609,1.61676 -0.007,0.72259 -0.0351,0.8449 -0.24832,1.06756 -0.57796,0.60364 -0.98365,1.70279 -1.23376,3.34261 -0.0554,0.36297 -0.10282,0.43087 -0.3381,0.4837 -0.34594,0.0777 -1.30461,0.0904 -1.38722,0.0184 -0.0713,-0.0621 0.0536,-1.04774 0.33682,-2.65912 0.1075,-0.61162 0.22483,-1.43231 0.26074,-1.82375 0.0639,-0.69684 -0.079,-2.0024 -0.24805,-2.26654 -0.0703,-0.10982 -0.42833,-0.13362 -2.10288,-0.13977 -1.10998,-0.004 -2.32728,-0.0406 -2.70511,-0.0811 l -0.68696,-0.0737 0,0.30485 c 0,0.32644 0.0837,0.61418 0.64321,2.21185 0.19707,0.56269 0.48264,1.42052 0.63459,1.90628 l 0.27629,0.88321 -0.23315,0.71813 c -0.12823,0.39498 -0.31854,0.86827 -0.42291,1.05176 -0.18409,0.32363 -0.20568,0.3334 -0.722,0.32658 -0.29273,-0.004 -0.6626,-0.0485 -0.82193,-0.0992 z" |
1176 | + id="path3046-2-3-0" |
1177 | + inkscape:connector-curvature="0" /> |
1178 | + </g> |
1179 | + </g> |
1180 | + <g |
1181 | + inkscape:groupmode="layer" |
1182 | + id="layer3" |
1183 | + inkscape:label="PLACE YOUR PICTOGRAM HERE" |
1184 | + style="display:inline" /> |
1185 | + <g |
1186 | + inkscape:groupmode="layer" |
1187 | + id="layer2" |
1188 | + inkscape:label="BADGE" |
1189 | + style="display:none" |
1190 | + sodipodi:insensitive="true"> |
1191 | + <g |
1192 | + style="display:inline" |
1193 | + transform="translate(-340.00001,-581)" |
1194 | + id="g4394" |
1195 | + clip-path="none"> |
1196 | + <g |
1197 | + id="g855"> |
1198 | + <g |
1199 | + inkscape:groupmode="maskhelper" |
1200 | + id="g870" |
1201 | + clip-path="url(#clipPath873)" |
1202 | + style="opacity:0.6;filter:url(#filter891)"> |
1203 | + <path |
1204 | + transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-237.54282)" |
1205 | + d="m 264,552.36218 c 0,6.62742 -5.37258,12 -12,12 -6.62742,0 -12,-5.37258 -12,-12 0,-6.62741 5.37258,-12 12,-12 6.62742,0 12,5.37259 12,12 z" |
1206 | + sodipodi:ry="12" |
1207 | + sodipodi:rx="12" |
1208 | + sodipodi:cy="552.36218" |
1209 | + sodipodi:cx="252" |
1210 | + id="path844" |
1211 | + style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" |
1212 | + sodipodi:type="arc" /> |
1213 | + </g> |
1214 | + <g |
1215 | + id="g862"> |
1216 | + <path |
1217 | + sodipodi:type="arc" |
1218 | + style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" |
1219 | + id="path4398" |
1220 | + sodipodi:cx="252" |
1221 | + sodipodi:cy="552.36218" |
1222 | + sodipodi:rx="12" |
1223 | + sodipodi:ry="12" |
1224 | + d="m 264,552.36218 c 0,6.62742 -5.37258,12 -12,12 -6.62742,0 -12,-5.37258 -12,-12 0,-6.62741 5.37258,-12 12,-12 6.62742,0 12,5.37259 12,12 z" |
1225 | + transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-238.54282)" /> |
1226 | + <path |
1227 | + transform="matrix(1.25,0,0,1.25,33,-100.45273)" |
1228 | + d="m 264,552.36218 c 0,6.62742 -5.37258,12 -12,12 -6.62742,0 -12,-5.37258 -12,-12 0,-6.62741 5.37258,-12 12,-12 6.62742,0 12,5.37259 12,12 z" |
1229 | + sodipodi:ry="12" |
1230 | + sodipodi:rx="12" |
1231 | + sodipodi:cy="552.36218" |
1232 | + sodipodi:cx="252" |
1233 | + id="path4400" |
1234 | + style="color:#000000;fill:#dd4814;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" |
1235 | + sodipodi:type="arc" /> |
1236 | + <path |
1237 | + sodipodi:type="star" |
1238 | + style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" |
1239 | + id="path4459" |
1240 | + sodipodi:sides="5" |
1241 | + sodipodi:cx="666.19574" |
1242 | + sodipodi:cy="589.50385" |
1243 | + sodipodi:r1="7.2431178" |
1244 | + sodipodi:r2="4.3458705" |
1245 | + sodipodi:arg1="1.0471976" |
1246 | + sodipodi:arg2="1.6755161" |
1247 | + inkscape:flatsided="false" |
1248 | + inkscape:rounded="0.1" |
1249 | + inkscape:randomized="0" |
1250 | + d="m 669.8173,595.77657 c -0.39132,0.22593 -3.62645,-1.90343 -4.07583,-1.95066 -0.44938,-0.0472 -4.05653,1.36297 -4.39232,1.06062 -0.3358,-0.30235 0.68963,-4.03715 0.59569,-4.47913 -0.0939,-0.44198 -2.5498,-3.43681 -2.36602,-3.8496 0.18379,-0.41279 4.05267,-0.59166 4.44398,-0.81759 0.39132,-0.22593 2.48067,-3.48704 2.93005,-3.4398 0.44938,0.0472 1.81505,3.67147 2.15084,3.97382 0.3358,0.30236 4.08294,1.2817 4.17689,1.72369 0.0939,0.44198 -2.9309,2.86076 -3.11469,3.27355 -0.18379,0.41279 0.0427,4.27917 -0.34859,4.5051 z" |
1251 | + transform="matrix(1.511423,-0.16366377,0.16366377,1.511423,-755.37346,-191.93651)" /> |
1252 | + </g> |
1253 | + </g> |
1254 | + </g> |
1255 | + </g> |
1256 | +</svg> |
1257 | |
1258 | === modified file 'metadata.yaml' |
1259 | --- metadata.yaml 2012-09-10 05:28:47 +0000 |
1260 | +++ metadata.yaml 2013-06-05 17:10:31 +0000 |
1261 | @@ -1,6 +1,7 @@ |
1262 | name: gunicorn |
1263 | summary: Gunicorn |
1264 | maintainer: Patrick Hetu <patrick.hetu@gmail.com> |
1265 | +categories: ["misc"] |
1266 | description: | |
1267 | Gunicorn or Green Unicorn is a Python WSGI HTTP Server for UNIX. It's a |
1268 | pre-fork worker model ported from Ruby's Unicorn project. The Gunicorn server |
1269 | @@ -12,8 +13,3 @@ |
1270 | interface: wsgi |
1271 | scope: container |
1272 | optional: true |
1273 | - django-settings: |
1274 | - interface: django |
1275 | - scope: container |
1276 | - optional: true |
1277 | - |
1278 | |
1279 | === modified file 'revision' |
1280 | --- revision 2012-03-27 01:16:08 +0000 |
1281 | +++ revision 2013-06-05 17:10:31 +0000 |
1282 | @@ -1,1 +1,1 @@ |
1283 | -1 |
1284 | +3 |
1285 | |
1286 | === added directory 'templates' |
1287 | === added file 'templates/upstart.tmpl' |
1288 | --- templates/upstart.tmpl 1970-01-01 00:00:00 +0000 |
1289 | +++ templates/upstart.tmpl 2013-06-05 17:10:31 +0000 |
1290 | @@ -0,0 +1,34 @@ |
1291 | +#-------------------------------------------------------------- |
1292 | +# This file is managed by Juju; ANY CHANGES WILL BE OVERWRITTEN |
1293 | +#-------------------------------------------------------------- |
1294 | + |
1295 | +description "Gunicorn daemon for the {{ project_name }} project" |
1296 | + |
1297 | +start on (local-filesystems and net-device-up IFACE=eth0) |
1298 | +stop on runlevel [!12345] |
1299 | + |
1300 | +# If the process quits unexpectadly trigger a respawn |
1301 | +respawn |
1302 | + |
1303 | +setuid {{ wsgi_user }} |
1304 | +setgid {{ wsgi_group }} |
1305 | +chdir {{ working_dir }} |
1306 | + |
1307 | +# This line can be removed and replace with the --pythonpath {{ python_path }} \ |
1308 | +# option with Gunicorn>1.17 |
1309 | +env PYTHONPATH={{ python_path }} |
1310 | + |
1311 | +exec gunicorn \ |
1312 | + --name={{ project_name }} \ |
1313 | + --workers={{ wsgi_workers }} \ |
1314 | + --worker-class={{ wsgi_worker_class }} \ |
1315 | + --worker-connections={{ wsgi_worker_connections }} \ |
1316 | + --max-requests={{ wsgi_max_requests }} \ |
1317 | + --backlog={{ wsgi_backlog }} \ |
1318 | + --timeout={{ wsgi_timeout }} \ |
1319 | + --keep-alive={{ wsgi_keep_alive }} \ |
1320 | + --umask={{ wsgi_umask }} \ |
1321 | + --bind={{ listen_ip }}:{{ port }} \ |
1322 | + --log-file={{ wsgi_log_file }} \ |
1323 | + --log-level={{ wsgi_log_level }} \ |
1324 | + {{ wsgi_extra }} {{ wsgi_wsgi_file }} |
This looks awesome... glad you took the time to do the rewrite.
One thing, can you please make this sub provide a website http relation? This way it can be related to a load balancer or reverse proxy.
There're a couple of other things that `charm proof gunicorn` is turning up:
W: Metadata is missing categories.
W: No icon.svg file.
W: missing recommended hook start
W: missing recommended hook stop
I've filed bugs for a couple of them.
https:/ /bugs.launchpad .net/charms/ +source/ gunicorn/ +bug/1187419 /bugs.launchpad .net/charms/ +source/ gunicorn/ +bug/1187421
https:/
for start/stop you might want to stub out empty hooks to quiet `charm proof` or not... your call.
No need to get all of this into this MP... just the website- relation- joined if you would please.
Thanks!