Merge ~hloeung/content-cache-charm:rate-limiting into content-cache-charm:master
- Git
- lp:~hloeung/content-cache-charm
- rate-limiting
- Merge into master
Proposed by
Haw Loeung
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Haw Loeung | ||||
Approved revision: | 08cf92089609e1bdb3250d2931783b13d51e88ea | ||||
Merged at revision: | 61cc22b969fc5555ec7e0d5c37f0fca78ce6b9f8 | ||||
Proposed branch: | ~hloeung/content-cache-charm:rate-limiting | ||||
Merge into: | content-cache-charm:master | ||||
Diff against target: |
460 lines (+293/-2) 14 files modified
lib/haproxy.py (+21/-1) reactive/content_cache.py (+2/-0) tests/unit/files/config_test_config.txt (+6/-1) tests/unit/files/content_cache_rendered_haproxy_test_output.txt (+3/-0) tests/unit/files/content_cache_rendered_haproxy_test_output_auto_maxconns.txt (+3/-0) tests/unit/files/content_cache_rendered_haproxy_test_output_load_balancing_algorithm.txt (+3/-0) tests/unit/files/content_cache_rendered_haproxy_test_output_processes_and_threads.txt (+3/-0) tests/unit/files/content_cache_rendered_haproxy_test_output_processes_and_threads_haproxy2.txt (+3/-0) tests/unit/files/content_cache_rendered_haproxy_test_output_rate_limiting.txt (+95/-0) tests/unit/files/content_cache_rendered_haproxy_test_output_rate_limiting_missing_condition.txt (+92/-0) tests/unit/files/haproxy_config_rendered_backends_stanzas_test_output.txt (+3/-0) tests/unit/files/haproxy_config_rendered_test_output.txt (+3/-0) tests/unit/files/haproxy_config_rendered_test_output2.txt (+3/-0) tests/unit/test_content_cache.py (+53/-0) |
||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
James Simpson | Approve | ||
Canonical IS Reviewers | Pending | ||
Review via email: mp+441030@code.launchpad.net |
Commit message
Add support for rate limiting on source IP and more - LP:1918439
Description of the change
To post a comment you must log in.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Change successfully merged at revision 61cc22b969fc555
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/lib/haproxy.py b/lib/haproxy.py |
2 | index d666169..fa5c940 100644 |
3 | --- a/lib/haproxy.py |
4 | +++ b/lib/haproxy.py |
5 | @@ -227,7 +227,7 @@ listen {name} |
6 | backend_stanza = """ |
7 | backend backend-{name} |
8 | {indent}{httpchk} |
9 | -{indent}http-request set-header Host {site_name} |
10 | +{ratelimit}{indent}http-request set-header Host {site_name} |
11 | {options}{indent}balance {load_balancing_algorithm} |
12 | {backends} |
13 | """ |
14 | @@ -314,6 +314,25 @@ backend backend-{name} |
15 | ) |
16 | ) |
17 | |
18 | + ratelimit = '' |
19 | + if loc_conf.get('rate-limit'): |
20 | + loc_rl = loc_conf.get('rate-limit') |
21 | + condition = loc_rl.get('condition') |
22 | + sticky = loc_rl.get('sticky-table') |
23 | + track = loc_rl.get('track') |
24 | + |
25 | + if condition and sticky and track: |
26 | + rlconf = [] |
27 | + # https://www.haproxy.com/blog/four-examples-of-haproxy-rate-limiting/ |
28 | + rlconf.append('{indent}stick-table {sticky}'.format(sticky=sticky, indent=INDENT)) |
29 | + rlconf.append('{indent}http-request track-sc0 {track}'.format(track=track, indent=INDENT)) |
30 | + rlconf.append( |
31 | + '{indent}http-request deny deny_status 429 if {condition}'.format( |
32 | + condition=condition, indent=INDENT |
33 | + ) |
34 | + ) |
35 | + ratelimit = '\n'.join(rlconf + ['']) |
36 | + |
37 | opts = [] |
38 | for option in loc_conf.get('backend-options', []): |
39 | # retry-on only available from HAProxy 2.1. |
40 | @@ -344,6 +363,7 @@ backend backend-{name} |
41 | load_balancing_algorithm=self.load_balancing_algorithm, |
42 | backends='\n'.join(backend_confs), |
43 | options=options, |
44 | + ratelimit=ratelimit, |
45 | indent=INDENT, |
46 | ) |
47 | |
48 | diff --git a/reactive/content_cache.py b/reactive/content_cache.py |
49 | index 4ddeb7a..d811b14 100644 |
50 | --- a/reactive/content_cache.py |
51 | +++ b/reactive/content_cache.py |
52 | @@ -322,6 +322,8 @@ def configure_haproxy(): # NOQA: C901 LP#1825084 |
53 | # the X-F-F header in case it's spoofed. |
54 | new_cached_loc_conf['backend-options'].insert(0, 'http-request set-header X-Forwarded-For %[src]') |
55 | |
56 | + new_cached_loc_conf['rate-limit'] = loc_conf.get('rate-limit', '') |
57 | + |
58 | # No backends |
59 | if not site_conf['locations'][location].get('backends'): |
60 | if not new_conf[cached_site]['locations']: |
61 | diff --git a/tests/unit/files/config_test_config.txt b/tests/unit/files/config_test_config.txt |
62 | index 32ea46c..6c591e0 100644 |
63 | --- a/tests/unit/files/config_test_config.txt |
64 | +++ b/tests/unit/files/config_test_config.txt |
65 | @@ -113,7 +113,7 @@ site8.local: |
66 | cache-valid: ['200 1h', '401 15m'] |
67 | site-name: auth.site8.local |
68 | |
69 | -# Test 9: Custom backend-inter-time |
70 | +# Test 9: Custom backend-inter-time and rate limiting |
71 | site9.local: |
72 | locations: |
73 | /: |
74 | @@ -124,6 +124,11 @@ site9.local: |
75 | cache-validity: ['200 1h', '401 30m'] |
76 | extra-configs: |
77 | - proxy_force_ranges off |
78 | + # https://www.haproxy.com/blog/four-examples-of-haproxy-rate-limiting/ |
79 | + rate-limit: |
80 | + condition: "{ sc_http_req_rate(0) gt 20 }" |
81 | + sticky-table: "type ip size 1m expire 30s store http_req_rate(10s)" |
82 | + track: "src" |
83 | /private/content: |
84 | extra-configs: |
85 | - root /srv/example1.com/content/ |
86 | diff --git a/tests/unit/files/content_cache_rendered_haproxy_test_output.txt b/tests/unit/files/content_cache_rendered_haproxy_test_output.txt |
87 | index f06fc0e..7d7d726 100644 |
88 | --- a/tests/unit/files/content_cache_rendered_haproxy_test_output.txt |
89 | +++ b/tests/unit/files/content_cache_rendered_haproxy_test_output.txt |
90 | @@ -297,6 +297,9 @@ backend backend-site8-local-2 |
91 | |
92 | backend backend-cached-site9-local |
93 | option httpchk GET /_status/content-cache-check HTTP/1.1\r\nHost:\ site9.local\r\nUser-Agent:\ haproxy/httpchk |
94 | + stick-table type ip size 1m expire 30s store http_req_rate(10s) |
95 | + http-request track-sc0 src |
96 | + http-request deny deny_status 429 if { sc_http_req_rate(0) gt 20 } |
97 | http-request set-header Host site9.local |
98 | http-request set-header X-Forwarded-For %[src] |
99 | balance leastconn |
100 | diff --git a/tests/unit/files/content_cache_rendered_haproxy_test_output_auto_maxconns.txt b/tests/unit/files/content_cache_rendered_haproxy_test_output_auto_maxconns.txt |
101 | index 4435e25..93e026a 100644 |
102 | --- a/tests/unit/files/content_cache_rendered_haproxy_test_output_auto_maxconns.txt |
103 | +++ b/tests/unit/files/content_cache_rendered_haproxy_test_output_auto_maxconns.txt |
104 | @@ -297,6 +297,9 @@ backend backend-site8-local-2 |
105 | |
106 | backend backend-cached-site9-local |
107 | option httpchk GET /_status/content-cache-check HTTP/1.1\r\nHost:\ site9.local\r\nUser-Agent:\ haproxy/httpchk |
108 | + stick-table type ip size 1m expire 30s store http_req_rate(10s) |
109 | + http-request track-sc0 src |
110 | + http-request deny deny_status 429 if { sc_http_req_rate(0) gt 20 } |
111 | http-request set-header Host site9.local |
112 | http-request set-header X-Forwarded-For %[src] |
113 | balance leastconn |
114 | diff --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 |
115 | index e3a0956..2cd7279 100644 |
116 | --- a/tests/unit/files/content_cache_rendered_haproxy_test_output_load_balancing_algorithm.txt |
117 | +++ b/tests/unit/files/content_cache_rendered_haproxy_test_output_load_balancing_algorithm.txt |
118 | @@ -297,6 +297,9 @@ backend backend-site8-local-2 |
119 | |
120 | backend backend-cached-site9-local |
121 | option httpchk GET /_status/content-cache-check HTTP/1.1\r\nHost:\ site9.local\r\nUser-Agent:\ haproxy/httpchk |
122 | + stick-table type ip size 1m expire 30s store http_req_rate(10s) |
123 | + http-request track-sc0 src |
124 | + http-request deny deny_status 429 if { sc_http_req_rate(0) gt 20 } |
125 | http-request set-header Host site9.local |
126 | http-request set-header X-Forwarded-For %[src] |
127 | balance roundrobin |
128 | diff --git a/tests/unit/files/content_cache_rendered_haproxy_test_output_processes_and_threads.txt b/tests/unit/files/content_cache_rendered_haproxy_test_output_processes_and_threads.txt |
129 | index fc0c096..450faaa 100644 |
130 | --- a/tests/unit/files/content_cache_rendered_haproxy_test_output_processes_and_threads.txt |
131 | +++ b/tests/unit/files/content_cache_rendered_haproxy_test_output_processes_and_threads.txt |
132 | @@ -298,6 +298,9 @@ backend backend-site8-local-2 |
133 | |
134 | backend backend-cached-site9-local |
135 | option httpchk GET /_status/content-cache-check HTTP/1.1\r\nHost:\ site9.local\r\nUser-Agent:\ haproxy/httpchk |
136 | + stick-table type ip size 1m expire 30s store http_req_rate(10s) |
137 | + http-request track-sc0 src |
138 | + http-request deny deny_status 429 if { sc_http_req_rate(0) gt 20 } |
139 | http-request set-header Host site9.local |
140 | http-request set-header X-Forwarded-For %[src] |
141 | balance leastconn |
142 | diff --git a/tests/unit/files/content_cache_rendered_haproxy_test_output_processes_and_threads_haproxy2.txt b/tests/unit/files/content_cache_rendered_haproxy_test_output_processes_and_threads_haproxy2.txt |
143 | index 1943512..8905d99 100644 |
144 | --- a/tests/unit/files/content_cache_rendered_haproxy_test_output_processes_and_threads_haproxy2.txt |
145 | +++ b/tests/unit/files/content_cache_rendered_haproxy_test_output_processes_and_threads_haproxy2.txt |
146 | @@ -306,6 +306,9 @@ backend backend-site8-local-2 |
147 | |
148 | backend backend-cached-site9-local |
149 | option httpchk GET /_status/content-cache-check HTTP/1.1\r\nHost:\ site9.local\r\nUser-Agent:\ haproxy/httpchk |
150 | + stick-table type ip size 1m expire 30s store http_req_rate(10s) |
151 | + http-request track-sc0 src |
152 | + http-request deny deny_status 429 if { sc_http_req_rate(0) gt 20 } |
153 | http-request set-header Host site9.local |
154 | http-request set-header X-Forwarded-For %[src] |
155 | balance leastconn |
156 | diff --git a/tests/unit/files/content_cache_rendered_haproxy_test_output_rate_limiting.txt b/tests/unit/files/content_cache_rendered_haproxy_test_output_rate_limiting.txt |
157 | new file mode 100644 |
158 | index 0000000..fa3404d |
159 | --- /dev/null |
160 | +++ b/tests/unit/files/content_cache_rendered_haproxy_test_output_rate_limiting.txt |
161 | @@ -0,0 +1,95 @@ |
162 | +global |
163 | + nbthread 4 |
164 | + maxconn 8192 |
165 | + log /dev/log local0 |
166 | + log /dev/log local1 notice |
167 | + chroot /var/lib/haproxy |
168 | + stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners |
169 | + stats timeout 30s |
170 | + server-state-file /run/haproxy/saved-server-state |
171 | + user haproxy |
172 | + group haproxy |
173 | + daemon |
174 | + |
175 | + # LP#1874386: Work around lingering HAProxy processes as per LP:1874386 |
176 | + # and kill them off. |
177 | + hard-stop-after 15m |
178 | + |
179 | + # Default SSL material locations |
180 | + ca-base /etc/ssl/certs |
181 | + crt-base /etc/ssl/private |
182 | + |
183 | + # Default ciphers to use on SSL-enabled listening sockets. |
184 | + # For more information, see ciphers(1SSL). This list is from: |
185 | + # https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/ |
186 | + # An alternative list with additional directives can be obtained from |
187 | + # https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=haproxy |
188 | + ssl-default-bind-ciphers ECDHE+AESGCM:ECDHE+AES256:ECDHE+AES128:!SSLv3:!TLSv1 |
189 | + ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11 |
190 | + # We'll eventually disable DHE (LP#1825321), but for now, bump DH params |
191 | + tune.ssl.default-dh-param 2048 |
192 | + |
193 | + # Increase the SSL/TLS session cache from the default 20k. But |
194 | + # rather than hardcode values, let's just set it to match |
195 | + # global_max_connections (which by default is calculated using |
196 | + # num. of CPU cores and num. of configured sites). Each entry |
197 | + # requires ~200 bytes so on a host with say 32 CPUs, 10 sites, |
198 | + # each with 2000 max conns will only consume around 122 Mbytes |
199 | + # (32 * 10 * 2000 * 200), which is not much. |
200 | + tune.ssl.cachesize 8192 |
201 | + |
202 | +defaults |
203 | + log global |
204 | + maxconn 8192 |
205 | + mode http |
206 | + option dontlognull |
207 | + timeout connect 5s |
208 | + timeout client 50s |
209 | + timeout server 50s |
210 | + errorfile 400 /etc/haproxy/errors/400.http |
211 | + errorfile 403 /etc/haproxy/errors/403.http |
212 | + errorfile 408 /etc/haproxy/errors/408.http |
213 | + errorfile 500 /etc/haproxy/errors/500.http |
214 | + errorfile 502 /etc/haproxy/errors/502.http |
215 | + errorfile 503 /etc/haproxy/errors/503.http |
216 | + errorfile 504 /etc/haproxy/errors/504.http |
217 | + load-server-state-from-file global |
218 | + unique-id-format %{+X}o\ %ci:%cp_%fi:%fp_%Ts_%rt:%pid |
219 | + unique-id-header X-Cache-Request-ID |
220 | + log-format "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r %ID" |
221 | + |
222 | +resolvers dns |
223 | + nameserver dns1 127.0.0.53:53 |
224 | + resolve_retries 3 |
225 | + timeout resolve 3s |
226 | + timeout retry 3s |
227 | + accepted_payload_size 8192 |
228 | + |
229 | +listen stats |
230 | + bind 127.0.0.1:10000 |
231 | + acl allowed_cidr src 127.0.0.0/8 |
232 | + http-request deny unless allowed_cidr |
233 | + |
234 | + mode http |
235 | + stats enable |
236 | + stats uri / |
237 | + stats realm Haproxy\ Statistics |
238 | + stats auth haproxy:biometricsarenotsecret |
239 | + stats refresh 3 |
240 | + |
241 | + |
242 | +listen cached-site1-local |
243 | + bind 0.0.0.0:80 |
244 | + bind :::80 |
245 | + capture request header X-Cache-Request-ID len 60 |
246 | + default_backend backend-cached-site1-local |
247 | + |
248 | +backend backend-cached-site1-local |
249 | + option httpchk GET /_status/content-cache-check HTTP/1.1\r\nHost:\ site1.local\r\nUser-Agent:\ haproxy/httpchk |
250 | + stick-table type ip size 1m expire 30s store http_req_rate(10s) |
251 | + http-request track-sc0 src |
252 | + http-request deny deny_status 429 if { sc_http_req_rate(0) gt 20 } |
253 | + http-request set-header Host site1.local |
254 | + http-request set-header X-Forwarded-For %[src] |
255 | + balance leastconn |
256 | + server server_1 127.0.0.1:6080 check inter 2s rise 2 fall 60 maxconn 200 |
257 | diff --git a/tests/unit/files/content_cache_rendered_haproxy_test_output_rate_limiting_missing_condition.txt b/tests/unit/files/content_cache_rendered_haproxy_test_output_rate_limiting_missing_condition.txt |
258 | new file mode 100644 |
259 | index 0000000..037cd9f |
260 | --- /dev/null |
261 | +++ b/tests/unit/files/content_cache_rendered_haproxy_test_output_rate_limiting_missing_condition.txt |
262 | @@ -0,0 +1,92 @@ |
263 | +global |
264 | + nbthread 4 |
265 | + maxconn 8192 |
266 | + log /dev/log local0 |
267 | + log /dev/log local1 notice |
268 | + chroot /var/lib/haproxy |
269 | + stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners |
270 | + stats timeout 30s |
271 | + server-state-file /run/haproxy/saved-server-state |
272 | + user haproxy |
273 | + group haproxy |
274 | + daemon |
275 | + |
276 | + # LP#1874386: Work around lingering HAProxy processes as per LP:1874386 |
277 | + # and kill them off. |
278 | + hard-stop-after 15m |
279 | + |
280 | + # Default SSL material locations |
281 | + ca-base /etc/ssl/certs |
282 | + crt-base /etc/ssl/private |
283 | + |
284 | + # Default ciphers to use on SSL-enabled listening sockets. |
285 | + # For more information, see ciphers(1SSL). This list is from: |
286 | + # https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/ |
287 | + # An alternative list with additional directives can be obtained from |
288 | + # https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=haproxy |
289 | + ssl-default-bind-ciphers ECDHE+AESGCM:ECDHE+AES256:ECDHE+AES128:!SSLv3:!TLSv1 |
290 | + ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11 |
291 | + # We'll eventually disable DHE (LP#1825321), but for now, bump DH params |
292 | + tune.ssl.default-dh-param 2048 |
293 | + |
294 | + # Increase the SSL/TLS session cache from the default 20k. But |
295 | + # rather than hardcode values, let's just set it to match |
296 | + # global_max_connections (which by default is calculated using |
297 | + # num. of CPU cores and num. of configured sites). Each entry |
298 | + # requires ~200 bytes so on a host with say 32 CPUs, 10 sites, |
299 | + # each with 2000 max conns will only consume around 122 Mbytes |
300 | + # (32 * 10 * 2000 * 200), which is not much. |
301 | + tune.ssl.cachesize 8192 |
302 | + |
303 | +defaults |
304 | + log global |
305 | + maxconn 8192 |
306 | + mode http |
307 | + option dontlognull |
308 | + timeout connect 5s |
309 | + timeout client 50s |
310 | + timeout server 50s |
311 | + errorfile 400 /etc/haproxy/errors/400.http |
312 | + errorfile 403 /etc/haproxy/errors/403.http |
313 | + errorfile 408 /etc/haproxy/errors/408.http |
314 | + errorfile 500 /etc/haproxy/errors/500.http |
315 | + errorfile 502 /etc/haproxy/errors/502.http |
316 | + errorfile 503 /etc/haproxy/errors/503.http |
317 | + errorfile 504 /etc/haproxy/errors/504.http |
318 | + load-server-state-from-file global |
319 | + unique-id-format %{+X}o\ %ci:%cp_%fi:%fp_%Ts_%rt:%pid |
320 | + unique-id-header X-Cache-Request-ID |
321 | + log-format "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r %ID" |
322 | + |
323 | +resolvers dns |
324 | + nameserver dns1 127.0.0.53:53 |
325 | + resolve_retries 3 |
326 | + timeout resolve 3s |
327 | + timeout retry 3s |
328 | + accepted_payload_size 8192 |
329 | + |
330 | +listen stats |
331 | + bind 127.0.0.1:10000 |
332 | + acl allowed_cidr src 127.0.0.0/8 |
333 | + http-request deny unless allowed_cidr |
334 | + |
335 | + mode http |
336 | + stats enable |
337 | + stats uri / |
338 | + stats realm Haproxy\ Statistics |
339 | + stats auth haproxy:biometricsarenotsecret |
340 | + stats refresh 3 |
341 | + |
342 | + |
343 | +listen cached-site1-local |
344 | + bind 0.0.0.0:80 |
345 | + bind :::80 |
346 | + capture request header X-Cache-Request-ID len 60 |
347 | + default_backend backend-cached-site1-local |
348 | + |
349 | +backend backend-cached-site1-local |
350 | + option httpchk GET /_status/content-cache-check HTTP/1.1\r\nHost:\ site1.local\r\nUser-Agent:\ haproxy/httpchk |
351 | + http-request set-header Host site1.local |
352 | + http-request set-header X-Forwarded-For %[src] |
353 | + balance leastconn |
354 | + server server_1 127.0.0.1:6080 check inter 2s rise 2 fall 60 maxconn 200 |
355 | diff --git a/tests/unit/files/haproxy_config_rendered_backends_stanzas_test_output.txt b/tests/unit/files/haproxy_config_rendered_backends_stanzas_test_output.txt |
356 | index bdb8064..565c469 100644 |
357 | --- a/tests/unit/files/haproxy_config_rendered_backends_stanzas_test_output.txt |
358 | +++ b/tests/unit/files/haproxy_config_rendered_backends_stanzas_test_output.txt |
359 | @@ -63,6 +63,9 @@ backend backend-site8-local-2 |
360 | |
361 | backend backend-site9-local |
362 | option httpchk HEAD / HTTP/1.1\r\nHost:\ site9.local\r\nUser-Agent:\ haproxy/httpchk |
363 | + stick-table type ip size 1m expire 30s store http_req_rate(10s) |
364 | + http-request track-sc0 src |
365 | + http-request deny deny_status 429 if { sc_http_req_rate(0) gt 20 } |
366 | http-request set-header Host site9.local |
367 | balance leastconn |
368 | server server_1 127.0.1.15:80 check inter 1m rise 2 fall 5 maxconn 200 |
369 | diff --git a/tests/unit/files/haproxy_config_rendered_test_output.txt b/tests/unit/files/haproxy_config_rendered_test_output.txt |
370 | index 4f3e3a7..5020ee0 100644 |
371 | --- a/tests/unit/files/haproxy_config_rendered_test_output.txt |
372 | +++ b/tests/unit/files/haproxy_config_rendered_test_output.txt |
373 | @@ -172,6 +172,9 @@ backend backend-site8-local-2 |
374 | |
375 | backend backend-site9-local |
376 | option httpchk HEAD / HTTP/1.1\r\nHost:\ site9.local\r\nUser-Agent:\ haproxy/httpchk |
377 | + stick-table type ip size 1m expire 30s store http_req_rate(10s) |
378 | + http-request track-sc0 src |
379 | + http-request deny deny_status 429 if { sc_http_req_rate(0) gt 20 } |
380 | http-request set-header Host site9.local |
381 | balance leastconn |
382 | server server_1 127.0.1.15:80 check inter 1m rise 2 fall 5 maxconn 200 |
383 | diff --git a/tests/unit/files/haproxy_config_rendered_test_output2.txt b/tests/unit/files/haproxy_config_rendered_test_output2.txt |
384 | index 0020879..4071e23 100644 |
385 | --- a/tests/unit/files/haproxy_config_rendered_test_output2.txt |
386 | +++ b/tests/unit/files/haproxy_config_rendered_test_output2.txt |
387 | @@ -172,6 +172,9 @@ backend backend-site8-local-2 |
388 | |
389 | backend backend-site9-local |
390 | option httpchk HEAD / HTTP/1.1\r\nHost:\ site9.local\r\nUser-Agent:\ haproxy/httpchk |
391 | + stick-table type ip size 1m expire 30s store http_req_rate(10s) |
392 | + http-request track-sc0 src |
393 | + http-request deny deny_status 429 if { sc_http_req_rate(0) gt 20 } |
394 | http-request set-header Host site9.local |
395 | balance leastconn |
396 | server server_1 127.0.1.15:80 check inter 1m rise 2 fall 5 maxconn 200 |
397 | diff --git a/tests/unit/test_content_cache.py b/tests/unit/test_content_cache.py |
398 | index f5f32a2..41e098c 100644 |
399 | --- a/tests/unit/test_content_cache.py |
400 | +++ b/tests/unit/test_content_cache.py |
401 | @@ -685,6 +685,59 @@ site1.local: |
402 | @mock.patch('charms.reactive.set_flag') |
403 | @mock.patch('lib.haproxy.HAProxyConf.save_server_state') |
404 | @mock.patch('reactive.content_cache.update_logrotate') |
405 | + def test_configure_haproxy_sites_rate_limiting(self, logrotation, save_s_state, set_flag, opened_ports): |
406 | + config = ''' |
407 | +site1.local: |
408 | + locations: |
409 | + /: |
410 | + rate-limit: |
411 | + condition: "{ sc_http_req_rate(0) gt 20 }" |
412 | + sticky-table: "type ip size 1m expire 30s store http_req_rate(10s)" |
413 | + track: "src" |
414 | +''' |
415 | + self.mock_config.return_value = {'haproxy_hard_stop_after': '15m', 'max_connections': 8192, 'sites': config} |
416 | + with mock.patch('lib.haproxy.HAProxyConf.conf_file', new_callable=mock.PropertyMock) as mock_conf_file: |
417 | + mock_conf_file.return_value = os.path.join(self.tmpdir, 'haproxy.cfg') |
418 | + opened_ports.return_value = ['443/tcp'] |
419 | + content_cache.configure_haproxy() |
420 | + |
421 | + with open( |
422 | + 'tests/unit/files/content_cache_rendered_haproxy_test_output_rate_limiting.txt', 'r', encoding='utf-8' |
423 | + ) as f: |
424 | + want = f.read() |
425 | + with open(os.path.join(self.tmpdir, 'haproxy.cfg'), 'r', encoding='utf-8') as f: |
426 | + got = f.read() |
427 | + self.assertEqual(got, want) |
428 | + |
429 | + # Missing 'condition' |
430 | + config = ''' |
431 | +site1.local: |
432 | + locations: |
433 | + /: |
434 | + rate-limit: |
435 | + sticky-table: "type ip size 1m expire 30s store http_req_rate(10s)" |
436 | +''' |
437 | + self.mock_config.return_value = {'haproxy_hard_stop_after': '15m', 'max_connections': 8192, 'sites': config} |
438 | + with mock.patch('lib.haproxy.HAProxyConf.conf_file', new_callable=mock.PropertyMock) as mock_conf_file: |
439 | + mock_conf_file.return_value = os.path.join(self.tmpdir, 'haproxy.cfg') |
440 | + opened_ports.return_value = ['443/tcp'] |
441 | + content_cache.configure_haproxy() |
442 | + |
443 | + with open( |
444 | + 'tests/unit/files/content_cache_rendered_haproxy_test_output_rate_limiting_missing_condition.txt', |
445 | + 'r', |
446 | + encoding='utf-8', |
447 | + ) as f: |
448 | + want = f.read() |
449 | + with open(os.path.join(self.tmpdir, 'haproxy.cfg'), 'r', encoding='utf-8') as f: |
450 | + got = f.read() |
451 | + self.assertEqual(got, want) |
452 | + |
453 | + @freezegun.freeze_time("2019-03-22", tz_offset=0) |
454 | + @mock.patch('charmhelpers.core.hookenv.opened_ports') |
455 | + @mock.patch('charms.reactive.set_flag') |
456 | + @mock.patch('lib.haproxy.HAProxyConf.save_server_state') |
457 | + @mock.patch('reactive.content_cache.update_logrotate') |
458 | def test_configure_haproxy_processes_and_threads(self, logrotation, save_server_state, set_flag, opened_ports): |
459 | with open('tests/unit/files/config_test_config.txt', 'r', encoding='utf-8') as f: |
460 | ngx_config = f.read() |
This merge proposal is being monitored by mergebot. Change the status to Approved to merge.