Merge lp:~jamesj/charms/trusty/haproxy/xenial-support into lp:charms/trusty/haproxy

Proposed by James Jesudason
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
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.

To post a comment you must log in.
105. By James Jesudason

Change to the nagios check to handle versions > 1.5

Revision history for this message
Cory Johns (johnsca) 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://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.

review: Needs Fixing
106. By James Jesudason

Merge fixes to the tests

Revision history for this message
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://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.
>
Done - thanks for doing that

> 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.

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.

Revision history for this message
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.

review: Approve
Revision history for this message
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.

Revision history for this message
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:

https://github.com/juju/docs/issues/1264

Revision history for this message
Review Queue (review-queue) wrote :

The results (PASS) are in and available here: http://juju-ci.vapour.ws/job/charm-bundle-test-aws/5014/

review: Approve (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws/job/charm-bundle-test-lxc/4811/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

The results (PASS) are in and available here: http://juju-ci.vapour.ws/job/charm-bundle-test-aws/5022/

review: Approve (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

The results (PASS) are in and available here: http://juju-ci.vapour.ws/job/charm-bundle-test-aws/5030/

review: Approve (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws/job/charm-bundle-test-lxc/4818/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws/job/charm-bundle-test-lxc/4823/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

The results (PASS) are in and available here: http://juju-ci.vapour.ws/job/charm-bundle-test-aws/5037/

review: Approve (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws/job/charm-bundle-test-lxc/4827/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

The results (PASS) are in and available here: http://juju-ci.vapour.ws/job/charm-bundle-test-aws/5041/

review: Approve (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws/job/charm-bundle-test-lxc/4831/

review: Needs Fixing (automated testing)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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.')

Subscribers

People subscribed via source and target branches

to all changes: