Merge lp:~free.ekanayaka/charms/trusty/haproxy/backends-support into lp:charms/trusty/haproxy
- Trusty Tahr (14.04)
- backends-support
- Merge into trunk
Proposed by
Free Ekanayaka
Status: | Merged |
---|---|
Merged at revision: | 89 |
Proposed branch: | lp:~free.ekanayaka/charms/trusty/haproxy/backends-support |
Merge into: | lp:charms/trusty/haproxy |
Diff against target: |
443 lines (+227/-19) 6 files modified
README.md (+24/-3) hooks/hooks.py (+52/-11) hooks/tests/test_helpers.py (+35/-3) hooks/tests/test_peer_hooks.py (+1/-1) hooks/tests/test_reverseproxy_hooks.py (+66/-0) tests/10_deploy_test.py (+49/-1) |
To merge this branch: | bzr merge lp:~free.ekanayaka/charms/trusty/haproxy/backends-support |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Chris Glass (community) | Approve | ||
Review Queue (community) | automated testing | Needs Fixing | |
Review via email: mp+251908@code.launchpad.net |
Commit message
Description of the change
This branch adds support for configuring extra backends beside the default one. This allows for example to implement URL-based routing and forward traffic to different backends based on URL patterns.
To post a comment you must log in.
- 92. By Free Ekanayaka
-
Add docstring
- 93. By Free Ekanayaka
-
More comments
Revision history for this message
Review Queue (review-queue) wrote : | # |
review:
Needs Fixing
(automated testing)
Revision history for this message
Review Queue (review-queue) wrote : | # |
This items has failed automated testing! Results available here http://
review:
Needs Fixing
(automated testing)
- 94. By Free Ekanayaka
-
Dummy commit to trigger test bots
- 95. By Free Ekanayaka
-
Woraround sentry bug in tests
Revision history for this message
Chris Glass (tribaal) wrote : | # |
Looks good, despite the CI not +1'ing (connection errors or CI errors - not branch problems).
+1, will merge now.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'README.md' | |||
2 | --- README.md 2015-02-09 16:45:29 +0000 | |||
3 | +++ README.md 2015-03-18 20:28:50 +0000 | |||
4 | @@ -65,6 +65,8 @@ | |||
5 | 65 | 65 | ||
6 | 66 | relation-set "services= | 66 | relation-set "services= |
7 | 67 | - { service_name: my_web_app, | 67 | - { service_name: my_web_app, |
8 | 68 | service_host: 0.0.0.0, | ||
9 | 69 | service_port: 80, | ||
10 | 68 | service_options: [mode http, balance leastconn], | 70 | service_options: [mode http, balance leastconn], |
11 | 69 | servers: [[my_web_app_1, $host, $port, option httpchk GET / HTTP/1.0], | 71 | servers: [[my_web_app_1, $host, $port, option httpchk GET / HTTP/1.0], |
12 | 70 | [... optionally more servers here ...]]} | 72 | [... optionally more servers here ...]]} |
13 | @@ -72,9 +74,28 @@ | |||
14 | 72 | " | 74 | " |
15 | 73 | 75 | ||
16 | 74 | Once set, haproxy will union multiple `servers` stanzas from any units | 76 | Once set, haproxy will union multiple `servers` stanzas from any units |
20 | 75 | joining with the same `service_name` under one listen stanza. | 77 | joining with the same `service_name` under one backend stanza, which will be |
21 | 76 | `service-options` and `server_options` will be overwritten, so ensure they | 78 | the default backend for the service (requests against the given service_port on |
22 | 77 | are set uniformly on all services with the same name. | 79 | the haproxy unit will be forwarded to that backend). Note that `service-options` |
23 | 80 | and `server_options` will be overwritten, so ensure they are set uniformly on | ||
24 | 81 | all services with the same name. | ||
25 | 82 | |||
26 | 83 | If you need additional backends, possibly handling ACL-filtered requests, you | ||
27 | 84 | can add a 'backends' entry to a service stanza. For example in order to redirect | ||
28 | 85 | to a different backend all requests to URLs starting with '/foo', you could have: | ||
29 | 86 | |||
30 | 87 | relation-set "services= | ||
31 | 88 | - { service_name: my_web_app, | ||
32 | 89 | service_host: 0.0.0.0, | ||
33 | 90 | service_port: 80, | ||
34 | 91 | service_options: [mode http, acl foo path_beg -i /foo, use_backend foo if foo], | ||
35 | 92 | servers: [[my_web_app_1, $host, $port, option httpchk GET / HTTP/1.0], | ||
36 | 93 | [... optionally more servers here ...]] | ||
37 | 94 | backends: | ||
38 | 95 | - { backend_name: foo, | ||
39 | 96 | servers: [[my_web_app2, $host, $port2, option httpchk GET / HTTP/1.0], | ||
40 | 97 | [... optionally more servers here ...]]}} | ||
41 | 98 | |||
42 | 78 | 99 | ||
43 | 79 | ## Website Relation | 100 | ## Website Relation |
44 | 80 | 101 | ||
45 | 81 | 102 | ||
46 | === modified file 'hooks/hooks.py' | |||
47 | --- hooks/hooks.py 2015-02-19 17:05:11 +0000 | |||
48 | +++ hooks/hooks.py 2015-03-18 20:28:50 +0000 | |||
49 | @@ -62,6 +62,7 @@ | |||
50 | 62 | ] | 62 | ] |
51 | 63 | 63 | ||
52 | 64 | frontend_only_options = [ | 64 | frontend_only_options = [ |
53 | 65 | "acl", | ||
54 | 65 | "backlog", | 66 | "backlog", |
55 | 66 | "bind", | 67 | "bind", |
56 | 67 | "capture cookie", | 68 | "capture cookie", |
57 | @@ -69,6 +70,7 @@ | |||
58 | 69 | "capture response header", | 70 | "capture response header", |
59 | 70 | "clitimeout", | 71 | "clitimeout", |
60 | 71 | "default_backend", | 72 | "default_backend", |
61 | 73 | "http-request", | ||
62 | 72 | "maxconn", | 74 | "maxconn", |
63 | 73 | "monitor fail", | 75 | "monitor fail", |
64 | 74 | "monitor-net", | 76 | "monitor-net", |
65 | @@ -84,6 +86,7 @@ | |||
66 | 84 | "option socket-stats", | 86 | "option socket-stats", |
67 | 85 | "option tcp-smart-accept", | 87 | "option tcp-smart-accept", |
68 | 86 | "rate-limit sessions", | 88 | "rate-limit sessions", |
69 | 89 | "redirect", | ||
70 | 87 | "tcp-request content accept", | 90 | "tcp-request content accept", |
71 | 88 | "tcp-request content reject", | 91 | "tcp-request content reject", |
72 | 89 | "tcp-request inspect-delay", | 92 | "tcp-request inspect-delay", |
73 | @@ -303,11 +306,14 @@ | |||
74 | 303 | # service_ip: IP address to listen for connections | 306 | # service_ip: IP address to listen for connections |
75 | 304 | # service_port: Port to listen for connections | 307 | # service_port: Port to listen for connections |
76 | 305 | # service_options: Comma separated list of options | 308 | # service_options: Comma separated list of options |
78 | 306 | # server_entries: List of tuples | 309 | # server_entries: List of tuples |
79 | 307 | # server_name | 310 | # server_name |
80 | 308 | # server_ip | 311 | # server_ip |
81 | 309 | # server_port | 312 | # server_port |
82 | 310 | # server_options | 313 | # server_options |
83 | 314 | # backends: List of dicts | ||
84 | 315 | # backend_name: backend name, | ||
85 | 316 | # servers: list of tuples as in server_entries | ||
86 | 311 | # errorfiles: List of dicts | 317 | # errorfiles: List of dicts |
87 | 312 | # http_status: status to handle | 318 | # http_status: status to handle |
88 | 313 | # content: base 64 content for HAProxy to | 319 | # content: base 64 content for HAProxy to |
89 | @@ -318,7 +324,7 @@ | |||
90 | 318 | def create_listen_stanza(service_name=None, service_ip=None, | 324 | def create_listen_stanza(service_name=None, service_ip=None, |
91 | 319 | service_port=None, service_options=None, | 325 | service_port=None, service_options=None, |
92 | 320 | server_entries=None, service_errorfiles=None, | 326 | server_entries=None, service_errorfiles=None, |
94 | 321 | service_crts=None): | 327 | service_crts=None, service_backends=None): |
95 | 322 | if service_name is None or service_ip is None or service_port is None: | 328 | if service_name is None or service_ip is None or service_port is None: |
96 | 323 | return None | 329 | return None |
97 | 324 | fe_options = [] | 330 | fe_options = [] |
98 | @@ -359,17 +365,43 @@ | |||
99 | 359 | service_config.append(" default_backend %s" % (service_name,)) | 365 | service_config.append(" default_backend %s" % (service_name,)) |
100 | 360 | service_config.extend(" %s" % service_option.strip() | 366 | service_config.extend(" %s" % service_option.strip() |
101 | 361 | for service_option in fe_options) | 367 | for service_option in fe_options) |
106 | 362 | service_config.append("") | 368 | |
107 | 363 | service_config.append("backend %s" % (service_name,)) | 369 | # For now errorfiles are common for all backends, in the future we |
108 | 364 | service_config.extend(" %s" % service_option.strip() | 370 | # might offer support for per-backend error files. |
109 | 365 | for service_option in be_options) | 371 | backend_errorfiles = [] # List of (status, path) tuples |
110 | 366 | if service_errorfiles is not None: | 372 | if service_errorfiles is not None: |
111 | 367 | for errorfile in service_errorfiles: | 373 | for errorfile in service_errorfiles: |
112 | 368 | path = os.path.join(default_haproxy_lib_dir, | 374 | path = os.path.join(default_haproxy_lib_dir, |
113 | 369 | "service_%s" % service_name, | 375 | "service_%s" % service_name, |
114 | 370 | "%s.http" % errorfile["http_status"]) | 376 | "%s.http" % errorfile["http_status"]) |
117 | 371 | service_config.append( | 377 | backend_errorfiles.append((errorfile["http_status"], path)) |
118 | 372 | " errorfile %s %s" % (errorfile["http_status"], path)) | 378 | |
119 | 379 | # Default backend | ||
120 | 380 | _append_backend( | ||
121 | 381 | service_config, service_name, be_options, backend_errorfiles, | ||
122 | 382 | server_entries) | ||
123 | 383 | |||
124 | 384 | # Extra backends | ||
125 | 385 | if service_backends is not None: | ||
126 | 386 | for service_backend in service_backends: | ||
127 | 387 | _append_backend( | ||
128 | 388 | service_config, service_backend["backend_name"], | ||
129 | 389 | be_options, backend_errorfiles, service_backend["servers"]) | ||
130 | 390 | |||
131 | 391 | return '\n'.join(service_config) | ||
132 | 392 | |||
133 | 393 | |||
134 | 394 | def _append_backend(service_config, name, options, errorfiles, server_entries): | ||
135 | 395 | """Append a new backend stanza to the given service_config. | ||
136 | 396 | |||
137 | 397 | A backend stanza consists in a 'backend <name>' line followed by option | ||
138 | 398 | lines, errorfile lines and server line. | ||
139 | 399 | """ | ||
140 | 400 | service_config.append("") | ||
141 | 401 | service_config.append("backend %s" % (name,)) | ||
142 | 402 | service_config.extend(" %s" % option.strip() for option in options) | ||
143 | 403 | for status, path in errorfiles: | ||
144 | 404 | service_config.append(" errorfile %s %s" % (status, path)) | ||
145 | 373 | if isinstance(server_entries, (list, tuple)): | 405 | if isinstance(server_entries, (list, tuple)): |
146 | 374 | for i, (server_name, server_ip, server_port, | 406 | for i, (server_name, server_ip, server_port, |
147 | 375 | server_options) in enumerate(server_entries): | 407 | server_options) in enumerate(server_entries): |
148 | @@ -382,7 +414,6 @@ | |||
149 | 382 | server_line += " " + " ".join(server_options) | 414 | server_line += " " + " ".join(server_options) |
150 | 383 | server_line = server_line.format(i=i) | 415 | server_line = server_line.format(i=i) |
151 | 384 | service_config.append(server_line) | 416 | service_config.append(server_line) |
152 | 385 | return '\n'.join(service_config) | ||
153 | 386 | 417 | ||
154 | 387 | 418 | ||
155 | 388 | # ----------------------------------------------------------------------------- | 419 | # ----------------------------------------------------------------------------- |
156 | @@ -403,7 +434,7 @@ | |||
157 | 403 | monitoring_config.append("mode http") | 434 | monitoring_config.append("mode http") |
158 | 404 | monitoring_config.append("acl allowed_cidr src %s" % | 435 | monitoring_config.append("acl allowed_cidr src %s" % |
159 | 405 | config_data['monitoring_allowed_cidr']) | 436 | config_data['monitoring_allowed_cidr']) |
161 | 406 | monitoring_config.append("block unless allowed_cidr") | 437 | monitoring_config.append("http-request deny unless allowed_cidr") |
162 | 407 | monitoring_config.append("stats enable") | 438 | monitoring_config.append("stats enable") |
163 | 408 | monitoring_config.append("stats uri /") | 439 | monitoring_config.append("stats uri /") |
164 | 409 | monitoring_config.append("stats realm Haproxy\ Statistics") | 440 | monitoring_config.append("stats realm Haproxy\ Statistics") |
165 | @@ -475,11 +506,20 @@ | |||
166 | 475 | service = new_service.copy() | 506 | service = new_service.copy() |
167 | 476 | service.update(old_service) | 507 | service.update(old_service) |
168 | 477 | if "servers" in service: | 508 | if "servers" in service: |
169 | 509 | # Merge all 'servers' entries of the default backend | ||
170 | 478 | servers = service["servers"] | 510 | servers = service["servers"] |
171 | 479 | if "servers" in new_service: | 511 | if "servers" in new_service: |
172 | 480 | servers.extend(new_service["servers"]) | 512 | servers.extend(new_service["servers"]) |
173 | 481 | servers.sort() | 513 | servers.sort() |
174 | 482 | service["servers"] = list(x for x, _ in groupby(servers)) | 514 | service["servers"] = list(x for x, _ in groupby(servers)) |
175 | 515 | if "backends" in service and "backends" in new_service: | ||
176 | 516 | # Merge all 'servers' entries of the additional backends | ||
177 | 517 | for i, backend in enumerate(service["backends"]): | ||
178 | 518 | servers = backend["servers"] | ||
179 | 519 | servers.extend(new_service["backends"][i]["servers"]) | ||
180 | 520 | servers.sort() | ||
181 | 521 | service["backends"][i]["servers"] = list( | ||
182 | 522 | x for x, _ in groupby(servers)) | ||
183 | 483 | return service | 523 | return service |
184 | 484 | 524 | ||
185 | 485 | 525 | ||
186 | @@ -688,6 +728,7 @@ | |||
187 | 688 | log("Service: %s" % service_key) | 728 | log("Service: %s" % service_key) |
188 | 689 | service_name = service_config["service_name"] | 729 | service_name = service_config["service_name"] |
189 | 690 | server_entries = service_config.get('servers') | 730 | server_entries = service_config.get('servers') |
190 | 731 | backends = service_config.get('backends', []) | ||
191 | 691 | 732 | ||
192 | 692 | errorfiles = service_config.get('errorfiles', []) | 733 | errorfiles = service_config.get('errorfiles', []) |
193 | 693 | for errorfile in errorfiles: | 734 | for errorfile in errorfiles: |
194 | @@ -718,7 +759,7 @@ | |||
195 | 718 | service_config['service_host'], | 759 | service_config['service_host'], |
196 | 719 | service_config['service_port'], | 760 | service_config['service_port'], |
197 | 720 | service_config['service_options'], | 761 | service_config['service_options'], |
199 | 721 | server_entries, errorfiles, crts)) | 762 | server_entries, errorfiles, crts, backends)) |
200 | 722 | 763 | ||
201 | 723 | 764 | ||
202 | 724 | def get_service_lib_path(service_name): | 765 | def get_service_lib_path(service_name): |
203 | 725 | 766 | ||
204 | === modified file 'hooks/tests/test_helpers.py' | |||
205 | --- hooks/tests/test_helpers.py 2015-02-09 11:39:40 +0000 | |||
206 | +++ hooks/tests/test_helpers.py 2015-03-18 20:28:50 +0000 | |||
207 | @@ -478,6 +478,38 @@ | |||
208 | 478 | 478 | ||
209 | 479 | self.assertEqual(expected, result) | 479 | self.assertEqual(expected, result) |
210 | 480 | 480 | ||
211 | 481 | @patch.dict(os.environ, {"JUJU_UNIT_NAME": "haproxy/2"}) | ||
212 | 482 | def test_creates_a_listen_stanza_with_backends(self): | ||
213 | 483 | service_name = 'foo' | ||
214 | 484 | service_ip = '1.2.3.4' | ||
215 | 485 | service_port = 80 | ||
216 | 486 | server_entries = [ | ||
217 | 487 | ('name-1', 'ip-1', 'port-1', ('foo1', 'bar1')), | ||
218 | 488 | ] | ||
219 | 489 | service_backends = [ | ||
220 | 490 | {"backend_name": "foo-bar", | ||
221 | 491 | "servers": [ | ||
222 | 492 | ('bar-name-1', 'bar-ip-1', 'bar-port-1', ('bar2', 'bar3')) | ||
223 | 493 | ]} | ||
224 | 494 | ] | ||
225 | 495 | result = hooks.create_listen_stanza( | ||
226 | 496 | service_name, service_ip, service_port, | ||
227 | 497 | server_entries=server_entries, service_backends=service_backends) | ||
228 | 498 | |||
229 | 499 | expected = '\n'.join(( | ||
230 | 500 | 'frontend haproxy-2-80', | ||
231 | 501 | ' bind 1.2.3.4:80', | ||
232 | 502 | ' default_backend foo', | ||
233 | 503 | '', | ||
234 | 504 | 'backend foo', | ||
235 | 505 | ' server name-1 ip-1:port-1 foo1 bar1', | ||
236 | 506 | '', | ||
237 | 507 | 'backend foo-bar', | ||
238 | 508 | ' server bar-name-1 bar-ip-1:bar-port-1 bar2 bar3', | ||
239 | 509 | )) | ||
240 | 510 | |||
241 | 511 | self.assertEqual(expected, result) | ||
242 | 512 | |||
243 | 481 | def test_doesnt_create_listen_stanza_if_args_not_provided(self): | 513 | def test_doesnt_create_listen_stanza_if_args_not_provided(self): |
244 | 482 | self.assertIsNone(hooks.create_listen_stanza()) | 514 | self.assertIsNone(hooks.create_listen_stanza()) |
245 | 483 | 515 | ||
246 | @@ -504,7 +536,7 @@ | |||
247 | 504 | 'some-service', '0.0.0.0', 1234, [ | 536 | 'some-service', '0.0.0.0', 1234, [ |
248 | 505 | 'mode http', | 537 | 'mode http', |
249 | 506 | 'acl allowed_cidr src some-cidr', | 538 | 'acl allowed_cidr src some-cidr', |
251 | 507 | 'block unless allowed_cidr', | 539 | 'http-request deny unless allowed_cidr', |
252 | 508 | 'stats enable', | 540 | 'stats enable', |
253 | 509 | 'stats uri /', | 541 | 'stats uri /', |
254 | 510 | 'stats realm Haproxy\\ Statistics', | 542 | 'stats realm Haproxy\\ Statistics', |
255 | @@ -551,7 +583,7 @@ | |||
256 | 551 | 'some-service', '0.0.0.0', 1234, [ | 583 | 'some-service', '0.0.0.0', 1234, [ |
257 | 552 | 'mode http', | 584 | 'mode http', |
258 | 553 | 'acl allowed_cidr src some-cidr', | 585 | 'acl allowed_cidr src some-cidr', |
260 | 554 | 'block unless allowed_cidr', | 586 | 'http-request deny unless allowed_cidr', |
261 | 555 | 'stats enable', | 587 | 'stats enable', |
262 | 556 | 'stats uri /', | 588 | 'stats uri /', |
263 | 557 | 'stats realm Haproxy\\ Statistics', | 589 | 'stats realm Haproxy\\ Statistics', |
264 | @@ -585,7 +617,7 @@ | |||
265 | 585 | 'some-service', '0.0.0.0', 1234, [ | 617 | 'some-service', '0.0.0.0', 1234, [ |
266 | 586 | 'mode http', | 618 | 'mode http', |
267 | 587 | 'acl allowed_cidr src some-cidr', | 619 | 'acl allowed_cidr src some-cidr', |
269 | 588 | 'block unless allowed_cidr', | 620 | 'http-request deny unless allowed_cidr', |
270 | 589 | 'stats enable', | 621 | 'stats enable', |
271 | 590 | 'stats uri /', | 622 | 'stats uri /', |
272 | 591 | 'stats realm Haproxy\\ Statistics', | 623 | 'stats realm Haproxy\\ Statistics', |
273 | 592 | 624 | ||
274 | === modified file 'hooks/tests/test_peer_hooks.py' | |||
275 | --- hooks/tests/test_peer_hooks.py 2015-02-19 17:05:11 +0000 | |||
276 | +++ hooks/tests/test_peer_hooks.py 2015-03-18 20:28:50 +0000 | |||
277 | @@ -197,7 +197,7 @@ | |||
278 | 197 | 197 | ||
279 | 198 | create_listen_stanza.assert_called_with( | 198 | create_listen_stanza.assert_called_with( |
280 | 199 | 'bar', 'some-host', 'some-port', 'some-options', | 199 | 'bar', 'some-host', 'some-port', 'some-options', |
282 | 200 | (1, 2), [], []) | 200 | (1, 2), [], [], []) |
283 | 201 | mock_open.assert_called_with( | 201 | mock_open.assert_called_with( |
284 | 202 | '/var/run/haproxy/bar.service', 'w') | 202 | '/var/run/haproxy/bar.service', 'w') |
285 | 203 | mock_file.write.assert_called_with('some content') | 203 | mock_file.write.assert_called_with('some content') |
286 | 204 | 204 | ||
287 | === modified file 'hooks/tests/test_reverseproxy_hooks.py' | |||
288 | --- hooks/tests/test_reverseproxy_hooks.py 2014-01-21 15:46:29 +0000 | |||
289 | +++ hooks/tests/test_reverseproxy_hooks.py 2015-03-18 20:28:50 +0000 | |||
290 | @@ -393,6 +393,72 @@ | |||
291 | 393 | self.assertEqual(expected, hooks.create_services()) | 393 | self.assertEqual(expected, hooks.create_services()) |
292 | 394 | self.write_service_config.assert_called_with(expected) | 394 | self.write_service_config.assert_called_with(expected) |
293 | 395 | 395 | ||
294 | 396 | def test_with_multiple_units_and_backends_in_relation(self): | ||
295 | 397 | """ | ||
296 | 398 | Have multiple units specifying "services" in the relation | ||
297 | 399 | using the "backends" option. Make sure data is created correctly | ||
298 | 400 | with create_services() | ||
299 | 401 | """ | ||
300 | 402 | self.get_config_services.return_value = { | ||
301 | 403 | None: { | ||
302 | 404 | "service_name": "service", | ||
303 | 405 | }, | ||
304 | 406 | } | ||
305 | 407 | self.relations_of_type.return_value = [ | ||
306 | 408 | {"port": 4242, | ||
307 | 409 | "private-address": "1.2.3.4", | ||
308 | 410 | "__unit__": "foo/0", | ||
309 | 411 | "services": yaml.safe_dump([{ | ||
310 | 412 | "service_name": "service", | ||
311 | 413 | "servers": [('foo-0', '1.2.3.4', | ||
312 | 414 | 4242, ["maxconn 4"])], | ||
313 | 415 | "backends": [ | ||
314 | 416 | {"backend_name": "foo-bar", | ||
315 | 417 | "servers": [('foo-bar-0', '2.2.2.2', | ||
316 | 418 | 2222, ["maxconn 4"])], | ||
317 | 419 | }, | ||
318 | 420 | ] | ||
319 | 421 | }]) | ||
320 | 422 | }, | ||
321 | 423 | {"port": 4242, | ||
322 | 424 | "private-address": "1.2.3.5", | ||
323 | 425 | "__unit__": "foo/1", | ||
324 | 426 | "services": yaml.safe_dump([{ | ||
325 | 427 | "service_name": "service", | ||
326 | 428 | "servers": [('foo-0', '1.2.3.5', | ||
327 | 429 | 4242, ["maxconn 4"])], | ||
328 | 430 | "backends": [ | ||
329 | 431 | {"backend_name": "foo-bar", | ||
330 | 432 | "servers": [('foo-bar-1', '2.2.2.3', | ||
331 | 433 | 3333, ["maxconn 4"])], | ||
332 | 434 | }, | ||
333 | 435 | ] | ||
334 | 436 | }]) | ||
335 | 437 | }, | ||
336 | 438 | ] | ||
337 | 439 | |||
338 | 440 | expected = { | ||
339 | 441 | 'service': { | ||
340 | 442 | 'service_name': 'service', | ||
341 | 443 | 'service_host': '0.0.0.0', | ||
342 | 444 | 'service_port': 10002, | ||
343 | 445 | 'servers': [ | ||
344 | 446 | ['foo-0', '1.2.3.4', 4242, ["maxconn 4"]], | ||
345 | 447 | ['foo-0', '1.2.3.5', 4242, ["maxconn 4"]] | ||
346 | 448 | ], | ||
347 | 449 | 'backends': [ | ||
348 | 450 | {"backend_name": "foo-bar", | ||
349 | 451 | "servers": [ | ||
350 | 452 | ['foo-bar-0', '2.2.2.2', 2222, ["maxconn 4"]], | ||
351 | 453 | ['foo-bar-1', '2.2.2.3', 3333, ["maxconn 4"]], | ||
352 | 454 | ], | ||
353 | 455 | }, | ||
354 | 456 | ] | ||
355 | 457 | }, | ||
356 | 458 | } | ||
357 | 459 | self.assertEqual(expected, hooks.create_services()) | ||
358 | 460 | self.write_service_config.assert_called_with(expected) | ||
359 | 461 | |||
360 | 396 | def test_merge_service(self): | 462 | def test_merge_service(self): |
361 | 397 | """ Make sure merge_services maintains "server" entries. """ | 463 | """ Make sure merge_services maintains "server" entries. """ |
362 | 398 | s1 = {'service_name': 'f', 'servers': [['f', '4', 4, ['maxconn 4']]]} | 464 | s1 = {'service_name': 'f', 'servers': [['f', '4', 4, ['maxconn 4']]]} |
363 | 399 | 465 | ||
364 | === modified file 'tests/10_deploy_test.py' | |||
365 | --- tests/10_deploy_test.py 2015-02-19 17:05:11 +0000 | |||
366 | +++ tests/10_deploy_test.py 2015-03-18 20:28:50 +0000 | |||
367 | @@ -12,7 +12,7 @@ | |||
368 | 12 | d = amulet.Deployment(series='trusty') | 12 | d = amulet.Deployment(series='trusty') |
369 | 13 | # Add the haproxy charm to the deployment. | 13 | # Add the haproxy charm to the deployment. |
370 | 14 | d.add('haproxy') | 14 | d.add('haproxy') |
372 | 15 | d.add('apache2') | 15 | d.add('apache2', units=2) |
373 | 16 | 16 | ||
374 | 17 | # Get the directory this way to load the file when CWD is different. | 17 | # Get the directory this way to load the file when CWD is different. |
375 | 18 | path = os.path.abspath(os.path.dirname(__file__)) | 18 | path = os.path.abspath(os.path.dirname(__file__)) |
376 | @@ -83,6 +83,7 @@ | |||
377 | 83 | 'configuration file.' % apache_private | 83 | 'configuration file.' % apache_private |
378 | 84 | amulet.raise_status(amulet.FAIL, msg=message) | 84 | amulet.raise_status(amulet.FAIL, msg=message) |
379 | 85 | 85 | ||
380 | 86 | # Test SSL termination | ||
381 | 86 | d.configure('haproxy', { | 87 | d.configure('haproxy', { |
382 | 87 | 'source': 'backports', | 88 | 'source': 'backports', |
383 | 88 | 'ssl_cert': 'SELFSIGNED', | 89 | 'ssl_cert': 'SELFSIGNED', |
384 | @@ -116,6 +117,7 @@ | |||
385 | 116 | page.raise_for_status() | 117 | page.raise_for_status() |
386 | 117 | page = requests.get(secure_url, verify=False) | 118 | page = requests.get(secure_url, verify=False) |
387 | 118 | page.raise_for_status() | 119 | page.raise_for_status() |
388 | 120 | success = True | ||
389 | 119 | except requests.exceptions.ConnectionError: | 121 | except requests.exceptions.ConnectionError: |
390 | 120 | if i == retries - 1: | 122 | if i == retries - 1: |
391 | 121 | # This was the last one, let's fail | 123 | # This was the last one, let's fail |
392 | @@ -126,5 +128,51 @@ | |||
393 | 126 | 128 | ||
394 | 127 | print('Successfully got the Apache2 web page through haproxy SSL termination.') | 129 | print('Successfully got the Apache2 web page through haproxy SSL termination.') |
395 | 128 | 130 | ||
396 | 131 | apache_unit2 = d.sentry.unit['apache2/1'] | ||
397 | 132 | apache_private2 = apache_unit2.run("unit-get private-address")[0] | ||
398 | 133 | |||
399 | 134 | # Create a file on the second apache unit's www directory. | ||
400 | 135 | apache_unit2.run("echo foo > /var/www/html/foo") | ||
401 | 136 | |||
402 | 137 | d.configure('haproxy', { | ||
403 | 138 | 'services': yaml.safe_dump([ | ||
404 | 139 | {'service_name': 'apache', | ||
405 | 140 | 'service_host': '0.0.0.0', | ||
406 | 141 | 'service_port': 80, | ||
407 | 142 | 'service_options': [ | ||
408 | 143 | 'mode http', 'balance leastconn', 'option httpchk GET / HTTP/1.0', | ||
409 | 144 | 'acl foo path_beg -i /foo', 'use_backend foo if foo', | ||
410 | 145 | ], | ||
411 | 146 | 'servers': [ | ||
412 | 147 | ['apache', apache_private, 80, 'maxconn 50']], | ||
413 | 148 | 'backends': [ | ||
414 | 149 | {'backend_name': 'foo', | ||
415 | 150 | 'servers': [ | ||
416 | 151 | ['apache2', apache_private2, 80, 'maxconn 50']]} | ||
417 | 152 | ]}]) | ||
418 | 153 | }) | ||
419 | 154 | |||
420 | 155 | # Let's exercise our URL-based routing by trying to fetch a URL that will | ||
421 | 156 | # only work for the second apache unit (which is configured as server | ||
422 | 157 | # of the extra backend). | ||
423 | 158 | url = 'http://%s/foo' % haproxy_address | ||
424 | 159 | |||
425 | 160 | # We need a retry loop here, since there's no way to tell when the new | ||
426 | 161 | # configuration is in place. | ||
427 | 162 | retries = 10 | ||
428 | 163 | for i in range(retries): | ||
429 | 164 | try: | ||
430 | 165 | page = requests.get(url) | ||
431 | 166 | page.raise_for_status() | ||
432 | 167 | except: | ||
433 | 168 | if i == retries - 1: | ||
434 | 169 | # This was the last one, let's fail | ||
435 | 170 | raise | ||
436 | 171 | time.sleep(6) | ||
437 | 172 | else: | ||
438 | 173 | break | ||
439 | 174 | |||
440 | 175 | print('Successfully got the /foo URL from the second Apache unit.') | ||
441 | 176 | |||
442 | 129 | # Send a message that the tests are complete. | 177 | # Send a message that the tests are complete. |
443 | 130 | print('The haproxy tests are complete.') | 178 | print('The haproxy tests are complete.') |
This items has failed automated testing! Results available here http:// reports. vapour. ws/charm- tests/charm- bundle- test-11100- results