Merge ~ballot/content-cache-charm/+git/content-cache-charm:reuseport into content-cache-charm:master

Proposed by Benjamin Allot
Status: Merged
Approved by: Haw Loeung
Approved revision: cbdb4806871eddfacf96762fe7344ba852470ff1
Merged at revision: f13eeb0394a217e536b01f7d31ef413f55fce5cd
Proposed branch: ~ballot/content-cache-charm/+git/content-cache-charm:reuseport
Merge into: content-cache-charm:master
Diff against target: 238 lines (+112/-1)
7 files modified
config.yaml (+7/-0)
lib/nginx.py (+1/-0)
reactive/content_cache.py (+1/-0)
templates/nginx_cfg.tmpl (+1/-1)
tests/unit/files/nginx_config_rendered_test_output-reuseport.txt (+49/-0)
tests/unit/test_content_cache.py (+51/-0)
tests/unit/test_nginx.py (+2/-0)
Reviewer Review Type Date Requested Status
Joel Sing (community) +1 Approve
Haw Loeung +1 Approve
Review via email: mp+400068@code.launchpad.net

Commit message

Add the reuseport directive in nginx.

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
Haw Loeung (hloeung) wrote :

LGTM

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

LGTM

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

Change successfully merged at revision f13eeb0394a217e536b01f7d31ef413f55fce5cd

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/config.yaml b/config.yaml
index 84f8834..3ce504a 100644
--- a/config.yaml
+++ b/config.yaml
@@ -102,6 +102,13 @@ options:
102 description: >102 description: >
103 A comma-separated list of nagios servicegroups.103 A comma-separated list of nagios servicegroups.
104 If left empty, the nagios_context will be used as the servicegroup104 If left empty, the nagios_context will be used as the servicegroup
105 reuseport:
106 default: false
107 type: boolean
108 description: >
109 Use the SO_REUSEPORT socket option, allowing a better load balance on the nginx workers.
110 Can result in performance drop due to increased latency on heavy workload.
111 See https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/.
105 sites:112 sites:
106 default: ""113 default: ""
107 type: string114 type: string
diff --git a/lib/nginx.py b/lib/nginx.py
index 2b733a3..4a81f75 100644
--- a/lib/nginx.py
+++ b/lib/nginx.py
@@ -162,6 +162,7 @@ class NginxConf:
162 'keys_zone': self._generate_keys_zone(conf['site']),162 'keys_zone': self._generate_keys_zone(conf['site']),
163 'locations': self._process_locations(conf['locations']),163 'locations': self._process_locations(conf['locations']),
164 'port': conf['listen_port'],164 'port': conf['listen_port'],
165 'reuseport': conf['reuseport'],
165 'site': conf['site'],166 'site': conf['site'],
166 'site_name': conf['site_name'],167 'site_name': conf['site_name'],
167 }168 }
diff --git a/reactive/content_cache.py b/reactive/content_cache.py
index d396779..15cf56d 100644
--- a/reactive/content_cache.py
+++ b/reactive/content_cache.py
@@ -175,6 +175,7 @@ def configure_nginx(conf_path=None):
175 conf['cache_max_size'] = config['cache_max_size'] or utils.cache_max_size(config['cache_path'])175 conf['cache_max_size'] = config['cache_max_size'] or utils.cache_max_size(config['cache_path'])
176 conf['cache_path'] = config['cache_path']176 conf['cache_path'] = config['cache_path']
177 conf['listen_address'] = '127.0.0.1'177 conf['listen_address'] = '127.0.0.1'
178 conf['reuseport'] = config['reuseport']
178 changed = False179 changed = False
179 for site, site_conf in sites.items():180 for site, site_conf in sites.items():
180 conf['site'] = site181 conf['site'] = site
diff --git a/templates/nginx_cfg.tmpl b/templates/nginx_cfg.tmpl
index fae0d85..4751a43 100644
--- a/templates/nginx_cfg.tmpl
+++ b/templates/nginx_cfg.tmpl
@@ -2,7 +2,7 @@ proxy_cache_path {{cache_path}}/{{site}} use_temp_path=off levels=1:2 keys_zone=
22
3server {3server {
4 server_name {{site_name}};4 server_name {{site_name}};
5 listen {% if address %}{{address}}:{% endif %}{{port}};5 listen {% if address %}{{address}}:{% endif %}{{port}}{% if reuseport %} reuseport{% endif %};
66
7 port_in_redirect off;7 port_in_redirect off;
8 absolute_redirect off;8 absolute_redirect off;
diff --git a/tests/unit/files/nginx_config_rendered_test_output-reuseport.txt b/tests/unit/files/nginx_config_rendered_test_output-reuseport.txt
9new file mode 1006449new file mode 100644
index 0000000..59b9418
--- /dev/null
+++ b/tests/unit/files/nginx_config_rendered_test_output-reuseport.txt
@@ -0,0 +1,49 @@
1proxy_cache_path /var/lib/nginx/proxy/basic_site use_temp_path=off levels=1:2 keys_zone=e176ab9d1f16-cache:10m inactive=2h max_size=1g;
2
3server {
4 server_name basic_site;
5 listen 127.0.0.1:6080 reuseport;
6
7 port_in_redirect off;
8 absolute_redirect off;
9
10 location / {
11 proxy_pass http://localhost:8080;
12 proxy_set_header Host "basic_site";
13 # Removed the following headers to avoid cache poisoning.
14 proxy_set_header Forwarded "";
15 proxy_set_header X-Forwarded-Host "";
16 proxy_set_header X-Forwarded-Port "";
17 proxy_set_header X-Forwarded-Proto "";
18 proxy_set_header X-Forwarded-Scheme "";
19
20 add_header X-Cache-Status "$upstream_cache_status from mock-content-cache/0";
21 proxy_force_ranges on;
22 proxy_cache e176ab9d1f16-cache;
23 proxy_cache_background_update off;
24 proxy_cache_lock off;
25 proxy_cache_min_uses 1;
26 proxy_cache_revalidate on;
27 proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
28 proxy_cache_valid 200 1d;
29
30 access_by_lua_block {
31 -- Exclude healthchecks from nginx_cache_request_total metric.
32 from, to = ngx.re.find(ngx.var.uri, "^/check/?")
33 if not from then
34 cache_request_total:inc(1, {ngx.var.server_name})
35 end
36 }
37
38 log_by_lua_block {
39 http_request_total:inc(1, {ngx.var.server_name, ngx.var.status})
40 if ngx.var.upstream_cache_status == "HIT" then
41 cache_request_hit_total:inc(1, {ngx.var.server_name})
42 end
43 }
44 }
45
46
47 access_log /var/log/nginx/basic_site-access.log content_cache;
48 error_log /var/log/nginx/basic_site-error.log;
49}
diff --git a/tests/unit/test_content_cache.py b/tests/unit/test_content_cache.py
index 4d2a8e8..b8de692 100644
--- a/tests/unit/test_content_cache.py
+++ b/tests/unit/test_content_cache.py
@@ -7,6 +7,7 @@ from unittest import mock
77
8import freezegun8import freezegun
9import jinja29import jinja2
10import pytest
10import yaml11import yaml
1112
12# We also need to mock up charms.layer so we can run unit tests without having13# We also need to mock up charms.layer so we can run unit tests without having
@@ -214,6 +215,7 @@ class TestCharm(unittest.TestCase):
214 'enable_cache_background_update': True,215 'enable_cache_background_update': True,
215 'enable_cache_lock': True,216 'enable_cache_lock': True,
216 'enable_prometheus_metrics': False,217 'enable_prometheus_metrics': False,
218 'reuseport': False,
217 'sites': ngx_config,219 'sites': ngx_config,
218 'worker_connections': 768,220 'worker_connections': 768,
219 'worker_processes': 0,221 'worker_processes': 0,
@@ -291,6 +293,7 @@ site1.local:
291 'cache_inactive_time': '',293 'cache_inactive_time': '',
292 'cache_max_size': '1g',294 'cache_max_size': '1g',
293 'cache_path': '/var/lib/nginx/proxy',295 'cache_path': '/var/lib/nginx/proxy',
296 'reuseport': False,
294 'sites': config,297 'sites': config,
295 'sites_secrets': secrets,298 'sites_secrets': secrets,
296 'worker_connections': 768,299 'worker_connections': 768,
@@ -340,6 +343,7 @@ site1.local:
340 'cache_inactive_time': '2h',343 'cache_inactive_time': '2h',
341 'cache_max_size': '1g',344 'cache_max_size': '1g',
342 'cache_path': '/var/lib/nginx/proxy',345 'cache_path': '/var/lib/nginx/proxy',
346 'reuseport': False,
343 'sites': config,347 'sites': config,
344 'worker_connections': 768,348 'worker_connections': 768,
345 'worker_processes': 0,349 'worker_processes': 0,
@@ -357,6 +361,7 @@ site1.local:
357 'cache_inactive_time': '2h',361 'cache_inactive_time': '2h',
358 'cache_max_size': '20g',362 'cache_max_size': '20g',
359 'cache_path': '/srv/cache',363 'cache_path': '/srv/cache',
364 'reuseport': False,
360 'sites': config,365 'sites': config,
361 'worker_connections': 768,366 'worker_connections': 768,
362 'worker_processes': 0,367 'worker_processes': 0,
@@ -375,6 +380,7 @@ site1.local:
375 'cache_inactive_time': '2h',380 'cache_inactive_time': '2h',
376 'cache_max_size': '',381 'cache_max_size': '',
377 'cache_path': '/srv/cache',382 'cache_path': '/srv/cache',
383 'reuseport': False,
378 'sites': config,384 'sites': config,
379 'worker_connections': 768,385 'worker_connections': 768,
380 'worker_processes': 0,386 'worker_processes': 0,
@@ -1300,6 +1306,7 @@ site1.local:
1300 'enable_cache_lock': False,1306 'enable_cache_lock': False,
1301 'enable_prometheus_metrics': True,1307 'enable_prometheus_metrics': True,
1302 'metrics_listen_address': '127.0.0.2',1308 'metrics_listen_address': '127.0.0.2',
1309 'reuseport': False,
1303 'sites': ngx_config,1310 'sites': ngx_config,
1304 'worker_connections': 768,1311 'worker_connections': 768,
1305 'worker_processes': 0,1312 'worker_processes': 0,
@@ -1400,3 +1407,47 @@ site1.local:
1400 }1407 }
1401 content_cache.configure_haproxy()1408 content_cache.configure_haproxy()
1402 close_port.assert_not_called()1409 close_port.assert_not_called()
1410
1411 @mock.patch('charms.reactive.set_flag')
1412 @mock.patch('charmhelpers.core.hookenv.close_port')
1413 @mock.patch('charmhelpers.core.hookenv.opened_ports')
1414 @mock.patch('reactive.content_cache.update_logrotate')
1415 def test_configure_nginx_reuseport(self, logrotation, opened_ports, close_port, set_flag):
1416 '''Test configuration of Nginx sites.'''
1417 with open('tests/unit/files/config_test_basic_config.txt', 'r', encoding='utf-8') as f:
1418 ngx_config = f.read()
1419 self.mock_config.return_value = {
1420 'cache_inactive_time': '2h',
1421 'cache_max_size': '1g',
1422 'cache_path': '/var/lib/nginx/proxy',
1423 'enable_cache_background_update': False,
1424 'enable_cache_lock': False,
1425 'enable_prometheus_metrics': True,
1426 'metrics_listen_address': '127.0.0.2',
1427 'reuseport': True,
1428 'sites': ngx_config,
1429 'worker_connections': 768,
1430 'worker_processes': 0,
1431 }
1432
1433 with mock.patch('lib.nginx.NginxConf.sites_path', new_callable=mock.PropertyMock) as mock_site_path:
1434 mock_site_path.return_value = os.path.join(self.tmpdir, 'sites-available')
1435 # conf.d, sites-available, and sites-enabled won't exist in our
1436 # temporary directory.
1437 os.mkdir(os.path.join(self.tmpdir, 'conf.d'))
1438 os.mkdir(os.path.join(self.tmpdir, 'sites-available'))
1439 os.mkdir(os.path.join(self.tmpdir, 'sites-enabled'))
1440 shutil.copyfile('tests/unit/files/nginx.conf', os.path.join(self.tmpdir, 'nginx.conf'))
1441
1442 opened_ports.return_value = ['80/tcp', '{0}/tcp'.format(nginx.METRICS_PORT)]
1443 content_cache.configure_nginx(self.tmpdir)
1444
1445 site = 'basic_site'
1446 test_file = 'tests/unit/files/nginx_config_rendered_test_output-reuseport.txt'
1447 with open(test_file, 'r', encoding='utf-8') as f:
1448 want = f.read()
1449
1450 test_file = os.path.join(self.tmpdir, 'sites-available/{0}.conf'.format(site))
1451 with open(test_file, 'r', encoding='utf-8') as f:
1452 got = f.read()
1453 self.assertEqual(got, want)
diff --git a/tests/unit/test_nginx.py b/tests/unit/test_nginx.py
index b0abde9..2734123 100644
--- a/tests/unit/test_nginx.py
+++ b/tests/unit/test_nginx.py
@@ -53,6 +53,7 @@ class TestLibNginx(unittest.TestCase):
53 conf['enable_prometheus_metrics'] = False53 conf['enable_prometheus_metrics'] = False
54 conf['cache_path'] = '/var/lib/nginx/proxy'54 conf['cache_path'] = '/var/lib/nginx/proxy'
55 conf['listen_address'] = '127.0.0.1'55 conf['listen_address'] = '127.0.0.1'
56 conf['reuseport'] = False
56 # From the given YAML-formatted list of sites, check that each individual57 # From the given YAML-formatted list of sites, check that each individual
57 # Nginx config rendered matches what's in tests/unit/files.58 # Nginx config rendered matches what's in tests/unit/files.
58 port = BASE_LISTEN_PORT - 159 port = BASE_LISTEN_PORT - 1
@@ -139,6 +140,7 @@ class TestLibNginx(unittest.TestCase):
139 'cache_path': '/var/lib/nginx/proxy',140 'cache_path': '/var/lib/nginx/proxy',
140 'enable_prometheus_metrics': True,141 'enable_prometheus_metrics': True,
141 'listen_address': '127.0.0.1',142 'listen_address': '127.0.0.1',
143 'reuseport': False,
142 }144 }
143 for site, site_conf in sites.items():145 for site, site_conf in sites.items():
144 conf['site'] = site146 conf['site'] = site

Subscribers

People subscribed via source and target branches