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

Proposed by Haw Loeung
Status: Merged
Approved by: Haw Loeung
Approved revision: 008d8f07f3131b418c4f868bb700f114292bfd40
Merged at revision: 941f22cb2ada546a8868e956002744c1fc1f9414
Proposed branch: ~hloeung/content-cache-charm:haproxy-config
Merge into: content-cache-charm:master
Diff against target: 120 lines (+70/-3)
4 files modified
lib/haproxy.py (+9/-3)
lib/utils.py (+28/-0)
tests/unit/files/limits.txt (+17/-0)
tests/unit/test_utils.py (+16/-0)
Reviewer Review Type Date Requested Status
Stuart Bishop (community) Approve
Canonical IS Reviewers Pending
Review via email: mp+381090@code.launchpad.net

Commit message

Set maxconns ceiling of system's init/PID 1's max. open files.

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
Stuart Bishop (stub) wrote :

Looks fine, apart from tests that seem to rely on systemd defaults not being changed. Comments inline. Probably doesn't require rereview, but ping if it does.

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

Change successfully merged at revision 941f22cb2ada546a8868e956002744c1fc1f9414

Revision history for this message
Joel Sing (jsing) :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/lib/haproxy.py b/lib/haproxy.py
index b8d8727..2d378e9 100644
--- a/lib/haproxy.py
+++ b/lib/haproxy.py
@@ -244,23 +244,29 @@ backend backend-{name}
244 def render(self, config, num_threads=None, monitoring_password=None, tls_cipher_suites=None):244 def render(self, config, num_threads=None, monitoring_password=None, tls_cipher_suites=None):
245 if not num_threads:245 if not num_threads:
246 num_threads = multiprocessing.cpu_count()246 num_threads = multiprocessing.cpu_count()
247
248 listen_stanzas = self.render_stanza_listen(config)
249
247 if self.max_connections:250 if self.max_connections:
248 max_connections = self.max_connections251 max_connections = self.max_connections
249 else:252 else:
250 max_connections = num_threads * 2000253 max_connections = num_threads * 2000
254 global_max_connections = max_connections * len(listen_stanzas)
255 init_maxfds = utils.process_rlimits(1, 'NOFILE')
256 if init_maxfds != 'unlimited' and (global_max_connections * 2) > int(init_maxfds):
257 global_max_connections = int(init_maxfds) // 2
258
251 if not tls_cipher_suites:259 if not tls_cipher_suites:
252 tls_cipher_suites = TLS_CIPHER_SUITES260 tls_cipher_suites = TLS_CIPHER_SUITES
253 tls_cipher_suites = utils.tls_cipher_suites(tls_cipher_suites)261 tls_cipher_suites = utils.tls_cipher_suites(tls_cipher_suites)
254262
255 listen_stanzas = self.render_stanza_listen(config)
256
257 base = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))263 base = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
258 env = jinja2.Environment(loader=jinja2.FileSystemLoader(base))264 env = jinja2.Environment(loader=jinja2.FileSystemLoader(base))
259 template = env.get_template('templates/haproxy_cfg.tmpl')265 template = env.get_template('templates/haproxy_cfg.tmpl')
260 return template.render(266 return template.render(
261 {267 {
262 'backend': self.render_stanza_backend(config),268 'backend': self.render_stanza_backend(config),
263 'global_max_connections': max_connections * len(listen_stanzas),269 'global_max_connections': global_max_connections,
264 'listen': listen_stanzas,270 'listen': listen_stanzas,
265 'max_connections': max_connections,271 'max_connections': max_connections,
266 'monitoring_password': monitoring_password or self.monitoring_password,272 'monitoring_password': monitoring_password or self.monitoring_password,
diff --git a/lib/utils.py b/lib/utils.py
index a075d18..577d9e9 100644
--- a/lib/utils.py
+++ b/lib/utils.py
@@ -168,3 +168,31 @@ def logrotate(path, retention=30, dateext=True):
168 new.append(line)168 new.append(line)
169169
170 return '\n'.join(new)170 return '\n'.join(new)
171
172
173LIMITS_MATCH = {
174 'NOFILE': 'Max open files',
175}
176
177
178def process_rlimits(pid, res, limits_file=None):
179 if limits_file:
180 limitsf = limits_file
181 else:
182 limitsf = os.path.join('/proc', str(pid), 'limits')
183
184 if not os.path.exists(limitsf):
185 return None
186
187 with open(limitsf, 'r', encoding='utf-8') as f:
188 limits = f.read()
189
190 if res not in LIMITS_MATCH:
191 return None
192
193 for line in limits.split('\n'):
194 m = re.match(r'^{}\s+\S+\s+(\S+)'.format(LIMITS_MATCH[res]), line)
195 if m:
196 return m.group(1)
197
198 return None
diff --git a/tests/unit/files/limits.txt b/tests/unit/files/limits.txt
171new file mode 100644199new file mode 100644
index 0000000..679cc39
--- /dev/null
+++ b/tests/unit/files/limits.txt
@@ -0,0 +1,17 @@
1Limit Soft Limit Hard Limit Units
2Max cpu time unlimited unlimited seconds
3Max file size unlimited unlimited bytes
4Max data size unlimited unlimited bytes
5Max stack size 8388608 unlimited bytes
6Max core file size 0 unlimited bytes
7Max resident set unlimited unlimited bytes
8Max processes 31268 31268 processes
9Max open files 1048576 1048576 files
10Max locked memory 67108864 67108864 bytes
11Max address space unlimited unlimited bytes
12Max file locks unlimited unlimited locks
13Max pending signals 31268 31268 signals
14Max msgqueue size 819200 819200 bytes
15Max nice priority 0 0
16Max realtime priority 0 0
17Max realtime timeout unlimited unlimited us
diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py
index 301aafc..d4d4045 100644
--- a/tests/unit/test_utils.py
+++ b/tests/unit/test_utils.py
@@ -169,3 +169,19 @@ class TestLibUtils(unittest.TestCase):
169169
170 # Test when config file doesn't exist.170 # Test when config file doesn't exist.
171 self.assertEqual(utils.logrotate(retention=14, path='tests/unit/files/some-file-that-doesnt-exist'), None)171 self.assertEqual(utils.logrotate(retention=14, path='tests/unit/files/some-file-that-doesnt-exist'), None)
172
173 def test_process_rlimits(self):
174 # Read current Max open files from PID 1 and make sure
175 # utils.process_rlimits() returns the same.
176 with open('/proc/1/limits') as f:
177 for line in f:
178 if line.startswith('Max open files'):
179 limit = str(line.split()[4])
180 self.assertEqual(limit, utils.process_rlimits(1, 'NOFILE'))
181 self.assertEqual(limit, utils.process_rlimits(1, 'NOFILE', None))
182 self.assertEqual(limit, utils.process_rlimits(1, 'NOFILE', 'tests/unit/files/limits.txt'))
183
184 self.assertEqual(None, utils.process_rlimits(1, 'NOFILE', 'tests/unit/files/test_file.txt'))
185 self.assertEqual(None, utils.process_rlimits(1, 'NOFILE', 'tests/unit/files/limits-file-does-not-exist.txt'))
186
187 self.assertEqual(None, utils.process_rlimits(1, 'NOMATCH'))

Subscribers

People subscribed via source and target branches