Merge ~hloeung/content-cache-charm:master into content-cache-charm:master

Proposed by Haw Loeung
Status: Merged
Approved by: Haw Loeung
Approved revision: 539437892786c3e687ea2f7a9bc999e0fba1c12d
Merged at revision: 67f7f1384a4c1f788d2117dc31c377cc29a2051e
Proposed branch: ~hloeung/content-cache-charm:master
Merge into: content-cache-charm:master
Prerequisite: ~hloeung/content-cache-charm:haproxy-config
Diff against target: 392 lines (+317/-3)
5 files modified
config.yaml (+6/-0)
lib/haproxy.py (+9/-2)
reactive/content_cache.py (+6/-1)
tests/unit/files/content_cache_rendered_haproxy_test_output_load_balancing_algorithm.txt (+267/-0)
tests/unit/test_content_cache.py (+29/-0)
Reviewer Review Type Date Requested Status
Joel Sing (community) +1 Approve
Barry Price Approve
Review via email: mp+390519@code.launchpad.net

Commit message

Allow overriding default load balancing algorithm used by HAProxy - LP:1891263

To post a comment you must log in.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
Barry Price (barryprice) wrote :

We could consider adding safety checks to handle a typo or just a plain wrong setting via the haproxy_load_balancing_algorithm config item. If someone sets it to e.g. "xyzzy", will haproxy start? Should it?

On the other hand, if an operator is determined to break things, there's only so much the charms can do to prevent this. Other than that, LGTM - approving on that basis.

review: Approve
Revision history for this message
Joel Sing (jsing) wrote :

LGTM, minor comments inline.

