Merge lp:~axino/charms/trusty/haproxy/trunk-merges into lp:charms/trusty/haproxy

Proposed by Junien F
Status: Merged
Merged at revision: 95
Proposed branch: lp:~axino/charms/trusty/haproxy/trunk-merges
Merge into: lp:charms/trusty/haproxy
Diff against target: 202 lines (+117/-6)
5 files modified
README.md (+51/-2)
config.yaml (+10/-0)
hooks/hooks.py (+5/-2)
hooks/tests/test_helpers.py (+1/-1)
hooks/tests/test_reverseproxy_hooks.py (+50/-1)
To merge this branch: bzr merge lp:~axino/charms/trusty/haproxy/trunk-merges
Reviewer Review Type Date Requested Status
Charles Butler (community) Approve
Review via email: mp+261632@code.launchpad.net

Description of the change

Add "peering-mode" option that allows "active-passive" mode or "active-active" mode. This option allows haproxy to scale as you add units.

Also, only open ports that aren't opened (and fix the appropriate check).

To post a comment you must log in.
96. By Junien F

[mhilton] only open ports that aren't opened

Revision history for this message
Charles Butler (lazypower) wrote :

Greetings Junien,

I've taken a look over this proposal and I have to say, a big thank you for crossing off all the lines that I look for

- Documentation update for the new feature
- Fully documented config option
- Tests to validate the feature works

+1 LGTM. Thank you for taking the time to submit this fix for the charm store. We appreciate your work. I've merged this branch and it should be available in the charm store after the next ingestion.

