Merge lp:~corey.bryant/charm-helpers/wsgi into lp:charm-helpers
- wsgi
- Merge into devel
Status: | Merged |
---|---|
Merged at revision: | 670 |
Proposed branch: | lp:~corey.bryant/charm-helpers/wsgi |
Merge into: | lp:charm-helpers |
Diff against target: |
294 lines (+232/-0) 5 files modified
charmhelpers/contrib/openstack/context.py (+40/-0) charmhelpers/contrib/openstack/templates/wsgi-openstack-api.conf (+100/-0) charmhelpers/contrib/openstack/utils.py (+29/-0) tests/contrib/openstack/test_openstack_utils.py (+34/-0) tests/contrib/openstack/test_os_contexts.py (+29/-0) |
To merge this branch: | bzr merge lp:~corey.bryant/charm-helpers/wsgi |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Liam Young (community) | Approve | ||
charmers | Pending | ||
Review via email:
|
Commit message
Add WSGI context and template for OpenStack APIs
Description of the change
- 667. By Liam Young
-
[gnuoy, r=ajkavanagh] Fixes for amulet memcache service checks.
1) Use 'service' not 'status' for checking status of memcached on pre-systemd systems as its not an upstart script.
2) Trusty memcache uses ip6-localhost as its listen address because it does not understand ::1 - 668. By Stuart Bishop
-
[hloeung,r=stub] Add timeout to rsync and capture rsync error messages
- 669. By Liam Young
-
[gnuoy, r=ajkavanagh] Add option package var to Memcache context to allow contect to lookup Openstack release without using Openstack-origin which may not be present. enable_memcache also had to be extended to take a package option to support this
- 670. By Corey Bryant
-
Add WSGI context and template for OpenStack APIs
- 671. By Corey Bryant
-
Add unit test for WSGI context.
- 672. By Corey Bryant
-
Add unit test for git_determine_*()
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Corey Bryant (corey.bryant) wrote : | # |
Thanks for the review Liam. I've added unit tests.
Preview Diff
1 | === modified file 'charmhelpers/contrib/openstack/context.py' |
2 | --- charmhelpers/contrib/openstack/context.py 2016-12-14 15:10:32 +0000 |
3 | +++ charmhelpers/contrib/openstack/context.py 2016-12-14 18:51:33 +0000 |
4 | @@ -14,6 +14,7 @@ |
5 | |
6 | import glob |
7 | import json |
8 | +import math |
9 | import os |
10 | import re |
11 | import time |
12 | @@ -90,6 +91,8 @@ |
13 | from charmhelpers.contrib.openstack.utils import ( |
14 | config_flags_parser, |
15 | get_host_ip, |
16 | + git_determine_usr_bin, |
17 | + git_determine_python_path, |
18 | enable_memcache, |
19 | ) |
20 | from charmhelpers.core.unitdata import kv |
21 | @@ -1208,6 +1211,43 @@ |
22 | return ctxt |
23 | |
24 | |
25 | +class WSGIWorkerConfigContext(WorkerConfigContext): |
26 | + |
27 | + def __init__(self, name=None, script=None, admin_script=None, |
28 | + public_script=None, process_weight=1.00, |
29 | + admin_process_weight=0.75, public_process_weight=0.25): |
30 | + self.service_name = name |
31 | + self.user = name |
32 | + self.group = name |
33 | + self.script = script |
34 | + self.admin_script = admin_script |
35 | + self.public_script = public_script |
36 | + self.process_weight = process_weight |
37 | + self.admin_process_weight = admin_process_weight |
38 | + self.public_process_weight = public_process_weight |
39 | + |
40 | + def __call__(self): |
41 | + multiplier = config('worker-multiplier') or 1 |
42 | + total_processes = self.num_cpus * multiplier |
43 | + ctxt = { |
44 | + "service_name": self.service_name, |
45 | + "user": self.user, |
46 | + "group": self.group, |
47 | + "script": self.script, |
48 | + "admin_script": self.admin_script, |
49 | + "public_script": self.public_script, |
50 | + "processes": int(math.ceil(self.process_weight * total_processes)), |
51 | + "admin_processes": int(math.ceil(self.admin_process_weight * |
52 | + total_processes)), |
53 | + "public_processes": int(math.ceil(self.public_process_weight * |
54 | + total_processes)), |
55 | + "threads": 1, |
56 | + "usr_bin": git_determine_usr_bin(), |
57 | + "python_path": git_determine_python_path(), |
58 | + } |
59 | + return ctxt |
60 | + |
61 | + |
62 | class ZeroMQContext(OSContextGenerator): |
63 | interfaces = ['zeromq-configuration'] |
64 | |
65 | |
66 | === added file 'charmhelpers/contrib/openstack/templates/wsgi-openstack-api.conf' |
67 | --- charmhelpers/contrib/openstack/templates/wsgi-openstack-api.conf 1970-01-01 00:00:00 +0000 |
68 | +++ charmhelpers/contrib/openstack/templates/wsgi-openstack-api.conf 2016-12-14 18:51:33 +0000 |
69 | @@ -0,0 +1,100 @@ |
70 | +# Configuration file maintained by Juju. Local changes may be overwritten. |
71 | + |
72 | +{% if port -%} |
73 | +Listen {{ port }} |
74 | +{% endif -%} |
75 | + |
76 | +{% if admin_port -%} |
77 | +Listen {{ admin_port }} |
78 | +{% endif -%} |
79 | + |
80 | +{% if public_port -%} |
81 | +Listen {{ public_port }} |
82 | +{% endif -%} |
83 | + |
84 | +{% if port -%} |
85 | +<VirtualHost *:{{ port }}> |
86 | + WSGIDaemonProcess {{ service_name }} processes={{ processes }} threads={{ threads }} user={{ service_name }} group={{ service_name }} \ |
87 | +{% if python_path -%} |
88 | + python-path={{ python_path }} \ |
89 | +{% endif -%} |
90 | + display-name=%{GROUP} |
91 | + WSGIProcessGroup {{ service_name }} |
92 | + WSGIScriptAlias / {{ script }} |
93 | + WSGIApplicationGroup %{GLOBAL} |
94 | + WSGIPassAuthorization On |
95 | + <IfVersion >= 2.4> |
96 | + ErrorLogFormat "%{cu}t %M" |
97 | + </IfVersion> |
98 | + ErrorLog /var/log/apache2/{{ service_name }}_error.log |
99 | + CustomLog /var/log/apache2/{{ service_name }}_access.log combined |
100 | + |
101 | + <Directory {{ usr_bin }}> |
102 | + <IfVersion >= 2.4> |
103 | + Require all granted |
104 | + </IfVersion> |
105 | + <IfVersion < 2.4> |
106 | + Order allow,deny |
107 | + Allow from all |
108 | + </IfVersion> |
109 | + </Directory> |
110 | +</VirtualHost> |
111 | +{% endif -%} |
112 | + |
113 | +{% if admin_port -%} |
114 | +<VirtualHost *:{{ admin_port }}> |
115 | + WSGIDaemonProcess {{ service_name }}-admin processes={{ admin_processes }} threads={{ threads }} user={{ service_name }} group={{ service_name }} \ |
116 | +{% if python_path -%} |
117 | + python-path={{ python_path }} \ |
118 | +{% endif -%} |
119 | + display-name=%{GROUP} |
120 | + WSGIProcessGroup {{ service_name }}-admin |
121 | + WSGIScriptAlias / {{ admin_script }} |
122 | + WSGIApplicationGroup %{GLOBAL} |
123 | + WSGIPassAuthorization On |
124 | + <IfVersion >= 2.4> |
125 | + ErrorLogFormat "%{cu}t %M" |
126 | + </IfVersion> |
127 | + ErrorLog /var/log/apache2/{{ service_name }}_error.log |
128 | + CustomLog /var/log/apache2/{{ service_name }}_access.log combined |
129 | + |
130 | + <Directory {{ usr_bin }}> |
131 | + <IfVersion >= 2.4> |
132 | + Require all granted |
133 | + </IfVersion> |
134 | + <IfVersion < 2.4> |
135 | + Order allow,deny |
136 | + Allow from all |
137 | + </IfVersion> |
138 | + </Directory> |
139 | +</VirtualHost> |
140 | +{% endif -%} |
141 | + |
142 | +{% if public_port -%} |
143 | +<VirtualHost *:{{ public_port }}> |
144 | + WSGIDaemonProcess {{ service_name }}-public processes={{ public_processes }} threads={{ threads }} user={{ service_name }} group={{ service_name }} \ |
145 | +{% if python_path -%} |
146 | + python-path={{ python_path }} \ |
147 | +{% endif -%} |
148 | + display-name=%{GROUP} |
149 | + WSGIProcessGroup {{ service_name }}-public |
150 | + WSGIScriptAlias / {{ public_script }} |
151 | + WSGIApplicationGroup %{GLOBAL} |
152 | + WSGIPassAuthorization On |
153 | + <IfVersion >= 2.4> |
154 | + ErrorLogFormat "%{cu}t %M" |
155 | + </IfVersion> |
156 | + ErrorLog /var/log/apache2/{{ service_name }}_error.log |
157 | + CustomLog /var/log/apache2/{{ service_name }}_access.log combined |
158 | + |
159 | + <Directory {{ usr_bin }}> |
160 | + <IfVersion >= 2.4> |
161 | + Require all granted |
162 | + </IfVersion> |
163 | + <IfVersion < 2.4> |
164 | + Order allow,deny |
165 | + Allow from all |
166 | + </IfVersion> |
167 | + </Directory> |
168 | +</VirtualHost> |
169 | +{% endif -%} |
170 | |
171 | === modified file 'charmhelpers/contrib/openstack/utils.py' |
172 | --- charmhelpers/contrib/openstack/utils.py 2016-12-14 15:46:41 +0000 |
173 | +++ charmhelpers/contrib/openstack/utils.py 2016-12-14 18:51:33 +0000 |
174 | @@ -1119,6 +1119,35 @@ |
175 | shutil.copyfile(service_source, service_dest) |
176 | |
177 | |
178 | +def git_determine_usr_bin(): |
179 | + """Return the /usr/bin path for Apache2 config. |
180 | + |
181 | + The /usr/bin path will be located in the virtualenv if the charm |
182 | + is configured to deploy from source. |
183 | + """ |
184 | + if git_install_requested(): |
185 | + projects_yaml = config('openstack-origin-git') |
186 | + projects_yaml = git_default_repos(projects_yaml) |
187 | + return os.path.join(git_pip_venv_dir(projects_yaml), 'bin') |
188 | + else: |
189 | + return '/usr/bin' |
190 | + |
191 | + |
192 | +def git_determine_python_path(): |
193 | + """Return the python-path for Apache2 config. |
194 | + |
195 | + Returns 'None' unless the charm is configured to deploy from source, |
196 | + in which case the path of the virtualenv's site-packages is returned. |
197 | + """ |
198 | + if git_install_requested(): |
199 | + projects_yaml = config('openstack-origin-git') |
200 | + projects_yaml = git_default_repos(projects_yaml) |
201 | + return os.path.join(git_pip_venv_dir(projects_yaml), |
202 | + 'lib/python2.7/site-packages') |
203 | + else: |
204 | + return None |
205 | + |
206 | + |
207 | def os_workload_status(configs, required_interfaces, charm_func=None): |
208 | """ |
209 | Decorator to set workload status based on complete contexts |
210 | |
211 | === modified file 'tests/contrib/openstack/test_openstack_utils.py' |
212 | --- tests/contrib/openstack/test_openstack_utils.py 2016-09-16 09:50:01 +0000 |
213 | +++ tests/contrib/openstack/test_openstack_utils.py 2016-12-14 18:51:33 +0000 |
214 | @@ -933,6 +933,40 @@ |
215 | openstack.git_src_dir(openstack_origin_git, 'keystone') |
216 | join.assert_called_with('/mnt/openstack-git', 'keystone') |
217 | |
218 | + @patch.object(openstack, 'config') |
219 | + def test_git_determine_usr_bin(self, config): |
220 | + config.return_value = None |
221 | + result = openstack.git_determine_usr_bin() |
222 | + self.assertEquals(result, '/usr/bin') |
223 | + |
224 | + @patch('os.path.join') |
225 | + @patch.object(openstack, 'git_default_repos') |
226 | + @patch.object(openstack, 'config') |
227 | + def test_git_determine_usr_bin_git(self, config, git_default, join): |
228 | + venv_bin = '/mnt/openstack/.venv/bin' |
229 | + join.return_value = venv_bin |
230 | + config.return_value = openstack_origin_git |
231 | + git_default.return_value = openstack_origin_git |
232 | + result = openstack.git_determine_usr_bin() |
233 | + self.assertEquals(result, venv_bin) |
234 | + |
235 | + @patch.object(openstack, 'config') |
236 | + def test_git_determine_python_path(self, config): |
237 | + config.return_value = None |
238 | + result = openstack.git_determine_python_path() |
239 | + self.assertEquals(result, None) |
240 | + |
241 | + @patch('os.path.join') |
242 | + @patch.object(openstack, 'git_default_repos') |
243 | + @patch.object(openstack, 'config') |
244 | + def test_git_determine_python_path_git(self, config, git_default, join): |
245 | + venv_site = '/mnt/openstack/.venv/lib/python2.7/site-packages' |
246 | + join.return_value = venv_site |
247 | + config.return_value = openstack_origin_git |
248 | + git_default.return_value = openstack_origin_git |
249 | + result = openstack.git_determine_python_path() |
250 | + self.assertEquals(result, venv_site) |
251 | + |
252 | def test_incomplete_relation_data(self): |
253 | configs = MagicMock() |
254 | configs.complete_contexts.return_value = ['pgsql-db', 'amqp'] |
255 | |
256 | === modified file 'tests/contrib/openstack/test_os_contexts.py' |
257 | --- tests/contrib/openstack/test_os_contexts.py 2016-12-09 16:48:53 +0000 |
258 | +++ tests/contrib/openstack/test_os_contexts.py 2016-12-14 18:51:33 +0000 |
259 | @@ -2371,6 +2371,35 @@ |
260 | } |
261 | self.assertEquals(result, expected) |
262 | |
263 | + @patch.object(context, 'psutil') |
264 | + @patch.object(context, 'git_determine_python_path') |
265 | + @patch.object(context, 'git_determine_usr_bin') |
266 | + def test_wsgi_worker_config_context(self, usr_bin, python_path, psutil): |
267 | + self.config.return_value = 2 # worker-multiplier=2 |
268 | + usr_bin_path = '/usr/bin' |
269 | + usr_bin.return_value = usr_bin_path |
270 | + python_path.return_value = None |
271 | + psutil.cpu_count.return_value = 4 |
272 | + service_name = 'service-name' |
273 | + script = '/usr/bin/script' |
274 | + ctxt = context.WSGIWorkerConfigContext(name=service_name, |
275 | + script=script) |
276 | + expect = { |
277 | + "service_name": service_name, |
278 | + "user": service_name, |
279 | + "group": service_name, |
280 | + "script": script, |
281 | + "admin_script": None, |
282 | + "public_script": None, |
283 | + "processes": 8, |
284 | + "admin_processes": 6, |
285 | + "public_processes": 2, |
286 | + "threads": 1, |
287 | + "usr_bin": usr_bin_path, |
288 | + "python_path": None, |
289 | + } |
290 | + self.assertEqual(expect, ctxt()) |
291 | + |
292 | def test_zeromq_context_unrelated(self): |
293 | self.is_relation_made.return_value = False |
294 | self.assertEquals(context.ZeroMQContext()(), {}) |
It looks good but needs unit tests for the new context and methods