review: Approve (+1)
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Change successfully merged at revision 67f7f1384a4c1f788d2117dc31c377cc29a2051e

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/config.yaml b/config.yaml
2index 12560a6..e634fe2 100644
3--- a/config.yaml
4+++ b/config.yaml
5@@ -45,6 +45,12 @@ options:
6 description: >
7 Tune HAProxy's hard-stop-after to prevent lingering HAProxy processes
8 (LP:1874386).
9+ haproxy_load_balancing_algorithm:
10+ default: "leastconn"
11+ type: string
12+ description: >
13+ Change the load balancing algorithm used by HAProxy for the
14+ backends.
15 haproxy_processes:
16 default: 0
17 type: int
18diff --git a/lib/haproxy.py b/lib/haproxy.py
19index c3f3a76..cdb5721 100644
20--- a/lib/haproxy.py
21+++ b/lib/haproxy.py
22@@ -11,15 +11,21 @@ from lib import utils
23
24
25 HAPROXY_BASE_PATH = '/etc/haproxy'
26+HAPROXY_LOAD_BALANCING_ALGORITHM = 'leastconn'
27 INDENT = ' ' * 4
28 TLS_CIPHER_SUITES = 'ECDHE+AESGCM:ECDHE+AES256:ECDHE+AES128:!SSLv3:!TLSv1'
29
30
31 class HAProxyConf:
32- def __init__(self, conf_path=HAPROXY_BASE_PATH, max_connections=0, hard_stop_after='5m'):
33+ def __init__(
34+ self, conf_path=HAPROXY_BASE_PATH, max_connections=0, hard_stop_after='5m', load_balancing_algorithm=None
35+ ):
36 self._conf_path = conf_path
37 self.max_connections = int(max_connections)
38 self.hard_stop_after = hard_stop_after
39+ self.load_balancing_algorithm = HAPROXY_LOAD_BALANCING_ALGORITHM
40+ if load_balancing_algorithm:
41+ self.load_balancing_algorithm = load_balancing_algorithm
42
43 @property
44 def conf_path(self):
45@@ -212,7 +218,7 @@ listen {name}
46 backend backend-{name}
47 {options}{indent}{httpchk}
48 {indent}http-request set-header Host {site_name}
49-{indent}balance leastconn
50+{indent}balance {load_balancing_algorithm}
51 {backends}
52 """
53 rendered_output = []
54@@ -293,6 +299,7 @@ backend backend-{name}
55 site=site,
56 site_name=site_name,
57 httpchk=httpchk,
58+ load_balancing_algorithm=self.load_balancing_algorithm,
59 backends='\n'.join(backend_confs),
60 options=options,
61 indent=INDENT,
62diff --git a/reactive/content_cache.py b/reactive/content_cache.py
63index 5b15f8a..5d4460b 100644
64--- a/reactive/content_cache.py
65+++ b/reactive/content_cache.py
66@@ -208,7 +208,12 @@ def configure_haproxy(): # NOQA: C901 LP#1825084
67
68 max_connections = config.get('max_connections', 0)
69 hard_stop_after = config.get('haproxy_hard_stop_after')
70- haproxy = HAProxy.HAProxyConf(max_connections=max_connections, hard_stop_after=hard_stop_after)
71+ load_balancing_algorithm = config.get('haproxy_load_balancing_algorithm')
72+ haproxy = HAProxy.HAProxyConf(
73+ max_connections=max_connections,
74+ hard_stop_after=hard_stop_after,
75+ load_balancing_algorithm=load_balancing_algorithm,
76+ )
77 sites_secrets = secrets_from_config(config.get('sites_secrets'))
78 blacklist_ports = [int(x.strip()) for x in config.get('blacklist_ports', '').split(',') if x.strip()]
79 sites = sites_from_config(config.get('sites'), sites_secrets, blacklist_ports=blacklist_ports)
80diff --git a/tests/unit/files/content_cache_rendered_haproxy_test_output_load_balancing_algorithm.txt b/tests/unit/files/content_cache_rendered_haproxy_test_output_load_balancing_algorithm.txt
81new file mode 100644
82index 0000000..b0c6370
83--- /dev/null
84+++ b/tests/unit/files/content_cache_rendered_haproxy_test_output_load_balancing_algorithm.txt
85@@ -0,0 +1,267 @@
86+global
87+ nbthread 4
88+ maxconn 106496
89+ log /dev/log local0
90+ log /dev/log local1 notice
91+ chroot /var/lib/haproxy
92+ stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
93+ stats timeout 30s
94+ user haproxy
95+ group haproxy
96+ daemon
97+
98+ # LP#1874386: Work around lingering HAProxy processes as per LP:1874386
99+ # and kill them off.
100+ hard-stop-after 15m
101+
102+ # Default SSL material locations
103+ ca-base /etc/ssl/certs
104+ crt-base /etc/ssl/private
105+
106+ # Default ciphers to use on SSL-enabled listening sockets.
107+ # For more information, see ciphers(1SSL). This list is from:
108+ # https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
109+ # An alternative list with additional directives can be obtained from
110+ # https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=haproxy
111+ ssl-default-bind-ciphers ECDHE+AESGCM:ECDHE+AES256:ECDHE+AES128:!SSLv3:!TLSv1
112+ ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11
113+ # We'll eventually disable DHE (LP#1825321), but for now, bump DH params
114+ tune.ssl.default-dh-param 2048
115+
116+ # Increase the SSL/TLS session cache from the default 20k. But
117+ # rather than hardcode values, let's just set it to match
118+ # global_max_connections (which by default is calculated using
119+ # num. of CPU cores and num. of configured sites). Each entry
120+ # requires ~200 bytes so on a host with say 32 CPUs, 10 sites,
121+ # each with 2000 max conns will only consume around 122 Mbytes
122+ # (32 * 10 * 2000 * 200), which is not much.
123+ tune.ssl.cachesize 106496
124+
125+defaults
126+ log global
127+ maxconn 8192
128+ mode http
129+ option httplog
130+ option dontlognull
131+ timeout connect 5s
132+ timeout client 50s
133+ timeout server 50s
134+ errorfile 400 /etc/haproxy/errors/400.http
135+ errorfile 403 /etc/haproxy/errors/403.http
136+ errorfile 408 /etc/haproxy/errors/408.http
137+ errorfile 500 /etc/haproxy/errors/500.http
138+ errorfile 502 /etc/haproxy/errors/502.http
139+ errorfile 503 /etc/haproxy/errors/503.http
140+ errorfile 504 /etc/haproxy/errors/504.http
141+
142+resolvers dns
143+ nameserver dns1 127.0.0.53:53
144+ resolve_retries 3
145+ timeout resolve 3s
146+ timeout retry 3s
147+ accepted_payload_size 8192
148+
149+listen stats
150+ bind 127.0.0.1:10000
151+ acl allowed_cidr src 127.0.0.0/8
152+ http-request deny unless allowed_cidr
153+
154+ mode http
155+ stats enable
156+ stats uri /
157+ stats realm Haproxy\ Statistics
158+ stats auth haproxy:biometricsarenotsecret
159+ stats refresh 3
160+
161+
162+listen combined-80
163+ bind 0.0.0.0:80
164+ bind :::80
165+ redirect scheme https code 301 if { hdr(Host) -i site2.local } !{ ssl_fc }
166+ use_backend backend-cached-site1-local if { hdr(Host) -i site1.local }
167+ use_backend backend-cached-site3-local if { hdr(Host) -i site3.local }
168+ use_backend backend-cached-site4-local if { hdr(Host) -i site4.local }
169+ use_backend backend-cached-site5 if { hdr(Host) -i site5.local }
170+ use_backend backend-cached-site6-local if { hdr(Host) -i site6.local }
171+ use_backend backend-cached-site9-local if { hdr(Host) -i site9.local }
172+ default_backend backend-cached-site3-local
173+
174+listen site1-local
175+ bind 127.0.0.1:8080
176+ default_backend backend-site1-local
177+
178+listen cached-site2-local
179+ bind 0.0.0.0:443 ssl crt /etc/haproxy/site2-bundle.crt alpn h2,http/1.1
180+ bind :::443 ssl crt /etc/haproxy/site2-bundle.crt alpn h2,http/1.1
181+ default_backend backend-cached-site2-local
182+
183+listen site2-local
184+ bind 127.0.0.1:8081
185+ default_backend backend-site2-local
186+
187+listen site3-local
188+ bind 127.0.0.1:8082
189+ default_backend backend-site3-local
190+
191+listen site5
192+ bind 127.0.0.1:8083
193+ default_backend backend-site5
194+
195+listen site5-2
196+ bind 127.0.0.1:8084
197+ default_backend backend-site5-2
198+
199+listen site6-local
200+ bind 127.0.0.1:8085
201+ default_backend backend-site6-local
202+
203+listen combined-444
204+ bind 0.0.0.0:444 ssl crt /etc/haproxy/site7-bundle.crt crt /etc/haproxy/site8-bundle.crt alpn h2,http/1.1
205+ bind :::444 ssl crt /etc/haproxy/site7-bundle.crt crt /etc/haproxy/site8-bundle.crt alpn h2,http/1.1
206+ use_backend backend-cached-site7-local if { hdr(Host) -i site7.local }
207+ use_backend backend-cached-site8-local if { hdr(Host) -i site8.local }
208+
209+listen site7-local
210+ bind 127.0.0.1:8086
211+ default_backend backend-site7-local
212+
213+listen site8-local
214+ bind 127.0.0.1:8087
215+ default_backend backend-site8-local
216+
217+listen site8-local-2
218+ bind 127.0.0.1:8088
219+ default_backend backend-site8-local-2
220+
221+listen site9-local
222+ bind 127.0.0.1:8089
223+ default_backend backend-site9-local
224+
225+backend backend-cached-site1-local
226+ option forwardfor
227+ option httpchk HEAD /?token=1861920000_f3e404e205ed44749e942d481f7a7bec57c5e78a HTTP/1.0\r\nHost:\ site1.local\r\nUser-Agent:\ haproxy/httpchk\r\nCache-Control:\ no-cache
228+ http-request set-header Host site1.local
229+ balance roundrobin
230+ server server_1 127.0.0.1:6080 check inter 2s rise 2 fall 60 maxconn 2048
231+
232+backend backend-site1-local
233+ option httpchk HEAD / HTTP/1.0\r\nHost:\ site1.local\r\nUser-Agent:\ haproxy/httpchk\r\nCache-Control:\ no-cache
234+ http-request set-header Host site1.local
235+ balance roundrobin
236+ server server_1 127.0.1.10:80 check inter 5s rise 2 fall 5 maxconn 2048
237+ server server_2 127.0.1.11:80 check inter 5s rise 2 fall 5 maxconn 2048
238+ server server_3 127.0.1.12:80 check inter 5s rise 2 fall 5 maxconn 2048
239+
240+backend backend-cached-site2-local
241+ option forwardfor
242+ option httpchk GET /check/ HTTP/1.0\r\nHost:\ site2.local\r\nUser-Agent:\ haproxy/httpchk\r\nCache-Control:\ no-cache
243+ http-request set-header Host site2.local
244+ balance roundrobin
245+ server server_1 127.0.0.1:6081 check inter 2s rise 2 fall 60 maxconn 2048
246+
247+backend backend-site2-local
248+ option httpchk GET /check/ HTTP/1.0\r\nHost:\ site2.local\r\nUser-Agent:\ haproxy/httpchk\r\nCache-Control:\ no-cache
249+ http-request set-header Host site2.local
250+ balance roundrobin
251+ server server_1 127.0.1.10:443 check inter 5s rise 2 fall 5 maxconn 1024 ssl sni str(site2.local) check-sni site2.local verify required ca-file ca-certificates.crt
252+ server server_2 127.0.1.11:443 check inter 5s rise 2 fall 5 maxconn 1024 ssl sni str(site2.local) check-sni site2.local verify required ca-file ca-certificates.crt
253+ server server_3 127.0.1.12:443 check inter 5s rise 2 fall 5 maxconn 1024 ssl sni str(site2.local) check-sni site2.local verify required ca-file ca-certificates.crt
254+
255+backend backend-cached-site3-local
256+ option forwardfor
257+ option httpchk HEAD / HTTP/1.0\r\nHost:\ site3.local\r\nUser-Agent:\ haproxy/httpchk\r\nCache-Control:\ no-cache
258+ http-request set-header Host site3.local
259+ balance roundrobin
260+ server server_1 127.0.0.1:6082 check inter 2s rise 2 fall 60 maxconn 4096
261+
262+backend backend-site3-local
263+ option httpchk HEAD / HTTP/1.0\r\nHost:\ site3.local\r\nUser-Agent:\ haproxy/httpchk\r\nCache-Control:\ no-cache
264+ http-request set-header Host site3.local
265+ balance roundrobin
266+ server server_1 127.0.1.10:80 check inter 5s rise 2 fall 5 maxconn 2048
267+ server server_2 127.0.1.11:80 check inter 5s rise 2 fall 5 maxconn 2048
268+ server server_3 127.0.1.12:80 check inter 5s rise 2 fall 5 maxconn 2048
269+
270+backend backend-cached-site4-local
271+ option forwardfor
272+ option httpchk HEAD / HTTP/1.0\r\nHost:\ site4.local\r\nUser-Agent:\ haproxy/httpchk\r\nCache-Control:\ no-cache
273+ http-request set-header Host site4.local
274+ balance roundrobin
275+ server server_1 127.0.0.1:6083 check inter 2s rise 2 fall 60 maxconn 2048
276+
277+backend backend-cached-site5
278+ option forwardfor
279+ option httpchk HEAD / HTTP/1.0\r\nHost:\ site5.local\r\nUser-Agent:\ haproxy/httpchk\r\nCache-Control:\ no-cache
280+ http-request set-header Host site5.local
281+ balance roundrobin
282+ server server_1 127.0.0.1:6084 check inter 2s rise 2 fall 60 maxconn 2048
283+
284+backend backend-site5
285+ option httpchk HEAD / HTTP/1.0\r\nHost:\ site5.local\r\nUser-Agent:\ haproxy/httpchk\r\nCache-Control:\ no-cache
286+ http-request set-header Host site5.local
287+ balance roundrobin
288+ server server_1 127.0.1.10:80 check inter 5s rise 2 fall 5 maxconn 2048
289+
290+backend backend-site5-2
291+ option httpchk HEAD / HTTP/1.0\r\nHost:\ site5.local\r\nUser-Agent:\ haproxy/httpchk\r\nCache-Control:\ no-cache
292+ http-request set-header Host site5.local
293+ balance roundrobin
294+ server server_1 127.0.1.11:80 check inter 5s rise 2 fall 5 maxconn 2048
295+
296+backend backend-cached-site6-local
297+ option forwardfor
298+ option httpchk HEAD / HTTP/1.0\r\nHost:\ site6.local\r\nUser-Agent:\ haproxy/httpchk\r\nCache-Control:\ no-cache
299+ http-request set-header Host site6.local
300+ balance roundrobin
301+ server server_1 127.0.0.1:6085 check inter 2s rise 2 fall 60 maxconn 2048
302+
303+backend backend-site6-local
304+ option httpchk HEAD / HTTP/1.0\r\nHost:\ site6.local\r\nUser-Agent:\ haproxy/httpchk\r\nCache-Control:\ no-cache
305+ http-request set-header Host site6.local
306+ balance roundrobin
307+ server server_1 127.0.1.10:443 check inter 5s rise 2 fall 5 maxconn 2048 ssl sni str(site6.local) check-sni site6.local verify required ca-file ca-certificates.crt
308+
309+backend backend-cached-site7-local
310+ option forwardfor
311+ option httpchk HEAD / HTTP/1.0\r\nHost:\ site7.local\r\nUser-Agent:\ haproxy/httpchk\r\nCache-Control:\ no-cache
312+ http-request set-header Host site7.local
313+ balance roundrobin
314+ server server_1 127.0.0.1:6086 check inter 2s rise 2 fall 60 maxconn 2048
315+
316+backend backend-site7-local
317+ option httpchk HEAD / HTTP/1.0\r\nHost:\ site7.local\r\nUser-Agent:\ haproxy/httpchk\r\nCache-Control:\ no-cache
318+ http-request set-header Host site7.local
319+ balance roundrobin
320+ server server_1 127.0.1.10:80 check inter 5s rise 2 fall 5 maxconn 2048
321+
322+backend backend-cached-site8-local
323+ option forwardfor
324+ option httpchk HEAD / HTTP/1.0\r\nHost:\ site8.local\r\nUser-Agent:\ haproxy/httpchk\r\nCache-Control:\ no-cache
325+ http-request set-header Host site8.local
326+ balance roundrobin
327+ server server_1 127.0.0.1:6087 check inter 2s rise 2 fall 60 maxconn 2048
328+
329+backend backend-site8-local
330+ option httpchk HEAD / HTTP/1.0\r\nHost:\ site8.local\r\nUser-Agent:\ haproxy/httpchk\r\nCache-Control:\ no-cache
331+ http-request set-header Host site8.local
332+ balance roundrobin
333+ server server_1 127.0.1.10:80 check inter 5s rise 2 fall 5 maxconn 2048
334+
335+backend backend-site8-local-2
336+ option httpchk HEAD / HTTP/1.0\r\nHost:\ auth.site8.local\r\nUser-Agent:\ haproxy/httpchk\r\nCache-Control:\ no-cache
337+ http-request set-header Host auth.site8.local
338+ balance roundrobin
339+ server server_1 127.0.1.10:443 check inter 5s rise 2 fall 5 maxconn 2048 ssl sni str(auth.site8.local) check-sni auth.site8.local verify required ca-file ca-certificates.crt
340+
341+backend backend-cached-site9-local
342+ option forwardfor
343+ option httpchk HEAD / HTTP/1.0\r\nHost:\ site9.local\r\nUser-Agent:\ haproxy/httpchk\r\nCache-Control:\ no-cache
344+ http-request set-header Host site9.local
345+ balance roundrobin
346+ server server_1 127.0.0.1:6088 check inter 2s rise 2 fall 60 maxconn 2048
347+
348+backend backend-site9-local
349+ option httpchk HEAD / HTTP/1.0\r\nHost:\ site9.local\r\nUser-Agent:\ haproxy/httpchk\r\nCache-Control:\ no-cache
350+ http-request set-header Host site9.local
351+ balance roundrobin
352+ server server_1 127.0.1.15:80 check inter 1m rise 2 fall 5 maxconn 2048
353diff --git a/tests/unit/test_content_cache.py b/tests/unit/test_content_cache.py
354index 3b62cbb..561cd53 100644
355--- a/tests/unit/test_content_cache.py
356+++ b/tests/unit/test_content_cache.py
357@@ -485,6 +485,35 @@ site1.local:
358 @freezegun.freeze_time("2019-03-22", tz_offset=0)
359 @mock.patch('charmhelpers.core.hookenv.opened_ports')
360 @mock.patch('charms.reactive.set_flag')
361+ @mock.patch('reactive.content_cache.update_logrotate')
362+ def test_configure_haproxy_sites_load_balancing_algorithm(self, logrotation, set_flag, opened_ports):
363+ with open('tests/unit/files/config_test_config.txt', 'r', encoding='utf-8') as f:
364+ config = f.read()
365+ self.mock_config.return_value = {
366+ 'haproxy_hard_stop_after': '15m',
367+ 'haproxy_load_balancing_algorithm': 'roundrobin',
368+ 'max_connections': 8192,
369+ 'sites': config,
370+ }
371+
372+ with mock.patch('lib.haproxy.HAProxyConf.conf_file', new_callable=mock.PropertyMock) as mock_conf_file:
373+ mock_conf_file.return_value = os.path.join(self.tmpdir, 'haproxy.cfg')
374+ opened_ports.return_value = ['443/tcp']
375+ content_cache.configure_haproxy()
376+
377+ with open(
378+ 'tests/unit/files/content_cache_rendered_haproxy_test_output_load_balancing_algorithm.txt',
379+ 'r',
380+ encoding='utf-8',
381+ ) as f:
382+ want = f.read()
383+ with open(os.path.join(self.tmpdir, 'haproxy.cfg'), 'r', encoding='utf-8') as f:
384+ got = f.read()
385+ self.assertEqual(got, want)
386+
387+ @freezegun.freeze_time("2019-03-22", tz_offset=0)
388+ @mock.patch('charmhelpers.core.hookenv.opened_ports')
389+ @mock.patch('charms.reactive.set_flag')
390 @mock.patch('lib.utils.package_version')
391 @mock.patch('reactive.content_cache.update_logrotate')
392 def test_configure_haproxy_processes_and_threads(self, logrotation, package_version, set_flag, opened_ports):

Subscribers

People subscribed via source and target branches