If you have any questions/comments/concerns about the review contact us in #juju on irc.freenode.net or email the mailing list <email address hidden>, or ask a question tagged with "juju" on http://askubuntu.com.

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-04-14 14:21:45 +0000
3+++ README.md 2015-06-10 14:45:40 +0000
4@@ -106,7 +106,6 @@
5
6 ## Website Relation
7
8-
9 The website relation is the other side of haproxy. It can communicate with
10 charms written like apache2 that can act as a front-end for haproxy to take of
11 things like ssl encryption. When joining a service like apache2 on its
12@@ -186,7 +185,57 @@
13 cross-environment relations then that will be the best way to handle
14 this configuration, as it will work in either scenario.
15
16-## HAProxy Project Information
17+## peering\_mode and the indirection layer
18+
19+If you are going to spawn multiple haproxy units, you should pay special
20+attention to the peering\_mode configuration option.
21+
22+### active-passive mode
23+
24+The peering\_mode option defaults to "active-passive" and in this mode, all
25+haproxy units ("peers") will proxy traffic to the first working peer (i.e. that
26+passes a basic layer4 check). What this means is that extra peers are working
27+as "hot spares", and so adding units doesn't add global bandwidth to the
28+haproxy layer.
29+
30+In order to achieve this, the charm configures a new service in haproxy that
31+will simply forward the traffic to the first working peer. The haproxy service
32+that actually load-balances between the backends is renamed, and its port
33+number is increased by one.
34+
35+For example, if you have 3 working haproxy units haproxy/0, haproxy/1 and
36+haproxy/2 configured to listen on port 80, in active-passive mode, and
37+haproxy/2 gets a request, the request is routed through the following path :
38+
39+haproxy/2:80 ==> haproxy/0:81 ==> \[backends\]
40+
41+In the same fashion, if haproxy/1 receives a request, it's routed in the following way :
42+
43+haproxy/1:80 ==> haproxy/0:81 ==> \[backends\]
44+
45+If haproxy/0 was to go down, then all the requests would be forwarded to the
46+next working peer, i.e. haproxy/1. In this case, a request received by
47+haproxy/2 would be routed as follows :
48+
49+haproxy/2:80 ==> haproxy/1:81 ==> \[backends\]
50+
51+This mode allows a strict control of the maximum number of connections the
52+backends will receive, and guarantees you'll have enough bandwidth to the
53+backends should an haproxy unit die, at the cost of having less overall
54+bandwidth to the backends.
55+
56+### active-active mode
57+
58+If the peering\_mode option is set to "active-active", then any haproxy unit
59+will be independant from each other and will simply load-balance the traffic to
60+the backends. In this case, the indirection layer described above is not
61+created in this case.
62+
63+This mode allows increasing the bandwidth to the backends by adding additional
64+units, at the cost of having less control over the number of connections that
65+they will receive.
66+
67+# HAProxy Project Information
68
69 - [HAProxy Homepage](http://haproxy.1wt.eu/)
70 - [HAProxy mailing list](http://haproxy.1wt.eu/#tact)
71
72=== modified file 'config.yaml'
73--- config.yaml 2015-02-09 11:39:40 +0000
74+++ config.yaml 2015-06-10 14:45:40 +0000
75@@ -202,3 +202,13 @@
76 description: |
77 Key ID to import to the apt keyring to support use with arbitary source
78 configuration from outside of Launchpad archives or PPA's.
79+ peering_mode:
80+ default: "active-passive"
81+ type: string
82+ description: |
83+ Possible values : "active-passive", "active-active". This is only used
84+ if several units are spawned. In "active-passive" mode, all the units will
85+ forward traffic to the first working haproxy unit, which will then forward it
86+ to configured backends. In "active-active" mode, each unit will proxy the
87+ traffic directly to the backends. The "active-passive" mode gives a better
88+ control of the maximum connection that will be opened to a backend server.
89
90=== modified file 'hooks/hooks.py'
91--- hooks/hooks.py 2015-05-15 16:54:14 +0000
92+++ hooks/hooks.py 2015-06-10 14:45:40 +0000
93@@ -264,7 +264,8 @@
94 if port not in new_service_ports:
95 close_port(port)
96 for port in new_service_ports:
97- open_port(port)
98+ if port not in old_service_ports:
99+ open_port(port)
100
101
102 # -----------------------------------------------------------------------------
103@@ -594,6 +595,7 @@
104 # -----------------------------------------------------------------------------
105 def create_services():
106 services_dict = get_config_services()
107+ config_data = config_get()
108
109 # Augment services_dict with service definitions from relation data.
110 relation_data = relations_of_type("reverseproxy")
111@@ -681,7 +683,8 @@
112
113 del services_dict[None]
114 services_dict = ensure_service_host_port(services_dict)
115- services_dict = apply_peer_config(services_dict)
116+ if config_data["peering_mode"] != "active-active":
117+ services_dict = apply_peer_config(services_dict)
118 write_service_config(services_dict)
119 return services_dict
120
121
122=== modified file 'hooks/tests/test_helpers.py'
123--- hooks/tests/test_helpers.py 2015-03-02 14:57:38 +0000
124+++ hooks/tests/test_helpers.py 2015-06-10 14:45:40 +0000
125@@ -278,7 +278,7 @@
126
127 self.assertEqual(close_port.mock_calls, [call(123), call(234)])
128 self.assertEqual(open_port.mock_calls,
129- [call(345), call(456), call(567)])
130+ [call(456), call(567)])
131
132 @patch('hooks.open_port')
133 @patch('hooks.close_port')
134
135=== modified file 'hooks/tests/test_reverseproxy_hooks.py'
136--- hooks/tests/test_reverseproxy_hooks.py 2015-05-15 14:31:41 +0000
137+++ hooks/tests/test_reverseproxy_hooks.py 2015-06-10 14:45:40 +0000
138@@ -13,7 +13,7 @@
139 super(ReverseProxyRelationTest, self).setUp()
140
141 self.config_get = self.patch_hook("config_get")
142- self.config_get.return_value = {"monitoring_port": "10000"}
143+ self.config_get.return_value = {"monitoring_port": "10000", "peering_mode": "active-passive"}
144 self.relations_of_type = self.patch_hook("relations_of_type")
145 self.get_config_services = self.patch_hook("get_config_services")
146 self.log = self.patch_hook("log")
147@@ -460,6 +460,55 @@
148 self.assertEqual(expected, hooks.create_services())
149 self.write_service_config.assert_called_with(expected)
150
151+ @patch.dict(os.environ, {"JUJU_UNIT_NAME": "foo/1"})
152+ def test_with_multiple_units_in_relation_scaleout(self):
153+ """
154+ Test multiple units in scaleout mode.
155+ Ensure no indirection layer gets created.
156+ """
157+ self.config_get.return_value["peering_mode"] = "active-active"
158+ self.get_config_services.return_value = {
159+ None: {
160+ "service_name": "service",
161+ },
162+ }
163+ self.unit_get.return_value = "1.2.4.5"
164+ self.relations_of_type.return_value = [
165+ {"port": 4242,
166+ "private-address": "1.2.4.4",
167+ "__unit__": "foo/0",
168+ "services": yaml.safe_dump([{
169+ "service_name": "service",
170+ "servers": [('foo-0', '1.2.3.4',
171+ 4242, ["maxconn 4"])]
172+ }])
173+ },
174+
175+ {"__unit__": "foo/1",
176+ "hostname": "foo-1",
177+ "private-address": "1.2.4.4",
178+ "all_services": yaml.dump([
179+ {"service_name": "service",
180+ "service_host": "0.0.0.0",
181+ "service_options": ["balance leastconn"],
182+ "service_port": 4242},
183+ ])
184+ },
185+ ]
186+
187+ expected = {
188+ 'service': {
189+ 'service_name': 'service',
190+ 'service_host': '0.0.0.0',
191+ 'service_port': 10002,
192+ 'servers': [
193+ ['foo-0', '1.2.3.4', 4242, ["maxconn 4"]],
194+ ]
195+ },
196+ }
197+ self.assertEqual(expected, hooks.create_services())
198+ self.write_service_config.assert_called_with(expected)
199+
200 def test_merge_service(self):
201 """ Make sure merge_services maintains "server" entries. """
202 s1 = {'service_name': 'f', 'servers': [['f', '4', 4, ['maxconn 4']]]}

Subscribers

People subscribed via source and target branches

to all changes: