Merge lp:~jamesj/charms/trusty/haproxy/xenial-support into lp:charms/trusty/haproxy
- Trusty Tahr (14.04)
- xenial-support
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 104 | ||||
Proposed branch: | lp:~jamesj/charms/trusty/haproxy/xenial-support | ||||
Merge into: | lp:charms/trusty/haproxy | ||||
Diff against target: |
662 lines (+379/-190) 7 files modified
files/nrpe/check_haproxy.sh (+1/-1) hooks/hooks.py (+8/-6) hooks/tests/test_install.py (+3/-1) metadata.yaml (+3/-0) tests/10_deploy_test.py (+0/-182) tests/11_deploy_test_trusty.py (+182/-0) tests/12_deploy_test_xenial.py (+182/-0) |
||||
To merge this branch: | bzr merge lp:~jamesj/charms/trusty/haproxy/xenial-support | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Review Queue (community) | automated testing | Needs Fixing | |
Cory Johns (community) | Approve | ||
Review via email: mp+299196@code.launchpad.net |
Commit message
Install python-apt for Xenial compatibility.
Included some initial fixes for Python3 compatibility.
Description of the change
Install python-apt for Xenial compatibility.
Included some initial fixes for Python3 compatibility.
- 105. By James Jesudason
-
Change to the nagios check to handle versions > 1.5
- 106. By James Jesudason
-
Merge fixes to the tests
James Jesudason (jamesj) wrote : | # |
> James,
>
> Thanks for this contribution! It will be great to see the haproxy charm get
> support for Xenial.
>
> This was missing a deploy test for Xenial, and I hit some errors while testing
> it. I created a MP against this proposed branch with a Xenial test, test
> fixes, and adding the series to the metadata:
> https:/
> support/
> proposed branch and it will automatically update this MR.
>
Done - thanks for doing that
> I tried to (trivially) add support for Precise as well, but ran into an error:
> http://
> delta was between the Precise version of this charm and this one.
>
> With those suggested fixes, I think this would be good. Because of the new
> process for promulgation, though, we will need one of the maintainers (Juan or
> Tom) to publish this charm into the store under their (or an appropriate
> group) namespace before we can move forward with merging and re-promulgation.
At least, if we can get the change merged, it can be used from LP. Getting the update into the store would be great as we seem to be behind in getting Xenial support there for a number of projects.
Cory Johns (johnsca) wrote : | # |
This has my +1 to merge, but due to this now being multi-series and the new conventions regarding hte charm store and storing charms in Launchpad, the repo will need to move from lp:charms/trusty/haproxy to be a top-level project at lp:charm-haproxy and the listed owner of the charm on jujucharms.com will need to change to either one of the maintainers or an appropriate team (e.g., haproxy-team, which would need to be created), as decided by the maintainers.
James Jesudason (jamesj) wrote : | # |
> This has my +1 to merge, but due to this now being multi-series and the new
> conventions regarding hte charm store and storing charms in Launchpad, the
> repo will need to move from lp:charms/trusty/haproxy to be a top-level project
> at lp:charm-haproxy and the listed owner of the charm on jujucharms.com will
> need to change to either one of the maintainers or an appropriate team (e.g.,
> haproxy-team, which would need to be created), as decided by the maintainers.
Is there any chance that we can get the first step of merging this done? The renaming of the branch can happen later, when everyone is ready. I'm having to use my own fork for deployment at the moment.
Charles Butler (lazypower) wrote : | # |
For the purposes of an Audit trail:
I've pushed the branch to lp:~charmers/charms/trusty/haproxy/trunk
When the repository potentially moves location, we will need to ensure we've updated the store entry. This is also incomplete, as we have only merged the requested branch review.
We still need to address and triage the points brought up in:
Review Queue (review-queue) wrote : | # |
The results (PASS) are in and available here: http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
The results (PASS) are in and available here: http://
Review Queue (review-queue) wrote : | # |
The results (PASS) are in and available here: http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
The results (PASS) are in and available here: http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
The results (PASS) are in and available here: http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Preview Diff
1 | === modified file 'files/nrpe/check_haproxy.sh' |
2 | --- files/nrpe/check_haproxy.sh 2015-04-24 16:18:51 +0000 |
3 | +++ files/nrpe/check_haproxy.sh 2016-07-08 08:37:32 +0000 |
4 | @@ -13,7 +13,7 @@ |
5 | |
6 | HAPROXY_VERSION="$(dpkg-query -W --showformat='${Version}\n' haproxy)" |
7 | if dpkg --compare-versions "$HAPROXY_VERSION" ge 1.5; then |
8 | - CLASS_REGEX='(active|backup)(3|4)' |
9 | + CLASS_REGEX='(active_up|backup_up)' |
10 | else |
11 | CLASS_REGEX='(active|backup)(2|3)' |
12 | fi |
13 | |
14 | === modified file 'hooks/hooks.py' |
15 | --- hooks/hooks.py 2016-03-03 20:08:09 +0000 |
16 | +++ hooks/hooks.py 2016-07-08 08:37:32 +0000 |
17 | @@ -785,7 +785,7 @@ |
18 | f.write(content) |
19 | |
20 | if not os.path.exists(default_haproxy_service_config_dir): |
21 | - os.mkdir(default_haproxy_service_config_dir, 0600) |
22 | + os.mkdir(default_haproxy_service_config_dir, 0o600) |
23 | with open(os.path.join(default_haproxy_service_config_dir, |
24 | "%s.service" % service_name), 'w') as config: |
25 | config.write(create_listen_stanza( |
26 | @@ -841,7 +841,7 @@ |
27 | if os.path.exists(path): |
28 | try: |
29 | os.remove(path) |
30 | - except Exception, e: |
31 | + except Exception as e: |
32 | log(str(e)) |
33 | return False |
34 | return True |
35 | @@ -850,7 +850,7 @@ |
36 | default_haproxy_service_config_dir): |
37 | try: |
38 | os.remove(service) |
39 | - except Exception, e: |
40 | + except Exception as e: |
41 | log(str(e)) |
42 | pass |
43 | return True |
44 | @@ -901,7 +901,7 @@ |
45 | def install_hook(): |
46 | # Run both during initial install and during upgrade-charm. |
47 | if not os.path.exists(default_haproxy_service_config_dir): |
48 | - os.mkdir(default_haproxy_service_config_dir, 0600) |
49 | + os.mkdir(default_haproxy_service_config_dir, 0o600) |
50 | |
51 | config_data = config_get() |
52 | source = config_data.get('source') |
53 | @@ -913,7 +913,9 @@ |
54 | apt_update(fatal=True) |
55 | apt_install(['haproxy', 'python-jinja2'], fatal=True) |
56 | # Install pyasn1 library and modules for inspecting SSL certificates |
57 | - apt_install(['python-pyasn1', 'python-pyasn1-modules'], fatal=False) |
58 | + apt_install( |
59 | + ['python-pyasn1', 'python-pyasn1-modules', 'python-apt', |
60 | + 'python-openssl'], fatal=False) |
61 | ensure_package_status(service_affecting_packages, |
62 | config_data['package_status']) |
63 | enable_haproxy() |
64 | @@ -1349,7 +1351,7 @@ |
65 | "statistics-relation-changed"): |
66 | statistics_interface() |
67 | else: |
68 | - print "Unknown hook" |
69 | + print("Unknown hook") |
70 | sys.exit(1) |
71 | |
72 | if __name__ == "__main__": |
73 | |
74 | === modified file 'hooks/tests/test_install.py' |
75 | --- hooks/tests/test_install.py 2015-09-23 15:12:08 +0000 |
76 | +++ hooks/tests/test_install.py 2016-07-08 08:37:32 +0000 |
77 | @@ -48,7 +48,9 @@ |
78 | self.assertEqual((['haproxy', 'python-jinja2'],), calls[0][0]) |
79 | self.assertEqual({'fatal': True}, calls[0][1]) |
80 | self.assertEqual( |
81 | - (['python-pyasn1', 'python-pyasn1-modules'],), calls[1][0]) |
82 | + (['python-pyasn1', 'python-pyasn1-modules', 'python-apt', |
83 | + 'python-openssl'],), |
84 | + calls[1][0]) |
85 | self.assertEqual({'fatal': False}, calls[1][1]) |
86 | |
87 | def test_add_source(self): |
88 | |
89 | === modified file 'metadata.yaml' |
90 | --- metadata.yaml 2016-03-03 20:08:09 +0000 |
91 | +++ metadata.yaml 2016-07-08 08:37:32 +0000 |
92 | @@ -8,6 +8,9 @@ |
93 | has request blocking capabilities and provides interface to display server |
94 | status. |
95 | tags: ["cache-proxy"] |
96 | +series: |
97 | + - xenial |
98 | + - trusty |
99 | requires: |
100 | reverseproxy: |
101 | interface: http |
102 | |
103 | === removed file 'tests/10_deploy_test.py' |
104 | --- tests/10_deploy_test.py 2015-11-19 18:13:43 +0000 |
105 | +++ tests/10_deploy_test.py 1970-01-01 00:00:00 +0000 |
106 | @@ -1,182 +0,0 @@ |
107 | -#!/usr/bin/python3 |
108 | - |
109 | -# This Amulet test deploys haproxy and related charms. |
110 | - |
111 | -import os |
112 | -import amulet |
113 | -import requests |
114 | -import base64 |
115 | -import yaml |
116 | -import time |
117 | - |
118 | -d = amulet.Deployment(series='trusty') |
119 | -# Add the haproxy charm to the deployment. |
120 | -d.add('haproxy') |
121 | -d.add('apache2', units=2) |
122 | - |
123 | -# Get the directory this way to load the file when CWD is different. |
124 | -path = os.path.abspath(os.path.dirname(__file__)) |
125 | -template_path = os.path.join(path, 'default_apache.tmpl') |
126 | -# Read in the Apache2 default template file. |
127 | -with open(template_path) as f: |
128 | - template = f.read() |
129 | - encodedTemplate = base64.b64encode(template.encode('utf-8')) |
130 | -# Create a dictionary with configuration values for apache2. |
131 | -configuration = {'vhost_http_template': encodedTemplate.decode('ascii')} |
132 | -# Apache2 needs a base64 encoded template to configure the web site. |
133 | -d.configure('apache2', configuration) |
134 | - |
135 | -# Relate the haproxy to apache2. |
136 | -d.relate('haproxy:reverseproxy', 'apache2:website') |
137 | -# Make the haproxy visible to the outside world. |
138 | -d.expose('haproxy') |
139 | - |
140 | -# The number of seconds to wait for the environment to setup. |
141 | -seconds = 900 |
142 | -try: |
143 | - # Execute the deployer with the current mapping. |
144 | - d.setup(timeout=seconds) |
145 | - # Wait for the relation to finish the transations. |
146 | - d.sentry.wait(seconds) |
147 | -except amulet.helpers.TimeoutError: |
148 | - message = 'The environment did not setup in %d seconds.' % seconds |
149 | - # The SKIP status enables skip or fail the test based on configuration. |
150 | - amulet.raise_status(amulet.SKIP, msg=message) |
151 | -except: |
152 | - raise |
153 | - |
154 | -# Test that haproxy is acting as the proxy for apache2. |
155 | - |
156 | -# Get the haproxy unit. |
157 | -haproxy_unit = d.sentry['haproxy'][0] |
158 | -haproxy_address = haproxy_unit.info['public-address'] |
159 | -page = requests.get('http://%s/index.html' % haproxy_address) |
160 | -# Raise an error if the page does not load through haproxy. |
161 | -page.raise_for_status() |
162 | -print('Successfully got the Apache2 web page through haproxy IP address.') |
163 | - |
164 | -# Test that sticky session cookie is present |
165 | -if page.cookies.get('SRVNAME') != 'S0': |
166 | - msg = 'Missing or invalid sticky session cookie value: %s' % page.cookies.get('SRVNAME') |
167 | - amulet.raise_status(amulet.FAIL, msg=msg) |
168 | - |
169 | -# Test that the apache2 relation data is saved on the haproxy server. |
170 | - |
171 | -# Get the sentry for apache and get the private IP address. |
172 | -apache_unit = d.sentry['apache2'][0] |
173 | -# Get the relation. |
174 | -relation = apache_unit.relation('website', 'haproxy:reverseproxy') |
175 | -# Get the private address from the relation. |
176 | -apache_private = relation['private-address'] |
177 | - |
178 | -print('Private address of the apache2 relation ', apache_private) |
179 | - |
180 | -# Grep the configuration file for the private address |
181 | -output, code = haproxy_unit.run('grep %s /etc/haproxy/haproxy.cfg' % |
182 | - apache_private) |
183 | -if code == 0: |
184 | - print('Found the relation IP address in the haproxy configuration file!') |
185 | - print(output) |
186 | -else: |
187 | - print(output) |
188 | - message = 'Unable to find the Apache IP address %s in the haproxy ' \ |
189 | - 'configuration file.' % apache_private |
190 | - amulet.raise_status(amulet.FAIL, msg=message) |
191 | - |
192 | -# Test SSL termination |
193 | -d.configure('haproxy', { |
194 | - 'source': 'backports', |
195 | - 'ssl_cert': 'SELFSIGNED', |
196 | - 'services': yaml.safe_dump([ |
197 | - {'service_name': 'apache', |
198 | - 'service_host': '0.0.0.0', |
199 | - 'service_port': 80, |
200 | - 'service_options': [ |
201 | - 'mode http', 'balance leastconn', 'option httpchk GET / HTTP/1.0' |
202 | - ], |
203 | - 'servers': [ |
204 | - ['apache', apache_private, 80, 'maxconn 50']]}, |
205 | - {'service_name': 'apache-ssl', |
206 | - 'service_port': 443, |
207 | - 'service_host': '0.0.0.0', |
208 | - 'service_options': [ |
209 | - 'mode http', 'balance leastconn', 'option httpchk GET / HTTP/1.0' |
210 | - ], |
211 | - 'crts': ['DEFAULT'], |
212 | - 'servers': [['apache', apache_private, 80, 'maxconn 50']]}]) |
213 | -}) |
214 | -time.sleep(10) |
215 | -d.sentry.wait(seconds) |
216 | - |
217 | -# We need a retry loop here, since there's no way to tell when the new |
218 | -# configuration is in place. |
219 | -url = 'http://%s/index.html' % haproxy_address |
220 | -secure_url = 'https://%s/index.html' % haproxy_address |
221 | -retries = 10 |
222 | -for i in range(retries): |
223 | - try: |
224 | - page = requests.get(url) |
225 | - page.raise_for_status() |
226 | - page = requests.get(secure_url, verify=False) |
227 | - page.raise_for_status() |
228 | - success = True |
229 | - except requests.exceptions.ConnectionError: |
230 | - if i == retries - 1: |
231 | - # This was the last one, let's fail |
232 | - raise |
233 | - time.sleep(6) |
234 | - else: |
235 | - break |
236 | - |
237 | -print('Successfully got the Apache2 web page through haproxy SSL termination.') |
238 | - |
239 | -apache_unit2 = d.sentry['apache2'][1] |
240 | -apache_private2 = apache_unit2.run("unit-get private-address")[0] |
241 | - |
242 | -# Create a file on the second apache unit's www directory. |
243 | -apache_unit2.run("echo foo > /var/www/html/foo") |
244 | - |
245 | -d.configure('haproxy', { |
246 | - 'services': yaml.safe_dump([ |
247 | - {'service_name': 'apache', |
248 | - 'service_host': '0.0.0.0', |
249 | - 'service_port': 80, |
250 | - 'service_options': [ |
251 | - 'mode http', 'balance leastconn', 'option httpchk GET / HTTP/1.0', |
252 | - 'acl foo path_beg -i /foo', 'use_backend foo if foo', |
253 | - ], |
254 | - 'servers': [ |
255 | - ['apache', apache_private, 80, 'maxconn 50']], |
256 | - 'backends': [ |
257 | - {'backend_name': 'foo', |
258 | - 'servers': [ |
259 | - ['apache2', apache_private2, 80, 'maxconn 50']]} |
260 | - ]}]) |
261 | -}) |
262 | -time.sleep(10) |
263 | -d.sentry.wait(seconds) |
264 | - |
265 | -# Let's exercise our URL-based routing by trying to fetch a URL that will |
266 | -# only work for the second apache unit (which is configured as server |
267 | -# of the extra backend). |
268 | -url = 'http://%s/foo' % haproxy_address |
269 | - |
270 | -# We need a retry loop here, since there's no way to tell when the new |
271 | -# configuration is in place. |
272 | -retries = 10 |
273 | -for i in range(retries): |
274 | - try: |
275 | - page = requests.get(url) |
276 | - page.raise_for_status() |
277 | - except: |
278 | - if i == retries - 1: |
279 | - # This was the last one, let's fail |
280 | - raise |
281 | - time.sleep(6) |
282 | - else: |
283 | - break |
284 | - |
285 | -print('Successfully got the /foo URL from the second Apache unit.') |
286 | - |
287 | -# Send a message that the tests are complete. |
288 | -print('The haproxy tests are complete.') |
289 | |
290 | === added file 'tests/11_deploy_test_trusty.py' |
291 | --- tests/11_deploy_test_trusty.py 1970-01-01 00:00:00 +0000 |
292 | +++ tests/11_deploy_test_trusty.py 2016-07-08 08:37:32 +0000 |
293 | @@ -0,0 +1,182 @@ |
294 | +#!/usr/bin/python3 |
295 | + |
296 | +# This Amulet test deploys haproxy and related charms. |
297 | + |
298 | +import os |
299 | +import amulet |
300 | +import requests |
301 | +import base64 |
302 | +import yaml |
303 | +import time |
304 | + |
305 | +d = amulet.Deployment(series='trusty') |
306 | +# Add the haproxy charm to the deployment. |
307 | +d.add('haproxy') |
308 | +d.add('apache2', units=2) |
309 | + |
310 | +# Get the directory this way to load the file when CWD is different. |
311 | +path = os.path.abspath(os.path.dirname(__file__)) |
312 | +template_path = os.path.join(path, 'default_apache.tmpl') |
313 | +# Read in the Apache2 default template file. |
314 | +with open(template_path) as f: |
315 | + template = f.read() |
316 | + encodedTemplate = base64.b64encode(template.encode('utf-8')) |
317 | +# Create a dictionary with configuration values for apache2. |
318 | +configuration = {'vhost_http_template': encodedTemplate.decode('ascii')} |
319 | +# Apache2 needs a base64 encoded template to configure the web site. |
320 | +d.configure('apache2', configuration) |
321 | + |
322 | +# Relate the haproxy to apache2. |
323 | +d.relate('haproxy:reverseproxy', 'apache2:website') |
324 | +# Make the haproxy visible to the outside world. |
325 | +d.expose('haproxy') |
326 | + |
327 | +# The number of seconds to wait for the environment to setup. |
328 | +seconds = 900 |
329 | +try: |
330 | + # Execute the deployer with the current mapping. |
331 | + d.setup(timeout=seconds) |
332 | + # Wait for the relation to finish the transations. |
333 | + d.sentry.wait(seconds) |
334 | +except amulet.helpers.TimeoutError: |
335 | + message = 'The environment did not setup in %d seconds.' % seconds |
336 | + # The SKIP status enables skip or fail the test based on configuration. |
337 | + amulet.raise_status(amulet.SKIP, msg=message) |
338 | +except: |
339 | + raise |
340 | + |
341 | +# Test that haproxy is acting as the proxy for apache2. |
342 | + |
343 | +# Get the haproxy unit. |
344 | +haproxy_unit = d.sentry['haproxy'][0] |
345 | +haproxy_address = haproxy_unit.info['public-address'] |
346 | +page = requests.get('http://%s/index.html' % haproxy_address) |
347 | +# Raise an error if the page does not load through haproxy. |
348 | +page.raise_for_status() |
349 | +print('Successfully got the Apache2 web page through haproxy IP address.') |
350 | + |
351 | +# Test that sticky session cookie is present |
352 | +if page.cookies.get('SRVNAME') != 'S0': |
353 | + msg = 'Missing or invalid sticky session cookie value: %s' % page.cookies.get('SRVNAME') |
354 | + amulet.raise_status(amulet.FAIL, msg=msg) |
355 | + |
356 | +# Test that the apache2 relation data is saved on the haproxy server. |
357 | + |
358 | +# Get the sentry for apache and get the private IP address. |
359 | +apache_unit = d.sentry['apache2'][0] |
360 | +# Get the relation. |
361 | +relation = apache_unit.relation('website', 'haproxy:reverseproxy') |
362 | +# Get the private address from the relation. |
363 | +apache_private = relation['private-address'] |
364 | + |
365 | +print('Private address of the apache2 relation ', apache_private) |
366 | + |
367 | +# Grep the configuration file for the private address |
368 | +output, code = haproxy_unit.run('grep %s /etc/haproxy/haproxy.cfg' % |
369 | + apache_private) |
370 | +if code == 0: |
371 | + print('Found the relation IP address in the haproxy configuration file!') |
372 | + print(output) |
373 | +else: |
374 | + print(output) |
375 | + message = 'Unable to find the Apache IP address %s in the haproxy ' \ |
376 | + 'configuration file.' % apache_private |
377 | + amulet.raise_status(amulet.FAIL, msg=message) |
378 | + |
379 | +# Test SSL termination |
380 | +d.configure('haproxy', { |
381 | + 'source': 'backports', |
382 | + 'ssl_cert': 'SELFSIGNED', |
383 | + 'services': yaml.safe_dump([ |
384 | + {'service_name': 'apache', |
385 | + 'service_host': '0.0.0.0', |
386 | + 'service_port': 80, |
387 | + 'service_options': [ |
388 | + 'mode http', 'balance leastconn', 'option httpchk GET / HTTP/1.0' |
389 | + ], |
390 | + 'servers': [ |
391 | + ['apache', apache_private, 80, 'maxconn 50']]}, |
392 | + {'service_name': 'apache-ssl', |
393 | + 'service_port': 443, |
394 | + 'service_host': '0.0.0.0', |
395 | + 'service_options': [ |
396 | + 'mode http', 'balance leastconn', 'option httpchk GET / HTTP/1.0' |
397 | + ], |
398 | + 'crts': ['DEFAULT'], |
399 | + 'servers': [['apache', apache_private, 80, 'maxconn 50']]}]) |
400 | +}) |
401 | +time.sleep(10) |
402 | +d.sentry.wait(seconds) |
403 | + |
404 | +# We need a retry loop here, since there's no way to tell when the new |
405 | +# configuration is in place. |
406 | +url = 'http://%s/index.html' % haproxy_address |
407 | +secure_url = 'https://%s/index.html' % haproxy_address |
408 | +retries = 10 |
409 | +for i in range(retries): |
410 | + try: |
411 | + page = requests.get(url) |
412 | + page.raise_for_status() |
413 | + page = requests.get(secure_url, verify=False) |
414 | + page.raise_for_status() |
415 | + success = True |
416 | + except requests.exceptions.ConnectionError: |
417 | + if i == retries - 1: |
418 | + # This was the last one, let's fail |
419 | + raise |
420 | + time.sleep(6) |
421 | + else: |
422 | + break |
423 | + |
424 | +print('Successfully got the Apache2 web page through haproxy SSL termination.') |
425 | + |
426 | +apache_unit2 = d.sentry['apache2'][1] |
427 | +apache_private2 = apache_unit2.run("unit-get private-address")[0] |
428 | + |
429 | +# Create a file on the second apache unit's www directory. |
430 | +apache_unit2.run("echo foo > /var/www/html/foo") |
431 | + |
432 | +d.configure('haproxy', { |
433 | + 'services': yaml.safe_dump([ |
434 | + {'service_name': 'apache', |
435 | + 'service_host': '0.0.0.0', |
436 | + 'service_port': 80, |
437 | + 'service_options': [ |
438 | + 'mode http', 'balance leastconn', 'option httpchk GET / HTTP/1.0', |
439 | + 'acl foo path_beg -i /foo', 'use_backend foo if foo', |
440 | + ], |
441 | + 'servers': [ |
442 | + ['apache', apache_private, 80, 'maxconn 50']], |
443 | + 'backends': [ |
444 | + {'backend_name': 'foo', |
445 | + 'servers': [ |
446 | + ['apache2', apache_private2, 80, 'maxconn 50']]} |
447 | + ]}]) |
448 | +}) |
449 | +time.sleep(10) |
450 | +d.sentry.wait(seconds) |
451 | + |
452 | +# Let's exercise our URL-based routing by trying to fetch a URL that will |
453 | +# only work for the second apache unit (which is configured as server |
454 | +# of the extra backend). |
455 | +url = 'http://%s/foo' % haproxy_address |
456 | + |
457 | +# We need a retry loop here, since there's no way to tell when the new |
458 | +# configuration is in place. |
459 | +retries = 10 |
460 | +for i in range(retries): |
461 | + try: |
462 | + page = requests.get(url) |
463 | + page.raise_for_status() |
464 | + except: |
465 | + if i == retries - 1: |
466 | + # This was the last one, let's fail |
467 | + raise |
468 | + time.sleep(6) |
469 | + else: |
470 | + break |
471 | + |
472 | +print('Successfully got the /foo URL from the second Apache unit.') |
473 | + |
474 | +# Send a message that the tests are complete. |
475 | +print('The haproxy tests are complete.') |
476 | |
477 | === added file 'tests/12_deploy_test_xenial.py' |
478 | --- tests/12_deploy_test_xenial.py 1970-01-01 00:00:00 +0000 |
479 | +++ tests/12_deploy_test_xenial.py 2016-07-08 08:37:32 +0000 |
480 | @@ -0,0 +1,182 @@ |
481 | +#!/usr/bin/python3 |
482 | + |
483 | +# This Amulet test deploys haproxy and related charms. |
484 | + |
485 | +import os |
486 | +import amulet |
487 | +import requests |
488 | +import base64 |
489 | +import yaml |
490 | +import time |
491 | + |
492 | +d = amulet.Deployment(series='xenial') |
493 | +# Add the haproxy charm to the deployment. |
494 | +d.add('haproxy') |
495 | +d.add('apache2', 'cs:trusty/apache2', units=2) |
496 | + |
497 | +# Get the directory this way to load the file when CWD is different. |
498 | +path = os.path.abspath(os.path.dirname(__file__)) |
499 | +template_path = os.path.join(path, 'default_apache.tmpl') |
500 | +# Read in the Apache2 default template file. |
501 | +with open(template_path) as f: |
502 | + template = f.read() |
503 | + encodedTemplate = base64.b64encode(template.encode('utf-8')) |
504 | +# Create a dictionary with configuration values for apache2. |
505 | +configuration = {'vhost_http_template': encodedTemplate.decode('ascii')} |
506 | +# Apache2 needs a base64 encoded template to configure the web site. |
507 | +d.configure('apache2', configuration) |
508 | + |
509 | +# Relate the haproxy to apache2. |
510 | +d.relate('haproxy:reverseproxy', 'apache2:website') |
511 | +# Make the haproxy visible to the outside world. |
512 | +d.expose('haproxy') |
513 | + |
514 | +# The number of seconds to wait for the environment to setup. |
515 | +seconds = 900 |
516 | +try: |
517 | + # Execute the deployer with the current mapping. |
518 | + d.setup(timeout=seconds) |
519 | + # Wait for the relation to finish the transations. |
520 | + d.sentry.wait(seconds) |
521 | +except amulet.helpers.TimeoutError: |
522 | + message = 'The environment did not setup in %d seconds.' % seconds |
523 | + # The SKIP status enables skip or fail the test based on configuration. |
524 | + amulet.raise_status(amulet.SKIP, msg=message) |
525 | +except: |
526 | + raise |
527 | + |
528 | +# Test that haproxy is acting as the proxy for apache2. |
529 | + |
530 | +# Get the haproxy unit. |
531 | +haproxy_unit = d.sentry['haproxy'][0] |
532 | +haproxy_address = haproxy_unit.info['public-address'] |
533 | +page = requests.get('http://%s/index.html' % haproxy_address) |
534 | +# Raise an error if the page does not load through haproxy. |
535 | +page.raise_for_status() |
536 | +print('Successfully got the Apache2 web page through haproxy IP address.') |
537 | + |
538 | +# Test that sticky session cookie is present |
539 | +if page.cookies.get('SRVNAME') != 'S0': |
540 | + msg = 'Missing or invalid sticky session cookie value: %s' % page.cookies.get('SRVNAME') |
541 | + amulet.raise_status(amulet.FAIL, msg=msg) |
542 | + |
543 | +# Test that the apache2 relation data is saved on the haproxy server. |
544 | + |
545 | +# Get the sentry for apache and get the private IP address. |
546 | +apache_unit = d.sentry['apache2'][0] |
547 | +# Get the relation. |
548 | +relation = apache_unit.relation('website', 'haproxy:reverseproxy') |
549 | +# Get the private address from the relation. |
550 | +apache_private = relation['private-address'] |
551 | + |
552 | +print('Private address of the apache2 relation ', apache_private) |
553 | + |
554 | +# Grep the configuration file for the private address |
555 | +output, code = haproxy_unit.run('grep %s /etc/haproxy/haproxy.cfg' % |
556 | + apache_private) |
557 | +if code == 0: |
558 | + print('Found the relation IP address in the haproxy configuration file!') |
559 | + print(output) |
560 | +else: |
561 | + print(output) |
562 | + message = 'Unable to find the Apache IP address %s in the haproxy ' \ |
563 | + 'configuration file.' % apache_private |
564 | + amulet.raise_status(amulet.FAIL, msg=message) |
565 | + |
566 | +# Test SSL termination |
567 | +d.configure('haproxy', { |
568 | + 'source': 'backports', |
569 | + 'ssl_cert': 'SELFSIGNED', |
570 | + 'services': yaml.safe_dump([ |
571 | + {'service_name': 'apache', |
572 | + 'service_host': '0.0.0.0', |
573 | + 'service_port': 80, |
574 | + 'service_options': [ |
575 | + 'mode http', 'balance leastconn', 'option httpchk GET / HTTP/1.0' |
576 | + ], |
577 | + 'servers': [ |
578 | + ['apache', apache_private, 80, 'maxconn 50']]}, |
579 | + {'service_name': 'apache-ssl', |
580 | + 'service_port': 443, |
581 | + 'service_host': '0.0.0.0', |
582 | + 'service_options': [ |
583 | + 'mode http', 'balance leastconn', 'option httpchk GET / HTTP/1.0' |
584 | + ], |
585 | + 'crts': ['DEFAULT'], |
586 | + 'servers': [['apache', apache_private, 80, 'maxconn 50']]}]) |
587 | +}) |
588 | +time.sleep(10) |
589 | +d.sentry.wait(seconds) |
590 | + |
591 | +# We need a retry loop here, since there's no way to tell when the new |
592 | +# configuration is in place. |
593 | +url = 'http://%s/index.html' % haproxy_address |
594 | +secure_url = 'https://%s/index.html' % haproxy_address |
595 | +retries = 10 |
596 | +for i in range(retries): |
597 | + try: |
598 | + page = requests.get(url) |
599 | + page.raise_for_status() |
600 | + page = requests.get(secure_url, verify=False) |
601 | + page.raise_for_status() |
602 | + success = True |
603 | + except requests.exceptions.ConnectionError: |
604 | + if i == retries - 1: |
605 | + # This was the last one, let's fail |
606 | + raise |
607 | + time.sleep(6) |
608 | + else: |
609 | + break |
610 | + |
611 | +print('Successfully got the Apache2 web page through haproxy SSL termination.') |
612 | + |
613 | +apache_unit2 = d.sentry['apache2'][1] |
614 | +apache_private2 = apache_unit2.run("unit-get private-address")[0] |
615 | + |
616 | +# Create a file on the second apache unit's www directory. |
617 | +apache_unit2.run("echo foo > /var/www/html/foo") |
618 | + |
619 | +d.configure('haproxy', { |
620 | + 'services': yaml.safe_dump([ |
621 | + {'service_name': 'apache', |
622 | + 'service_host': '0.0.0.0', |
623 | + 'service_port': 80, |
624 | + 'service_options': [ |
625 | + 'mode http', 'balance leastconn', 'option httpchk GET / HTTP/1.0', |
626 | + 'acl foo path_beg -i /foo', 'use_backend foo if foo', |
627 | + ], |
628 | + 'servers': [ |
629 | + ['apache', apache_private, 80, 'maxconn 50']], |
630 | + 'backends': [ |
631 | + {'backend_name': 'foo', |
632 | + 'servers': [ |
633 | + ['apache2', apache_private2, 80, 'maxconn 50']]} |
634 | + ]}]) |
635 | +}) |
636 | +time.sleep(10) |
637 | +d.sentry.wait(seconds) |
638 | + |
639 | +# Let's exercise our URL-based routing by trying to fetch a URL that will |
640 | +# only work for the second apache unit (which is configured as server |
641 | +# of the extra backend). |
642 | +url = 'http://%s/foo' % haproxy_address |
643 | + |
644 | +# We need a retry loop here, since there's no way to tell when the new |
645 | +# configuration is in place. |
646 | +retries = 10 |
647 | +for i in range(retries): |
648 | + try: |
649 | + page = requests.get(url) |
650 | + page.raise_for_status() |
651 | + except: |
652 | + if i == retries - 1: |
653 | + # This was the last one, let's fail |
654 | + raise |
655 | + time.sleep(6) |
656 | + else: |
657 | + break |
658 | + |
659 | +print('Successfully got the /foo URL from the second Apache unit.') |
660 | + |
661 | +# Send a message that the tests are complete. |
662 | +print('The haproxy tests are complete.') |
James,
Thanks for this contribution! It will be great to see the haproxy charm get support for Xenial.
This was missing a deploy test for Xenial, and I hit some errors while testing it. I created a MP against this proposed branch with a Xenial test, test fixes, and adding the series to the metadata: https:/ /code.launchpad .net/~johnsca/ charms/ trusty/ haproxy/ xenial- support/ +merge/ 299470 If that looks good to you, please merge it into your proposed branch and it will automatically update this MR.
I tried to (trivially) add support for Precise as well, but ran into an error: http:// pastebin. ubuntu. com/18729639/ I didn't look any deeper into what the delta was between the Precise version of this charm and this one.
With those suggested fixes, I think this would be good. Because of the new process for promulgation, though, we will need one of the maintainers (Juan or Tom) to publish this charm into the store under their (or an appropriate group) namespace before we can move forward with merging and re-promulgation.