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 | 1 | Juju charm gunicorn | ||
6 | 2 | =================== | ||
7 | 3 | |||
8 | 4 | :Author: Patrick Hetu <patrick@koumbit.org> | ||
9 | 5 | |||
10 | 6 | How to configure the charm | ||
11 | 7 | -------------------------- | ||
12 | 8 | |||
13 | 9 | To deploy a charm with this subordinate it must minimaly: | ||
14 | 10 | |||
15 | 11 | 1. Provide the wsgi interface. | ||
16 | 12 | 2. Set the `working_dir` relation variable in the wsgi hook. | ||
17 | 13 | |||
18 | 14 | The configuration of Gunicorn will use the variable pass by | ||
19 | 15 | the relation hook first. If there are not define it will | ||
20 | 16 | fallback to the global configuration of the charm. | ||
21 | 17 | |||
22 | 18 | Example deployment | ||
23 | 19 | ------------------ | ||
24 | 20 | |||
25 | 21 | |||
26 | 22 | |||
27 | 23 | 1. Deployment with python-moinmoin for example:: | ||
28 | 24 | |||
29 | 25 | juju bootstrap | ||
30 | 26 | juju deploy --config mywiki_with_wsgi_settings.yaml python-moinmoin | ||
31 | 27 | juju deploy gunicorn | ||
32 | 28 | juju add-relation gunicorn python-moinmoin | ||
33 | 29 | juju expose gunicorn | ||
34 | 30 | |||
35 | 31 | 2. Accessing your new wiki should be ready at:: | ||
36 | 32 | |||
37 | 33 | http://<machine-addr>:8080/ | ||
38 | 34 | |||
39 | 35 | To find out the public address of gunicorn/python-moinmoin, look for it in | ||
40 | 36 | the output of the `juju status` command. | ||
41 | 37 | I recommend using a reverse proxy like Nginx in front of Gunicorn. | ||
42 | 38 | |||
43 | 39 | 0 | ||
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 | 1 | Juju charm gunicorn | ||
49 | 2 | =================== | ||
50 | 3 | |||
51 | 4 | :Author: Patrick Hetu <patrick@koumbit.org> | ||
52 | 5 | |||
53 | 6 | How to configure the charm | ||
54 | 7 | -------------------------- | ||
55 | 8 | |||
56 | 9 | To deploy a charm with this subordinate it must minimaly: | ||
57 | 10 | |||
58 | 11 | 1. Provide the wsgi interface. | ||
59 | 12 | 2. Set the `working_dir` relation variable in the wsgi hook. | ||
60 | 13 | |||
61 | 14 | The configuration of Gunicorn will use the variable pass by | ||
62 | 15 | the relation hook first. If there are not define it will | ||
63 | 16 | fallback to the global configuration of the charm. | ||
64 | 17 | |||
65 | 18 | Example deployment | ||
66 | 19 | ------------------ | ||
67 | 20 | |||
68 | 21 | 1. Deployment with python-moinmoin for example:: | ||
69 | 22 | |||
70 | 23 | juju bootstrap | ||
71 | 24 | juju deploy --config mywiki_with_wsgi_settings.yaml python-moinmoin | ||
72 | 25 | juju deploy gunicorn | ||
73 | 26 | juju add-relation gunicorn python-moinmoin | ||
74 | 27 | juju expose python-moinmoin | ||
75 | 28 | |||
76 | 29 | 2. Accessing your new wiki should be ready at:: | ||
77 | 30 | |||
78 | 31 | http://<machine-addr>:8080/ | ||
79 | 32 | |||
80 | 33 | To find out the public address of gunicorn/python-moinmoin, look for it in | ||
81 | 34 | the output of the `juju status` command. | ||
82 | 35 | I recommend using a reverse proxy like Nginx in front of Gunicorn. | ||
83 | 36 | |||
84 | 37 | Changelog | ||
85 | 38 | --------- | ||
86 | 39 | 3: | ||
87 | 40 | |||
88 | 41 | Notable changes: | ||
89 | 42 | |||
90 | 43 | * Rewrite the charm using python instead of BASH scripts | ||
91 | 44 | * add listen_ip configuration variable | ||
92 | 45 | |||
93 | 46 | Backwards incompatible changes: | ||
94 | 47 | * Remove the Django mode since Gunicorn is not recommending it anymore. | ||
95 | 48 | * Use Upstart to manage daemons | ||
96 | 49 | * no start/stop hook anymore use related charms instead. | ||
97 | 50 | * no configuration change directly on the charm anymore, use related charms instead. | ||
98 | 51 | * no access logging by default | ||
99 | 52 | * exposing a port must now be done in the linked charm instead of this one | ||
100 | 0 | 53 | ||
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 | 1 | options: | 1 | options: |
106 | 2 | wsgi_wsgi_file: | 2 | wsgi_wsgi_file: |
107 | 3 | type: string | 3 | type: string |
108 | 4 | default: "wsgi" | ||
109 | 4 | description: "The name of the WSGI application." | 5 | description: "The name of the WSGI application." |
110 | 5 | wsgi_workers: | 6 | wsgi_workers: |
111 | 6 | type: int | 7 | type: int |
114 | 7 | default: 2 | 8 | default: 0 |
115 | 8 | description: "The number of worker process for handling requests." | 9 | description: "The number of worker process for handling requests. 0 for count(cpu) + 1" |
116 | 9 | wsgi_worker_class: | 10 | wsgi_worker_class: |
117 | 10 | type: string | 11 | type: string |
118 | 11 | default: "sync" | 12 | default: "sync" |
120 | 12 | description: "Gunicorn workers type." | 13 | description: "Gunicorn workers type. Can be: sync, eventlet, gevent, tornado" |
121 | 13 | wsgi_worker_connections: | 14 | wsgi_worker_connections: |
122 | 14 | type: int | 15 | type: int |
123 | 15 | default: 1000 | 16 | default: 1000 |
124 | @@ -25,11 +26,11 @@ | |||
125 | 25 | wsgi_timeout: | 26 | wsgi_timeout: |
126 | 26 | type: int | 27 | type: int |
127 | 27 | default: 30 | 28 | default: 30 |
129 | 28 | description: "" | 29 | description: "Timeout of a request in seconds." |
130 | 29 | wsgi_keep_alive: | 30 | wsgi_keep_alive: |
131 | 30 | type: int | 31 | type: int |
132 | 31 | default: 2 | 32 | default: 2 |
134 | 32 | description: "" | 33 | description: "Keep alive time in seconds." |
135 | 33 | wsgi_umask: | 34 | wsgi_umask: |
136 | 34 | type: string | 35 | type: string |
137 | 35 | default: "0" | 36 | default: "0" |
138 | @@ -45,36 +46,36 @@ | |||
139 | 45 | wsgi_log_file: | 46 | wsgi_log_file: |
140 | 46 | type: string | 47 | type: string |
141 | 47 | default: "-" | 48 | default: "-" |
143 | 48 | description: "The log file to write to. If empty the file would be <your_application_dir>/gunicorn.log" | 49 | description: "The log file to write to. If empty the logs would be handle by upstart." |
144 | 49 | wsgi_log_level: | 50 | wsgi_log_level: |
145 | 50 | type: string | 51 | type: string |
146 | 51 | default: "info" | 52 | default: "info" |
147 | 52 | description: "The granularity of Error log outputs." | 53 | description: "The granularity of Error log outputs." |
148 | 53 | wsgi_access_logfile: | 54 | wsgi_access_logfile: |
149 | 54 | type: string | 55 | type: string |
151 | 55 | default: "-" | 56 | default: "" |
152 | 56 | description: "The Access log file to write to." | 57 | description: "The Access log file to write to." |
153 | 57 | wsgi_access_logformat: | 58 | wsgi_access_logformat: |
154 | 58 | type: string | 59 | type: string |
157 | 59 | default: '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"' | 60 | default: "" |
158 | 60 | description: "The Access log format ." | 61 | description: "The Access log format. Don't forget to escape all quotes and round brackets." |
159 | 61 | wsgi_extra: | 62 | wsgi_extra: |
160 | 62 | type: string | 63 | type: string |
161 | 63 | default: "" | 64 | default: "" |
163 | 64 | description: "Extra settings. For example: {--debug}," | 65 | description: "Space separated extra settings. For example: --debug" |
164 | 65 | wsgi_timestamp: | 66 | wsgi_timestamp: |
165 | 66 | type: string | 67 | type: string |
166 | 67 | default: "" | 68 | default: "" |
172 | 68 | description: "The variable to modify to trigger Gunicorn reload" | 69 | description: "The variable to modify to trigger Gunicorn reload." |
168 | 69 | django_settings: | ||
169 | 70 | type: string | ||
170 | 71 | default: "" | ||
171 | 72 | description: "The Python path to a Django settings module." | ||
173 | 73 | python_path: | 70 | python_path: |
174 | 74 | type: string | 71 | type: string |
175 | 75 | default: "" | 72 | default: "" |
177 | 76 | description: "Set PYTHONPATH environment variable" | 73 | description: "Set an additionnal PYTHONPATH to the project." |
178 | 74 | listen_ip: | ||
179 | 75 | type: string | ||
180 | 76 | default: "0.0.0.0" | ||
181 | 77 | description: "IP adresses that Gunicorn will listen on. By default we listen on all of them." | ||
182 | 77 | port: | 78 | port: |
183 | 78 | type: int | 79 | type: int |
184 | 79 | default: 8080 | 80 | default: 8080 |
186 | 80 | description: "Default listen port" | 81 | description: "Port the application will be listenning." |
187 | 81 | 82 | ||
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 | 1 | #!/bin/bash | ||
193 | 2 | set -e | ||
194 | 3 | |||
195 | 4 | juju-log "reloading Gunicorn" | ||
196 | 5 | |||
197 | 6 | service gunicorn reload || true | ||
198 | 7 | 0 | ||
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 | 1 | #!/usr/bin/env python | ||
208 | 2 | # vim: et ai ts=4 sw=4: | ||
209 | 3 | |||
210 | 4 | import json | ||
211 | 5 | import os | ||
212 | 6 | import re | ||
213 | 7 | import subprocess | ||
214 | 8 | import sys | ||
215 | 9 | import time | ||
216 | 10 | from pwd import getpwnam | ||
217 | 11 | from grp import getgrnam | ||
218 | 12 | |||
219 | 13 | CHARM_PACKAGES = ["gunicorn"] | ||
220 | 14 | |||
221 | 15 | INJECTED_WARNING = """ | ||
222 | 16 | #------------------------------------------------------------------------------ | ||
223 | 17 | # The following is the import code for the settings directory injected by Juju | ||
224 | 18 | #------------------------------------------------------------------------------ | ||
225 | 19 | """ | ||
226 | 20 | |||
227 | 21 | |||
228 | 22 | ############################################################################### | ||
229 | 23 | # Supporting functions | ||
230 | 24 | ############################################################################### | ||
231 | 25 | MSG_CRITICAL = "CRITICAL" | ||
232 | 26 | MSG_DEBUG = "DEBUG" | ||
233 | 27 | MSG_INFO = "INFO" | ||
234 | 28 | MSG_ERROR = "ERROR" | ||
235 | 29 | MSG_WARNING = "WARNING" | ||
236 | 30 | |||
237 | 31 | |||
238 | 32 | def juju_log(level, msg): | ||
239 | 33 | subprocess.call(['juju-log', '-l', level, msg]) | ||
240 | 34 | |||
241 | 35 | #------------------------------------------------------------------------------ | ||
242 | 36 | # run: Run a command, return the output | ||
243 | 37 | #------------------------------------------------------------------------------ | ||
244 | 38 | def run(command, exit_on_error=True, cwd=None): | ||
245 | 39 | try: | ||
246 | 40 | juju_log(MSG_DEBUG, command) | ||
247 | 41 | return subprocess.check_output( | ||
248 | 42 | command, stderr=subprocess.STDOUT, shell=True, cwd=cwd) | ||
249 | 43 | except subprocess.CalledProcessError, e: | ||
250 | 44 | juju_log(MSG_ERROR, "status=%d, output=%s" % (e.returncode, e.output)) | ||
251 | 45 | if exit_on_error: | ||
252 | 46 | sys.exit(e.returncode) | ||
253 | 47 | else: | ||
254 | 48 | raise | ||
255 | 49 | |||
256 | 50 | |||
257 | 51 | #------------------------------------------------------------------------------ | ||
258 | 52 | # install_file: install a file resource. overwites existing files. | ||
259 | 53 | #------------------------------------------------------------------------------ | ||
260 | 54 | def install_file(contents, dest, owner="root", group="root", mode=0600): | ||
261 | 55 | uid = getpwnam(owner)[2] | ||
262 | 56 | gid = getgrnam(group)[2] | ||
263 | 57 | dest_fd = os.open(dest, os.O_WRONLY | os.O_TRUNC | os.O_CREAT, mode) | ||
264 | 58 | os.fchown(dest_fd, uid, gid) | ||
265 | 59 | with os.fdopen(dest_fd, 'w') as destfile: | ||
266 | 60 | destfile.write(str(contents)) | ||
267 | 61 | |||
268 | 62 | |||
269 | 63 | #------------------------------------------------------------------------------ | ||
270 | 64 | # install_dir: create a directory | ||
271 | 65 | #------------------------------------------------------------------------------ | ||
272 | 66 | def install_dir(dirname, owner="root", group="root", mode=0700): | ||
273 | 67 | command = \ | ||
274 | 68 | '/usr/bin/install -o {} -g {} -m {} -d {}'.format(owner, group, oct(mode), | ||
275 | 69 | dirname) | ||
276 | 70 | return run(command) | ||
277 | 71 | |||
278 | 72 | #------------------------------------------------------------------------------ | ||
279 | 73 | # config_get: Returns a dictionary containing all of the config information | ||
280 | 74 | # Optional parameter: scope | ||
281 | 75 | # scope: limits the scope of the returned configuration to the | ||
282 | 76 | # desired config item. | ||
283 | 77 | #------------------------------------------------------------------------------ | ||
284 | 78 | def config_get(scope=None): | ||
285 | 79 | try: | ||
286 | 80 | config_cmd_line = ['config-get'] | ||
287 | 81 | if scope is not None: | ||
288 | 82 | config_cmd_line.append(scope) | ||
289 | 83 | config_cmd_line.append('--format=json') | ||
290 | 84 | config_data = json.loads(subprocess.check_output(config_cmd_line)) | ||
291 | 85 | except: | ||
292 | 86 | config_data = None | ||
293 | 87 | finally: | ||
294 | 88 | return(config_data) | ||
295 | 89 | |||
296 | 90 | |||
297 | 91 | #------------------------------------------------------------------------------ | ||
298 | 92 | # relation_json: Returns json-formatted relation data | ||
299 | 93 | # Optional parameters: scope, relation_id | ||
300 | 94 | # scope: limits the scope of the returned data to the | ||
301 | 95 | # desired item. | ||
302 | 96 | # unit_name: limits the data ( and optionally the scope ) | ||
303 | 97 | # to the specified unit | ||
304 | 98 | # relation_id: specify relation id for out of context usage. | ||
305 | 99 | #------------------------------------------------------------------------------ | ||
306 | 100 | def relation_json(scope=None, unit_name=None, relation_id=None): | ||
307 | 101 | command = ['relation-get', '--format=json'] | ||
308 | 102 | if relation_id is not None: | ||
309 | 103 | command.extend(('-r', relation_id)) | ||
310 | 104 | if scope is not None: | ||
311 | 105 | command.append(scope) | ||
312 | 106 | else: | ||
313 | 107 | command.append('-') | ||
314 | 108 | if unit_name is not None: | ||
315 | 109 | command.append(unit_name) | ||
316 | 110 | output = subprocess.check_output(command, stderr=subprocess.STDOUT) | ||
317 | 111 | return output or None | ||
318 | 112 | |||
319 | 113 | |||
320 | 114 | #------------------------------------------------------------------------------ | ||
321 | 115 | # relation_get: Returns a dictionary containing the relation information | ||
322 | 116 | # Optional parameters: scope, relation_id | ||
323 | 117 | # scope: limits the scope of the returned data to the | ||
324 | 118 | # desired item. | ||
325 | 119 | # unit_name: limits the data ( and optionally the scope ) | ||
326 | 120 | # to the specified unit | ||
327 | 121 | #------------------------------------------------------------------------------ | ||
328 | 122 | def relation_get(scope=None, unit_name=None, relation_id=None): | ||
329 | 123 | j = relation_json(scope, unit_name, relation_id) | ||
330 | 124 | if j: | ||
331 | 125 | return json.loads(j) | ||
332 | 126 | else: | ||
333 | 127 | return None | ||
334 | 128 | |||
335 | 129 | |||
336 | 130 | def relation_set(keyvalues, relation_id=None): | ||
337 | 131 | args = [] | ||
338 | 132 | if relation_id: | ||
339 | 133 | args.extend(['-r', relation_id]) | ||
340 | 134 | args.extend(["{}='{}'".format(k, v or '') for k, v in keyvalues.items()]) | ||
341 | 135 | run("relation-set {}".format(' '.join(args))) | ||
342 | 136 | |||
343 | 137 | ## Posting json to relation-set doesn't seem to work as documented? | ||
344 | 138 | ## Bug #1116179 | ||
345 | 139 | ## | ||
346 | 140 | ## cmd = ['relation-set'] | ||
347 | 141 | ## if relation_id: | ||
348 | 142 | ## cmd.extend(['-r', relation_id]) | ||
349 | 143 | ## p = Popen( | ||
350 | 144 | ## cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, | ||
351 | 145 | ## stderr=subprocess.PIPE) | ||
352 | 146 | ## (out, err) = p.communicate(json.dumps(keyvalues)) | ||
353 | 147 | ## if p.returncode: | ||
354 | 148 | ## juju_log(MSG_ERROR, err) | ||
355 | 149 | ## sys.exit(1) | ||
356 | 150 | ## juju_log(MSG_DEBUG, "relation-set {}".format(repr(keyvalues))) | ||
357 | 151 | |||
358 | 152 | |||
359 | 153 | def relation_list(relation_id=None): | ||
360 | 154 | """Return the list of units participating in the relation.""" | ||
361 | 155 | if relation_id is None: | ||
362 | 156 | relation_id = os.environ['JUJU_RELATION_ID'] | ||
363 | 157 | cmd = ['relation-list', '--format=json', '-r', relation_id] | ||
364 | 158 | json_units = subprocess.check_output(cmd).strip() | ||
365 | 159 | if json_units: | ||
366 | 160 | return json.loads(subprocess.check_output(cmd)) | ||
367 | 161 | return [] | ||
368 | 162 | |||
369 | 163 | |||
370 | 164 | #------------------------------------------------------------------------------ | ||
371 | 165 | # relation_ids: Returns a list of relation ids | ||
372 | 166 | # optional parameters: relation_type | ||
373 | 167 | # relation_type: return relations only of this type | ||
374 | 168 | #------------------------------------------------------------------------------ | ||
375 | 169 | def relation_ids(relation_types=('db',)): | ||
376 | 170 | # accept strings or iterators | ||
377 | 171 | if isinstance(relation_types, basestring): | ||
378 | 172 | reltypes = [relation_types, ] | ||
379 | 173 | else: | ||
380 | 174 | reltypes = relation_types | ||
381 | 175 | relids = [] | ||
382 | 176 | for reltype in reltypes: | ||
383 | 177 | relid_cmd_line = ['relation-ids', '--format=json', reltype] | ||
384 | 178 | json_relids = subprocess.check_output(relid_cmd_line).strip() | ||
385 | 179 | if json_relids: | ||
386 | 180 | relids.extend(json.loads(json_relids)) | ||
387 | 181 | return relids | ||
388 | 182 | |||
389 | 183 | |||
390 | 184 | #------------------------------------------------------------------------------ | ||
391 | 185 | # relation_get_all: Returns a dictionary containing the relation information | ||
392 | 186 | # optional parameters: relation_type | ||
393 | 187 | # relation_type: limits the scope of the returned data to the | ||
394 | 188 | # desired item. | ||
395 | 189 | #------------------------------------------------------------------------------ | ||
396 | 190 | def relation_get_all(*args, **kwargs): | ||
397 | 191 | relation_data = [] | ||
398 | 192 | relids = relation_ids(*args, **kwargs) | ||
399 | 193 | for relid in relids: | ||
400 | 194 | units_cmd_line = ['relation-list', '--format=json', '-r', relid] | ||
401 | 195 | json_units = subprocess.check_output(units_cmd_line).strip() | ||
402 | 196 | if json_units: | ||
403 | 197 | for unit in json.loads(json_units): | ||
404 | 198 | unit_data = \ | ||
405 | 199 | json.loads(relation_json(relation_id=relid, | ||
406 | 200 | unit_name=unit)) | ||
407 | 201 | for key in unit_data: | ||
408 | 202 | if key.endswith('-list'): | ||
409 | 203 | unit_data[key] = unit_data[key].split() | ||
410 | 204 | unit_data['relation-id'] = relid | ||
411 | 205 | unit_data['unit'] = unit | ||
412 | 206 | relation_data.append(unit_data) | ||
413 | 207 | return relation_data | ||
414 | 208 | |||
415 | 209 | def apt_get_update(): | ||
416 | 210 | cmd_line = ['apt-get', 'update'] | ||
417 | 211 | return(subprocess.call(cmd_line)) | ||
418 | 212 | |||
419 | 213 | |||
420 | 214 | #------------------------------------------------------------------------------ | ||
421 | 215 | # apt_get_install( packages ): Installs package(s) | ||
422 | 216 | #------------------------------------------------------------------------------ | ||
423 | 217 | def apt_get_install(packages=None): | ||
424 | 218 | if packages is None: | ||
425 | 219 | return(False) | ||
426 | 220 | cmd_line = ['apt-get', '-y', 'install', '-qq'] | ||
427 | 221 | if isinstance(packages, list): | ||
428 | 222 | cmd_line.extend(packages) | ||
429 | 223 | else: | ||
430 | 224 | cmd_line.append(packages) | ||
431 | 225 | return(subprocess.call(cmd_line)) | ||
432 | 226 | |||
433 | 227 | |||
434 | 228 | #------------------------------------------------------------------------------ | ||
435 | 229 | # pip_install( package ): Installs a python package | ||
436 | 230 | #------------------------------------------------------------------------------ | ||
437 | 231 | def pip_install(packages=None, upgrade=False): | ||
438 | 232 | cmd_line = ['pip', 'install'] | ||
439 | 233 | if packages is None: | ||
440 | 234 | return(False) | ||
441 | 235 | if upgrade: | ||
442 | 236 | cmd_line.append('-u') | ||
443 | 237 | if packages.startswith('svn+') or packages.startswith('git+') or \ | ||
444 | 238 | packages.startswith('hg+') or packages.startswith('bzr+'): | ||
445 | 239 | cmd_line.append('-e') | ||
446 | 240 | cmd_line.append(packages) | ||
447 | 241 | return run(cmd_line) | ||
448 | 242 | |||
449 | 243 | #------------------------------------------------------------------------------ | ||
450 | 244 | # pip_install_req( path ): Installs a requirements file | ||
451 | 245 | #------------------------------------------------------------------------------ | ||
452 | 246 | def pip_install_req(path=None, upgrade=False): | ||
453 | 247 | cmd_line = ['pip', 'install'] | ||
454 | 248 | if path is None: | ||
455 | 249 | return(False) | ||
456 | 250 | if upgrade: | ||
457 | 251 | cmd_line.append('-u') | ||
458 | 252 | cmd_line.append('-r') | ||
459 | 253 | cmd_line.append(path) | ||
460 | 254 | cwd = os.path.dirname(path) | ||
461 | 255 | return run(cmd_line) | ||
462 | 256 | |||
463 | 257 | #------------------------------------------------------------------------------ | ||
464 | 258 | # open_port: Convenience function to open a port in juju to | ||
465 | 259 | # expose a service | ||
466 | 260 | #------------------------------------------------------------------------------ | ||
467 | 261 | def open_port(port=None, protocol="TCP"): | ||
468 | 262 | if port is None: | ||
469 | 263 | return(None) | ||
470 | 264 | return(subprocess.call(['open-port', "%d/%s" % | ||
471 | 265 | (int(port), protocol)])) | ||
472 | 266 | |||
473 | 267 | |||
474 | 268 | #------------------------------------------------------------------------------ | ||
475 | 269 | # close_port: Convenience function to close a port in juju to | ||
476 | 270 | # unexpose a service | ||
477 | 271 | #------------------------------------------------------------------------------ | ||
478 | 272 | def close_port(port=None, protocol="TCP"): | ||
479 | 273 | if port is None: | ||
480 | 274 | return(None) | ||
481 | 275 | return(subprocess.call(['close-port', "%d/%s" % | ||
482 | 276 | (int(port), protocol)])) | ||
483 | 277 | |||
484 | 278 | |||
485 | 279 | #------------------------------------------------------------------------------ | ||
486 | 280 | # update_service_ports: Convenience function that evaluate the old and new | ||
487 | 281 | # service ports to decide which ports need to be | ||
488 | 282 | # opened and which to close | ||
489 | 283 | #------------------------------------------------------------------------------ | ||
490 | 284 | def update_service_port(old_service_port=None, new_service_port=None): | ||
491 | 285 | if old_service_port is None or new_service_port is None: | ||
492 | 286 | return(None) | ||
493 | 287 | if new_service_port != old_service_port: | ||
494 | 288 | close_port(old_service_port) | ||
495 | 289 | open_port(new_service_port) | ||
496 | 290 | |||
497 | 291 | # | ||
498 | 292 | # Utils | ||
499 | 293 | # | ||
500 | 294 | |||
501 | 295 | def install_or_append(contents, dest, owner="root", group="root", mode=0600): | ||
502 | 296 | if os.path.exists(dest): | ||
503 | 297 | uid = getpwnam(owner)[2] | ||
504 | 298 | gid = getgrnam(group)[2] | ||
505 | 299 | dest_fd = os.open(dest, os.O_APPEND, mode) | ||
506 | 300 | os.fchown(dest_fd, uid, gid) | ||
507 | 301 | with os.fdopen(dest_fd, 'a') as destfile: | ||
508 | 302 | destfile.write(str(contents)) | ||
509 | 303 | else: | ||
510 | 304 | install_file(contents, dest, owner, group, mode) | ||
511 | 305 | |||
512 | 306 | def token_sql_safe(value): | ||
513 | 307 | # Only allow alphanumeric + underscore in database identifiers | ||
514 | 308 | if re.search('[^A-Za-z0-9_]', value): | ||
515 | 309 | return False | ||
516 | 310 | return True | ||
517 | 311 | |||
518 | 312 | def sanitize(s): | ||
519 | 313 | s = s.replace(':', '_') | ||
520 | 314 | s = s.replace('-', '_') | ||
521 | 315 | s = s.replace('/', '_') | ||
522 | 316 | s = s.replace('"', '_') | ||
523 | 317 | s = s.replace("'", '_') | ||
524 | 318 | return s | ||
525 | 319 | |||
526 | 320 | def user_name(relid, remote_unit, admin=False, schema=False): | ||
527 | 321 | components = [sanitize(relid), sanitize(remote_unit)] | ||
528 | 322 | if admin: | ||
529 | 323 | components.append("admin") | ||
530 | 324 | elif schema: | ||
531 | 325 | components.append("schema") | ||
532 | 326 | return "_".join(components) | ||
533 | 327 | |||
534 | 328 | def get_relation_host(): | ||
535 | 329 | remote_host = run("relation-get ip") | ||
536 | 330 | if not remote_host: | ||
537 | 331 | # remote unit $JUJU_REMOTE_UNIT uses deprecated 'ip=' component of | ||
538 | 332 | # interface. | ||
539 | 333 | remote_host = run("relation-get private-address") | ||
540 | 334 | return remote_host | ||
541 | 335 | |||
542 | 336 | |||
543 | 337 | def get_unit_host(): | ||
544 | 338 | this_host = run("unit-get private-address") | ||
545 | 339 | return this_host.strip() | ||
546 | 340 | |||
547 | 341 | def process_template(template_name, template_vars, destination): | ||
548 | 342 | # --- exported service configuration file | ||
549 | 343 | from jinja2 import Environment, FileSystemLoader | ||
550 | 344 | template_env = Environment( | ||
551 | 345 | loader=FileSystemLoader(os.path.join(os.environ['CHARM_DIR'], | ||
552 | 346 | 'templates'))) | ||
553 | 347 | |||
554 | 348 | template = \ | ||
555 | 349 | template_env.get_template(template_name).render(template_vars) | ||
556 | 350 | |||
557 | 351 | with open(destination, 'w') as inject_file: | ||
558 | 352 | inject_file.write(str(template)) | ||
559 | 353 | |||
560 | 354 | def append_template(template_name, template_vars, path, try_append=False): | ||
561 | 355 | |||
562 | 356 | # --- exported service configuration file | ||
563 | 357 | from jinja2 import Environment, FileSystemLoader | ||
564 | 358 | template_env = Environment( | ||
565 | 359 | loader=FileSystemLoader(os.path.join(os.environ['CHARM_DIR'], | ||
566 | 360 | 'templates'))) | ||
567 | 361 | |||
568 | 362 | template = \ | ||
569 | 363 | template_env.get_template(template_name).render(template_vars) | ||
570 | 364 | |||
571 | 365 | append = False | ||
572 | 366 | if os.path.exists(path): | ||
573 | 367 | with open(path, 'r') as inject_file: | ||
574 | 368 | if not str(template) in inject_file: | ||
575 | 369 | append = True | ||
576 | 370 | else: | ||
577 | 371 | append = True | ||
578 | 372 | |||
579 | 373 | if append == True: | ||
580 | 374 | with open(path, 'a') as inject_file: | ||
581 | 375 | inject_file.write(INJECTED_WARNING) | ||
582 | 376 | inject_file.write(str(template)) | ||
583 | 377 | |||
584 | 378 | |||
585 | 379 | |||
586 | 380 | ############################################################################### | ||
587 | 381 | # Hook functions | ||
588 | 382 | ############################################################################### | ||
589 | 383 | def install(): | ||
590 | 384 | |||
591 | 385 | for retry in xrange(0,24): | ||
592 | 386 | if apt_get_install(CHARM_PACKAGES): | ||
593 | 387 | time.sleep(10) | ||
594 | 388 | else: | ||
595 | 389 | break | ||
596 | 390 | |||
597 | 391 | def upgrade(): | ||
598 | 392 | |||
599 | 393 | apt_get_update() | ||
600 | 394 | for retry in xrange(0,24): | ||
601 | 395 | if apt_get_install(CHARM_PACKAGES): | ||
602 | 396 | time.sleep(10) | ||
603 | 397 | else: | ||
604 | 398 | break | ||
605 | 399 | |||
606 | 400 | def wsgi_file_relation_joined_changed(): | ||
607 | 401 | wsgi_config = config_data | ||
608 | 402 | relation_id = os.environ['JUJU_RELATION_ID'] | ||
609 | 403 | juju_log(MSG_INFO, "JUJU_RELATION_ID: %s".format(relation_id)) | ||
610 | 404 | |||
611 | 405 | remote_unit_name = sanitize(os.environ['JUJU_REMOTE_UNIT'].split('/')[0]) | ||
612 | 406 | juju_log(MSG_INFO, "JUJU_REMOTE_UNIT: %s".format(remote_unit_name)) | ||
613 | 407 | wsgi_config['unit_name'] = remote_unit_name | ||
614 | 408 | |||
615 | 409 | project_conf = '/etc/init/%s.conf' % remote_unit_name | ||
616 | 410 | |||
617 | 411 | working_dir = relation_get('working_dir') | ||
618 | 412 | if not working_dir: | ||
619 | 413 | return | ||
620 | 414 | |||
621 | 415 | wsgi_config['working_dir'] = working_dir | ||
622 | 416 | wsgi_config['project_name'] = remote_unit_name | ||
623 | 417 | |||
624 | 418 | for v in wsgi_config.keys(): | ||
625 | 419 | if v.startswith('wsgi_') or v in ['python_path', 'listen_ip', 'port']: | ||
626 | 420 | upstream_value = relation_get(v) | ||
627 | 421 | if upstream_value: | ||
628 | 422 | wsgi_config[v] = upstream_value | ||
629 | 423 | |||
630 | 424 | if wsgi_config['wsgi_worker_class'] == 'eventlet': | ||
631 | 425 | apt_get_install('python-eventlet') | ||
632 | 426 | elif wsgi_config['wsgi_worker_class'] == 'gevent': | ||
633 | 427 | apt_get_install('python-gevent') | ||
634 | 428 | elif wsgi_config['wsgi_worker_class'] == 'tornado': | ||
635 | 429 | apt_get_install('python-tornado') | ||
636 | 430 | |||
637 | 431 | if wsgi_config['wsgi_workers'] == 0: | ||
638 | 432 | res = run('python -c "import multiprocessing ; print(multiprocessing.cpu_count())"') | ||
639 | 433 | wsgi_config['wsgi_workers'] = int(res) + 1 | ||
640 | 434 | |||
641 | 435 | if wsgi_config['wsgi_access_logfile']: | ||
642 | 436 | wsgi_config['wsgi_extra'] = " ".join([ | ||
643 | 437 | wsgi_config['wsgi_extra'], | ||
644 | 438 | '--access-logformat=%s' % wsgi_config['wsgi_access_logfile'], | ||
645 | 439 | '--access-logformat="%s"' % wsgi_config['wsgi_access_logformat'] | ||
646 | 440 | ]) | ||
647 | 441 | |||
648 | 442 | wsgi_config['wsgi_wsgi_file'] = wsgi_config['wsgi_wsgi_file'] | ||
649 | 443 | |||
650 | 444 | process_template('upstart.tmpl', wsgi_config, project_conf) | ||
651 | 445 | |||
652 | 446 | |||
653 | 447 | # We need this because when the contained charm configuration or code changed | ||
654 | 448 | # Gunicorn needs to restart to run the new code. | ||
655 | 449 | run("service %s restart || service %s start" % (remote_unit_name, remote_unit_name)) | ||
656 | 450 | |||
657 | 451 | |||
658 | 452 | def wsgi_file_relation_broken(): | ||
659 | 453 | remote_unit_name = sanitize(os.environ['JUJU_REMOTE_UNIT'].split('/')[0]) | ||
660 | 454 | |||
661 | 455 | run('service %s stop' % remote_unit_name) | ||
662 | 456 | run('rm /etc/init/%s.conf' % remote_unit_name) | ||
663 | 457 | |||
664 | 458 | |||
665 | 459 | ############################################################################### | ||
666 | 460 | # Global variables | ||
667 | 461 | ############################################################################### | ||
668 | 462 | config_data = config_get() | ||
669 | 463 | juju_log(MSG_DEBUG, "got config: %s" % str(config_data)) | ||
670 | 464 | |||
671 | 465 | unit_name = os.environ['JUJU_UNIT_NAME'].split('/')[0] | ||
672 | 466 | |||
673 | 467 | hook_name = os.path.basename(sys.argv[0]) | ||
674 | 468 | |||
675 | 469 | ############################################################################### | ||
676 | 470 | # Main section | ||
677 | 471 | ############################################################################### | ||
678 | 472 | def main(): | ||
679 | 473 | juju_log(MSG_INFO, "Running {} hook".format(hook_name)) | ||
680 | 474 | if hook_name == "install": | ||
681 | 475 | install() | ||
682 | 476 | |||
683 | 477 | elif hook_name == "upgrade-charm": | ||
684 | 478 | upgrade() | ||
685 | 479 | |||
686 | 480 | elif hook_name in ["wsgi-file-relation-joined", "wsgi-file-relation-changed"]: | ||
687 | 481 | wsgi_file_relation_joined_changed() | ||
688 | 482 | |||
689 | 483 | elif hook_name == "wsgi-file-relation-broken": | ||
690 | 484 | wsgi_file_relation_broken() | ||
691 | 485 | |||
692 | 486 | else: | ||
693 | 487 | print "Unknown hook {}".format(hook_name) | ||
694 | 488 | raise SystemExit(1) | ||
695 | 489 | |||
696 | 490 | if __name__ == '__main__': | ||
697 | 491 | raise SystemExit(main()) | ||
698 | 0 | 492 | ||
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 | 1 | #!/bin/bash | ||
704 | 2 | set -e | ||
705 | 3 | |||
706 | 4 | # Suboridnate charm hooks can run in parallel with other charm hooks | ||
707 | 5 | # on the same unit Bug #1068624. So a hook may fail to get a apt-get lock. | ||
708 | 6 | COUNTER=0 | ||
709 | 7 | APT_LOCKED=1 | ||
710 | 8 | while true; do | ||
711 | 9 | lsof /var/lib/dpkg/lock > /dev/null 2>&1 || APT_LOCKED=0 | ||
712 | 10 | if [[ $APT_LOCKED -eq 0 ]]; then | ||
713 | 11 | apt-get install --no-install-recommends -q -y gunicorn \ | ||
714 | 12 | python-eventlet \ | ||
715 | 13 | python-gevent \ | ||
716 | 14 | python-tornado | ||
717 | 15 | break | ||
718 | 16 | fi | ||
719 | 17 | if [[ $COUNTER -gt 5 ]]; then | ||
720 | 18 | echo "Failed to obtain apt lock to install python-amqplib" | ||
721 | 19 | exit 1 | ||
722 | 20 | fi | ||
723 | 21 | sleep 60 | ||
724 | 22 | COUNTER=$[$COUNTER+1] | ||
725 | 23 | done | ||
726 | 24 | |||
727 | 25 | 0 | ||
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 | 1 | #!/bin/bash | ||
734 | 2 | set -e | ||
735 | 3 | |||
736 | 4 | service gunicorn start || service gunicorn restart | ||
737 | 5 | 0 | ||
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 | 1 | #!/bin/bash | ||
743 | 2 | set -e | ||
744 | 3 | |||
745 | 4 | service gunicorn stop || true | ||
746 | 5 | 0 | ||
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 | 1 | #!/bin/bash | ||
752 | 2 | set -e | ||
753 | 3 | |||
754 | 4 | home=`dirname $0` | ||
755 | 5 | |||
756 | 6 | juju-log "Upgrading charm by running install hook again." | ||
757 | 7 | $home/install | ||
758 | 8 | |||
759 | 9 | juju-log "Upgrading charm, running config-changed hook again." | ||
760 | 10 | $home/config-changed | ||
761 | 11 | |||
762 | 12 | 0 | ||
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 | 1 | #!/bin/bash | ||
773 | 2 | set -e | ||
774 | 3 | |||
775 | 4 | unit_name=${JUJU_REMOTE_UNIT//\//-} | ||
776 | 5 | working_dir=$(relation-get working_dir) | ||
777 | 6 | |||
778 | 7 | if [ -z "$working_dir" ] ; then | ||
779 | 8 | juju-log "No working_dir yet: skipping." | ||
780 | 9 | exit 0 # wait for future handshake | ||
781 | 10 | fi | ||
782 | 11 | |||
783 | 12 | 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 | 13 | |||
785 | 14 | if [[ $JUJU_RELATION_ID =~ django.* ]]; then | ||
786 | 15 | mode=django | ||
787 | 16 | elif [[ $JUJU_RELATION_ID =~ wsgi.* ]]; then | ||
788 | 17 | mode=wsgi | ||
789 | 18 | else | ||
790 | 19 | juju-log "Unknown mode ($JUJU_RELATION_ID)" | ||
791 | 20 | exit 1 | ||
792 | 21 | fi | ||
793 | 22 | |||
794 | 23 | juju-log "Running in ${mode} mode" | ||
795 | 24 | |||
796 | 25 | |||
797 | 26 | declare -A VAR | ||
798 | 27 | for v in $variables;do | ||
799 | 28 | VAR[$v]=$(relation-get $v) | ||
800 | 29 | if [ -z "${VAR[$v]}" ] ; then | ||
801 | 30 | VAR[$v]=$(config-get $v) | ||
802 | 31 | fi | ||
803 | 32 | done | ||
804 | 33 | |||
805 | 34 | juju-log "Got variables: ${VAR[@]}" | ||
806 | 35 | |||
807 | 36 | |||
808 | 37 | if [ -z "$wsgi_error_logfile" ] ; then | ||
809 | 38 | error_logfile="${working_dir}/gunicorn.log" | ||
810 | 39 | fi | ||
811 | 40 | |||
812 | 41 | if [ -n "$VAR[django_settings]" ] ; then | ||
813 | 42 | django_settings="'django_settings': '${VAR[django_settings]}'," | ||
814 | 43 | fi | ||
815 | 44 | |||
816 | 45 | # If running in wsgi mode then set a default wsgi file | ||
817 | 46 | if [[ $mode -eq "wsgi" ]] && [[ $(relation-get wsgi_wsgi_file) -eq "" ]] ; then | ||
818 | 47 | VAR[wsgi_wsgi_file]="wsgi" | ||
819 | 48 | fi | ||
820 | 49 | |||
821 | 50 | if [[ -n ${VAR[wsgi_wsgi_file]} ]] ; then | ||
822 | 51 | wsgi_wsgi_file="'${VAR[wsgi_wsgi_file]}'," | ||
823 | 52 | fi | ||
824 | 53 | |||
825 | 54 | if [[ -z ${VAR[python_path]} ]] ; then | ||
826 | 55 | python_path=${working_dir} | ||
827 | 56 | else | ||
828 | 57 | python_path="${VAR[python_path]}" | ||
829 | 58 | fi | ||
830 | 59 | |||
831 | 60 | juju-log "Writing config file: /etc/gunicorn.d/${unit_name}.conf" | ||
832 | 61 | |||
833 | 62 | cat > /etc/gunicorn.d/${unit_name}.conf <<EOF | ||
834 | 63 | CONFIG = { | ||
835 | 64 | 'mode': '${mode}', | ||
836 | 65 | 'environment': { | ||
837 | 66 | 'PYTHONPATH': '${python_path}', | ||
838 | 67 | ${VAR[env_extra]} | ||
839 | 68 | }, | ||
840 | 69 | ${django_settings} | ||
841 | 70 | 'working_dir': '${working_dir}', | ||
842 | 71 | 'user': '${VAR[wsgi_user]}', | ||
843 | 72 | 'group': '${VAR[wsgi_group]}', | ||
844 | 73 | 'args': ( | ||
845 | 74 | '--name=${unit_name}', | ||
846 | 75 | '--workers=${VAR[wsgi_workers]}', | ||
847 | 76 | '--worker-class=${VAR[wsgi_worker_class]}', | ||
848 | 77 | '--worker-connections=${VAR[wsgi_worker_connections]}', | ||
849 | 78 | '--max-requests=${VAR[wsgi_max_requests]}', | ||
850 | 79 | '--backlog=${VAR[wsgi_backlog]}', | ||
851 | 80 | '--timeout=${VAR[wsgi_timeout]}', | ||
852 | 81 | '--keep-alive=${VAR[wsgi_keep_alive]}', | ||
853 | 82 | '--umask=${VAR[wsgi_umask]}', | ||
854 | 83 | '--bind=0.0.0.0:${VAR[port]}', | ||
855 | 84 | '--log-file=${VAR[wsgi_log_file]}', | ||
856 | 85 | '--log-level=${VAR[wsgi_log_level]}', | ||
857 | 86 | '--access-logfile=${VAR[wsgi_access_logfile]}', | ||
858 | 87 | '--access-logformat=${VAR[wsgi_access_logformat]}', | ||
859 | 88 | ${VAR[wsgi_extra]} | ||
860 | 89 | ${wsgi_wsgi_file} | ||
861 | 90 | ), | ||
862 | 91 | } | ||
863 | 92 | EOF | ||
864 | 93 | |||
865 | 94 | |||
866 | 95 | juju-log "start/restart gunicorn" | ||
867 | 96 | |||
868 | 97 | # We need this because when the contained charm configuration or code changed | ||
869 | 98 | # Gunicorn needs to restart to run the new code. | ||
870 | 99 | service gunicorn restart | ||
871 | 100 | |||
872 | 101 | juju-log "Opening port: ${VAR[port]}" | ||
873 | 102 | open-port "${VAR[port]}/tcp" | ||
874 | 103 | 0 | ||
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 | 1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
881 | 2 | <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||
882 | 3 | |||
883 | 4 | <svg | ||
884 | 5 | xmlns:dc="http://purl.org/dc/elements/1.1/" | ||
885 | 6 | xmlns:cc="http://creativecommons.org/ns#" | ||
886 | 7 | xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||
887 | 8 | xmlns:svg="http://www.w3.org/2000/svg" | ||
888 | 9 | xmlns="http://www.w3.org/2000/svg" | ||
889 | 10 | xmlns:xlink="http://www.w3.org/1999/xlink" | ||
890 | 11 | xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||
891 | 12 | xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||
892 | 13 | width="96" | ||
893 | 14 | height="96" | ||
894 | 15 | id="svg6517" | ||
895 | 16 | version="1.1" | ||
896 | 17 | inkscape:version="0.48.4 r9939" | ||
897 | 18 | sodipodi:docname="icon.svg"> | ||
898 | 19 | <defs | ||
899 | 20 | id="defs6519"> | ||
900 | 21 | <linearGradient | ||
901 | 22 | inkscape:collect="always" | ||
902 | 23 | xlink:href="#Background" | ||
903 | 24 | id="linearGradient6461" | ||
904 | 25 | gradientUnits="userSpaceOnUse" | ||
905 | 26 | x1="0" | ||
906 | 27 | y1="970.29498" | ||
907 | 28 | x2="144" | ||
908 | 29 | y2="970.29498" | ||
909 | 30 | gradientTransform="matrix(0,-0.66666669,0.6660448,0,-866.25992,731.29077)" /> | ||
910 | 31 | <linearGradient | ||
911 | 32 | id="Background"> | ||
912 | 33 | <stop | ||
913 | 34 | id="stop4178" | ||
914 | 35 | offset="0" | ||
915 | 36 | style="stop-color:#574c4a;stop-opacity:1" /> | ||
916 | 37 | <stop | ||
917 | 38 | id="stop4180" | ||
918 | 39 | offset="1" | ||
919 | 40 | style="stop-color:#80716d;stop-opacity:1" /> | ||
920 | 41 | </linearGradient> | ||
921 | 42 | <filter | ||
922 | 43 | style="color-interpolation-filters:sRGB;" | ||
923 | 44 | inkscape:label="Inner Shadow" | ||
924 | 45 | id="filter1121"> | ||
925 | 46 | <feFlood | ||
926 | 47 | flood-opacity="0.59999999999999998" | ||
927 | 48 | flood-color="rgb(0,0,0)" | ||
928 | 49 | result="flood" | ||
929 | 50 | id="feFlood1123" /> | ||
930 | 51 | <feComposite | ||
931 | 52 | in="flood" | ||
932 | 53 | in2="SourceGraphic" | ||
933 | 54 | operator="out" | ||
934 | 55 | result="composite1" | ||
935 | 56 | id="feComposite1125" /> | ||
936 | 57 | <feGaussianBlur | ||
937 | 58 | in="composite1" | ||
938 | 59 | stdDeviation="1" | ||
939 | 60 | result="blur" | ||
940 | 61 | id="feGaussianBlur1127" /> | ||
941 | 62 | <feOffset | ||
942 | 63 | dx="0" | ||
943 | 64 | dy="2" | ||
944 | 65 | result="offset" | ||
945 | 66 | id="feOffset1129" /> | ||
946 | 67 | <feComposite | ||
947 | 68 | in="offset" | ||
948 | 69 | in2="SourceGraphic" | ||
949 | 70 | operator="atop" | ||
950 | 71 | result="composite2" | ||
951 | 72 | id="feComposite1131" /> | ||
952 | 73 | </filter> | ||
953 | 74 | <filter | ||
954 | 75 | style="color-interpolation-filters:sRGB;" | ||
955 | 76 | inkscape:label="Drop Shadow" | ||
956 | 77 | id="filter950"> | ||
957 | 78 | <feFlood | ||
958 | 79 | flood-opacity="0.25" | ||
959 | 80 | flood-color="rgb(0,0,0)" | ||
960 | 81 | result="flood" | ||
961 | 82 | id="feFlood952" /> | ||
962 | 83 | <feComposite | ||
963 | 84 | in="flood" | ||
964 | 85 | in2="SourceGraphic" | ||
965 | 86 | operator="in" | ||
966 | 87 | result="composite1" | ||
967 | 88 | id="feComposite954" /> | ||
968 | 89 | <feGaussianBlur | ||
969 | 90 | in="composite1" | ||
970 | 91 | stdDeviation="1" | ||
971 | 92 | result="blur" | ||
972 | 93 | id="feGaussianBlur956" /> | ||
973 | 94 | <feOffset | ||
974 | 95 | dx="0" | ||
975 | 96 | dy="1" | ||
976 | 97 | result="offset" | ||
977 | 98 | id="feOffset958" /> | ||
978 | 99 | <feComposite | ||
979 | 100 | in="SourceGraphic" | ||
980 | 101 | in2="offset" | ||
981 | 102 | operator="over" | ||
982 | 103 | result="composite2" | ||
983 | 104 | id="feComposite960" /> | ||
984 | 105 | </filter> | ||
985 | 106 | <clipPath | ||
986 | 107 | clipPathUnits="userSpaceOnUse" | ||
987 | 108 | id="clipPath873"> | ||
988 | 109 | <g | ||
989 | 110 | transform="matrix(0,-0.66666667,0.66604479,0,-258.25992,677.00001)" | ||
990 | 111 | id="g875" | ||
991 | 112 | inkscape:label="Layer 1" | ||
992 | 113 | style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline"> | ||
993 | 114 | <path | ||
994 | 115 | style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline" | ||
995 | 116 | 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 | 117 | id="path877" | ||
997 | 118 | inkscape:connector-curvature="0" | ||
998 | 119 | sodipodi:nodetypes="sssssssss" /> | ||
999 | 120 | </g> | ||
1000 | 121 | </clipPath> | ||
1001 | 122 | <filter | ||
1002 | 123 | inkscape:collect="always" | ||
1003 | 124 | id="filter891" | ||
1004 | 125 | inkscape:label="Badge Shadow"> | ||
1005 | 126 | <feGaussianBlur | ||
1006 | 127 | inkscape:collect="always" | ||
1007 | 128 | stdDeviation="0.71999962" | ||
1008 | 129 | id="feGaussianBlur893" /> | ||
1009 | 130 | </filter> | ||
1010 | 131 | <clipPath | ||
1011 | 132 | clipPathUnits="userSpaceOnUse" | ||
1012 | 133 | id="clipPath874"> | ||
1013 | 134 | <path | ||
1014 | 135 | sodipodi:nodetypes="cccssczcssccccscc" | ||
1015 | 136 | inkscape:connector-curvature="0" | ||
1016 | 137 | id="path876" | ||
1017 | 138 | 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 | 139 | 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 | 140 | </clipPath> | ||
1020 | 141 | <clipPath | ||
1021 | 142 | clipPathUnits="userSpaceOnUse" | ||
1022 | 143 | id="clipPath896"> | ||
1023 | 144 | <path | ||
1024 | 145 | sodipodi:nodetypes="cccssczcssccccscc" | ||
1025 | 146 | inkscape:connector-curvature="0" | ||
1026 | 147 | id="path898" | ||
1027 | 148 | 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 | 149 | 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 | 150 | </clipPath> | ||
1030 | 151 | <linearGradient | ||
1031 | 152 | id="linearGradient3354-9"> | ||
1032 | 153 | <stop | ||
1033 | 154 | id="stop3356-9" | ||
1034 | 155 | offset="0" | ||
1035 | 156 | style="stop-color:#959595;stop-opacity:1;" /> | ||
1036 | 157 | <stop | ||
1037 | 158 | id="stop3358-9" | ||
1038 | 159 | offset="1" | ||
1039 | 160 | style="stop-color:#cccccc;stop-opacity:1;" /> | ||
1040 | 161 | </linearGradient> | ||
1041 | 162 | <linearGradient | ||
1042 | 163 | y2="-32.881535" | ||
1043 | 164 | x2="-560.61346" | ||
1044 | 165 | y1="-40.681377" | ||
1045 | 166 | x1="-403.07309" | ||
1046 | 167 | gradientUnits="userSpaceOnUse" | ||
1047 | 168 | id="linearGradient4343" | ||
1048 | 169 | xlink:href="#linearGradient3354-9" | ||
1049 | 170 | inkscape:collect="always" /> | ||
1050 | 171 | <inkscape:perspective | ||
1051 | 172 | sodipodi:type="inkscape:persp3d" | ||
1052 | 173 | inkscape:vp_x="0 : 0.5 : 1" | ||
1053 | 174 | inkscape:vp_y="0 : 1000 : 0" | ||
1054 | 175 | inkscape:vp_z="1 : 0.5 : 1" | ||
1055 | 176 | inkscape:persp3d-origin="0.5 : 0.33333333 : 1" | ||
1056 | 177 | id="perspective4393" /> | ||
1057 | 178 | <inkscape:perspective | ||
1058 | 179 | id="perspective4383" | ||
1059 | 180 | inkscape:persp3d-origin="372.04724 : 350.78739 : 1" | ||
1060 | 181 | inkscape:vp_z="744.09448 : 526.18109 : 1" | ||
1061 | 182 | inkscape:vp_y="0 : 1000 : 0" | ||
1062 | 183 | inkscape:vp_x="0 : 526.18109 : 1" | ||
1063 | 184 | sodipodi:type="inkscape:persp3d" /> | ||
1064 | 185 | <linearGradient | ||
1065 | 186 | inkscape:collect="always" | ||
1066 | 187 | xlink:href="#linearGradient3354-9" | ||
1067 | 188 | id="linearGradient3164" | ||
1068 | 189 | gradientUnits="userSpaceOnUse" | ||
1069 | 190 | x1="-403.07309" | ||
1070 | 191 | y1="-40.681377" | ||
1071 | 192 | x2="-560.61346" | ||
1072 | 193 | y2="-32.881535" /> | ||
1073 | 194 | </defs> | ||
1074 | 195 | <sodipodi:namedview | ||
1075 | 196 | id="base" | ||
1076 | 197 | pagecolor="#ffffff" | ||
1077 | 198 | bordercolor="#666666" | ||
1078 | 199 | borderopacity="1.0" | ||
1079 | 200 | inkscape:pageopacity="0.0" | ||
1080 | 201 | inkscape:pageshadow="2" | ||
1081 | 202 | inkscape:zoom="2.6077032" | ||
1082 | 203 | inkscape:cx="-17.529322" | ||
1083 | 204 | inkscape:cy="74.347537" | ||
1084 | 205 | inkscape:document-units="px" | ||
1085 | 206 | inkscape:current-layer="layer1" | ||
1086 | 207 | showgrid="false" | ||
1087 | 208 | fit-margin-top="0" | ||
1088 | 209 | fit-margin-left="0" | ||
1089 | 210 | fit-margin-right="0" | ||
1090 | 211 | fit-margin-bottom="0" | ||
1091 | 212 | inkscape:window-width="1920" | ||
1092 | 213 | inkscape:window-height="1056" | ||
1093 | 214 | inkscape:window-x="0" | ||
1094 | 215 | inkscape:window-y="24" | ||
1095 | 216 | inkscape:window-maximized="1" | ||
1096 | 217 | showborder="true" | ||
1097 | 218 | showguides="false" | ||
1098 | 219 | inkscape:guide-bbox="true" | ||
1099 | 220 | inkscape:showpageshadow="false" | ||
1100 | 221 | inkscape:snap-global="false" | ||
1101 | 222 | inkscape:snap-bbox="true" | ||
1102 | 223 | inkscape:bbox-paths="true" | ||
1103 | 224 | inkscape:bbox-nodes="true" | ||
1104 | 225 | inkscape:snap-bbox-edge-midpoints="true" | ||
1105 | 226 | inkscape:snap-bbox-midpoints="true" | ||
1106 | 227 | inkscape:snap-intersection-paths="true" | ||
1107 | 228 | inkscape:object-paths="true" | ||
1108 | 229 | inkscape:object-nodes="true" | ||
1109 | 230 | inkscape:snap-smooth-nodes="true" | ||
1110 | 231 | inkscape:snap-midpoints="true" | ||
1111 | 232 | inkscape:snap-object-midpoints="false" | ||
1112 | 233 | inkscape:snap-center="false" | ||
1113 | 234 | inkscape:snap-grids="false" | ||
1114 | 235 | inkscape:snap-to-guides="false"> | ||
1115 | 236 | <inkscape:grid | ||
1116 | 237 | type="xygrid" | ||
1117 | 238 | id="grid821" /> | ||
1118 | 239 | <sodipodi:guide | ||
1119 | 240 | orientation="1,0" | ||
1120 | 241 | position="16,48" | ||
1121 | 242 | id="guide823" /> | ||
1122 | 243 | <sodipodi:guide | ||
1123 | 244 | orientation="0,1" | ||
1124 | 245 | position="64,80" | ||
1125 | 246 | id="guide825" /> | ||
1126 | 247 | <sodipodi:guide | ||
1127 | 248 | orientation="1,0" | ||
1128 | 249 | position="80,40" | ||
1129 | 250 | id="guide827" /> | ||
1130 | 251 | <sodipodi:guide | ||
1131 | 252 | orientation="0,1" | ||
1132 | 253 | position="64,16" | ||
1133 | 254 | id="guide829" /> | ||
1134 | 255 | </sodipodi:namedview> | ||
1135 | 256 | <metadata | ||
1136 | 257 | id="metadata6522"> | ||
1137 | 258 | <rdf:RDF> | ||
1138 | 259 | <cc:Work | ||
1139 | 260 | rdf:about=""> | ||
1140 | 261 | <dc:format>image/svg+xml</dc:format> | ||
1141 | 262 | <dc:type | ||
1142 | 263 | rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||
1143 | 264 | <dc:title /> | ||
1144 | 265 | </cc:Work> | ||
1145 | 266 | </rdf:RDF> | ||
1146 | 267 | </metadata> | ||
1147 | 268 | <g | ||
1148 | 269 | inkscape:label="BACKGROUND" | ||
1149 | 270 | inkscape:groupmode="layer" | ||
1150 | 271 | id="layer1" | ||
1151 | 272 | transform="translate(268,-635.29076)" | ||
1152 | 273 | style="display:inline"> | ||
1153 | 274 | <path | ||
1154 | 275 | style="fill:url(#linearGradient6461);fill-opacity:1;stroke:none;display:inline;filter:url(#filter1121)" | ||
1155 | 276 | 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 | 277 | id="path6455" | ||
1157 | 278 | inkscape:connector-curvature="0" | ||
1158 | 279 | sodipodi:nodetypes="sssssssss" /> | ||
1159 | 280 | <g | ||
1160 | 281 | id="g3160" | ||
1161 | 282 | transform="matrix(2.5999788,0,0,2.5999788,820.10006,-1019.0997)"> | ||
1162 | 283 | <path | ||
1163 | 284 | transform="matrix(0.13863554,0,0,0.13863554,-336.25896,668.28059)" | ||
1164 | 285 | 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 | 286 | sodipodi:ry="8.4974337" | ||
1166 | 287 | sodipodi:rx="120.17799" | ||
1167 | 288 | sodipodi:cy="-32.881535" | ||
1168 | 289 | sodipodi:cx="-460.07535" | ||
1169 | 290 | id="path3423-1" | ||
1170 | 291 | style="opacity:0.26353838;fill:url(#linearGradient3164);fill-opacity:1;stroke:none" | ||
1171 | 292 | sodipodi:type="arc" /> | ||
1172 | 293 | <path | ||
1173 | 294 | sodipodi:nodetypes="cssssssssssssscccsssssssssssssssssssssssssssssssssscccssssssssssssscccscccssc" | ||
1174 | 295 | style="fill:#499848;fill-opacity:1" | ||
1175 | 296 | 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 | 297 | id="path3046-2-3-0" | ||
1177 | 298 | inkscape:connector-curvature="0" /> | ||
1178 | 299 | </g> | ||
1179 | 300 | </g> | ||
1180 | 301 | <g | ||
1181 | 302 | inkscape:groupmode="layer" | ||
1182 | 303 | id="layer3" | ||
1183 | 304 | inkscape:label="PLACE YOUR PICTOGRAM HERE" | ||
1184 | 305 | style="display:inline" /> | ||
1185 | 306 | <g | ||
1186 | 307 | inkscape:groupmode="layer" | ||
1187 | 308 | id="layer2" | ||
1188 | 309 | inkscape:label="BADGE" | ||
1189 | 310 | style="display:none" | ||
1190 | 311 | sodipodi:insensitive="true"> | ||
1191 | 312 | <g | ||
1192 | 313 | style="display:inline" | ||
1193 | 314 | transform="translate(-340.00001,-581)" | ||
1194 | 315 | id="g4394" | ||
1195 | 316 | clip-path="none"> | ||
1196 | 317 | <g | ||
1197 | 318 | id="g855"> | ||
1198 | 319 | <g | ||
1199 | 320 | inkscape:groupmode="maskhelper" | ||
1200 | 321 | id="g870" | ||
1201 | 322 | clip-path="url(#clipPath873)" | ||
1202 | 323 | style="opacity:0.6;filter:url(#filter891)"> | ||
1203 | 324 | <path | ||
1204 | 325 | transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-237.54282)" | ||
1205 | 326 | 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 | 327 | sodipodi:ry="12" | ||
1207 | 328 | sodipodi:rx="12" | ||
1208 | 329 | sodipodi:cy="552.36218" | ||
1209 | 330 | sodipodi:cx="252" | ||
1210 | 331 | id="path844" | ||
1211 | 332 | 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 | 333 | sodipodi:type="arc" /> | ||
1213 | 334 | </g> | ||
1214 | 335 | <g | ||
1215 | 336 | id="g862"> | ||
1216 | 337 | <path | ||
1217 | 338 | sodipodi:type="arc" | ||
1218 | 339 | 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 | 340 | id="path4398" | ||
1220 | 341 | sodipodi:cx="252" | ||
1221 | 342 | sodipodi:cy="552.36218" | ||
1222 | 343 | sodipodi:rx="12" | ||
1223 | 344 | sodipodi:ry="12" | ||
1224 | 345 | 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 | 346 | transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-238.54282)" /> | ||
1226 | 347 | <path | ||
1227 | 348 | transform="matrix(1.25,0,0,1.25,33,-100.45273)" | ||
1228 | 349 | 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 | 350 | sodipodi:ry="12" | ||
1230 | 351 | sodipodi:rx="12" | ||
1231 | 352 | sodipodi:cy="552.36218" | ||
1232 | 353 | sodipodi:cx="252" | ||
1233 | 354 | id="path4400" | ||
1234 | 355 | 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 | 356 | sodipodi:type="arc" /> | ||
1236 | 357 | <path | ||
1237 | 358 | sodipodi:type="star" | ||
1238 | 359 | 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 | 360 | id="path4459" | ||
1240 | 361 | sodipodi:sides="5" | ||
1241 | 362 | sodipodi:cx="666.19574" | ||
1242 | 363 | sodipodi:cy="589.50385" | ||
1243 | 364 | sodipodi:r1="7.2431178" | ||
1244 | 365 | sodipodi:r2="4.3458705" | ||
1245 | 366 | sodipodi:arg1="1.0471976" | ||
1246 | 367 | sodipodi:arg2="1.6755161" | ||
1247 | 368 | inkscape:flatsided="false" | ||
1248 | 369 | inkscape:rounded="0.1" | ||
1249 | 370 | inkscape:randomized="0" | ||
1250 | 371 | 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 | 372 | transform="matrix(1.511423,-0.16366377,0.16366377,1.511423,-755.37346,-191.93651)" /> | ||
1252 | 373 | </g> | ||
1253 | 374 | </g> | ||
1254 | 375 | </g> | ||
1255 | 376 | </g> | ||
1256 | 377 | </svg> | ||
1257 | 0 | 378 | ||
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 | 1 | name: gunicorn | 1 | name: gunicorn |
1263 | 2 | summary: Gunicorn | 2 | summary: Gunicorn |
1264 | 3 | maintainer: Patrick Hetu <patrick.hetu@gmail.com> | 3 | maintainer: Patrick Hetu <patrick.hetu@gmail.com> |
1265 | 4 | categories: ["misc"] | ||
1266 | 4 | description: | | 5 | description: | |
1267 | 5 | Gunicorn or Green Unicorn is a Python WSGI HTTP Server for UNIX. It's a | 6 | Gunicorn or Green Unicorn is a Python WSGI HTTP Server for UNIX. It's a |
1268 | 6 | pre-fork worker model ported from Ruby's Unicorn project. The Gunicorn server | 7 | pre-fork worker model ported from Ruby's Unicorn project. The Gunicorn server |
1269 | @@ -12,8 +13,3 @@ | |||
1270 | 12 | interface: wsgi | 13 | interface: wsgi |
1271 | 13 | scope: container | 14 | scope: container |
1272 | 14 | optional: true | 15 | optional: true |
1273 | 15 | django-settings: | ||
1274 | 16 | interface: django | ||
1275 | 17 | scope: container | ||
1276 | 18 | optional: true | ||
1277 | 19 | |||
1278 | 20 | 16 | ||
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 @@ | |||
1284 | 1 | 1 | 1 | 3 |
1285 | 2 | 2 | ||
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 | 1 | #-------------------------------------------------------------- | ||
1292 | 2 | # This file is managed by Juju; ANY CHANGES WILL BE OVERWRITTEN | ||
1293 | 3 | #-------------------------------------------------------------- | ||
1294 | 4 | |||
1295 | 5 | description "Gunicorn daemon for the {{ project_name }} project" | ||
1296 | 6 | |||
1297 | 7 | start on (local-filesystems and net-device-up IFACE=eth0) | ||
1298 | 8 | stop on runlevel [!12345] | ||
1299 | 9 | |||
1300 | 10 | # If the process quits unexpectadly trigger a respawn | ||
1301 | 11 | respawn | ||
1302 | 12 | |||
1303 | 13 | setuid {{ wsgi_user }} | ||
1304 | 14 | setgid {{ wsgi_group }} | ||
1305 | 15 | chdir {{ working_dir }} | ||
1306 | 16 | |||
1307 | 17 | # This line can be removed and replace with the --pythonpath {{ python_path }} \ | ||
1308 | 18 | # option with Gunicorn>1.17 | ||
1309 | 19 | env PYTHONPATH={{ python_path }} | ||
1310 | 20 | |||
1311 | 21 | exec gunicorn \ | ||
1312 | 22 | --name={{ project_name }} \ | ||
1313 | 23 | --workers={{ wsgi_workers }} \ | ||
1314 | 24 | --worker-class={{ wsgi_worker_class }} \ | ||
1315 | 25 | --worker-connections={{ wsgi_worker_connections }} \ | ||
1316 | 26 | --max-requests={{ wsgi_max_requests }} \ | ||
1317 | 27 | --backlog={{ wsgi_backlog }} \ | ||
1318 | 28 | --timeout={{ wsgi_timeout }} \ | ||
1319 | 29 | --keep-alive={{ wsgi_keep_alive }} \ | ||
1320 | 30 | --umask={{ wsgi_umask }} \ | ||
1321 | 31 | --bind={{ listen_ip }}:{{ port }} \ | ||
1322 | 32 | --log-file={{ wsgi_log_file }} \ | ||
1323 | 33 | --log-level={{ wsgi_log_level }} \ | ||
1324 | 34 | {{ 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!