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

Proposed by Haw Loeung
Status: Merged
Approved by: Haw Loeung
Approved revision: d63023e1646c17171c11c8962d77739d858d7105
Merged at revision: 16f2684a0ce7bf2bb03dc31d968e951f18161865
Proposed branch: ~hloeung/content-cache-charm:add-haproxy-v2
Merge into: content-cache-charm:master
Diff against target: 491 lines (+385/-5)
10 files modified
layer.yaml (+1/-0)
lib/haproxy.py (+100/-0)
reactive/content_cache.py (+8/-1)
templates/haproxy_cfg.tmpl (+46/-0)
tests/unit/files/config_test_config.txt (+23/-0)
tests/unit/files/haproxy_config_rendered_backends_stanzas_test_output.txt (+24/-0)
tests/unit/files/haproxy_config_rendered_listen_stanzas_test_output.txt (+12/-0)
tests/unit/files/haproxy_config_rendered_test_output.txt (+75/-0)
tests/unit/test_content_cache.py (+24/-4)
tests/unit/test_haproxy.py (+72/-0)
Reviewer Review Type Date Requested Status
Joel Sing (community) +1 Approve
Haw Loeung Pending
Canonical IS Reviewers Pending
Review via email: mp+364417@code.launchpad.net

This proposal supersedes a proposal from 2019-03-11.

Commit message

Add HAProxy configuration generation and service start/restart

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

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

Revision history for this message
Joel Sing (jsing) wrote : Posted in a previous version of this proposal

Generally looks good - handful of mostly minor things to address.

review: Needs Fixing
Revision history for this message
Haw Loeung (hloeung) : Posted in a previous version of this proposal
Revision history for this message
Haw Loeung (hloeung) wrote : Posted in a previous version of this proposal
Revision history for this message
Haw Loeung (hloeung) : Posted in a previous version of this proposal
review: Needs Resubmitting
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
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 16f2684a0ce7bf2bb03dc31d968e951f18161865

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/layer.yaml b/layer.yaml
index 253b93a..d102da7 100644
--- a/layer.yaml
+++ b/layer.yaml
@@ -9,3 +9,4 @@ options:
9 packages:9 packages:
10 - haproxy10 - haproxy
11 - nginx11 - nginx
12 - python3-jinja2
diff --git a/lib/haproxy.py b/lib/haproxy.py
12new file mode 10064413new file mode 100644
index 0000000..a64f3af
--- /dev/null
+++ b/lib/haproxy.py
@@ -0,0 +1,100 @@
1import os
2
3import jinja2
4
5
6HAPROXY_BASE_PATH = '/etc/haproxy'
7INDENT = ' '*4
8
9
10class HAProxyConf:
11
12 def __init__(self, conf_path=HAPROXY_BASE_PATH):
13 self._conf_path = conf_path
14
15 @property
16 def conf_path(self):
17 return self._conf_path
18
19 @property
20 def conf_file(self):
21 return os.path.join(self._conf_path, 'haproxy.cfg')
22
23 def _generate_stanza_name(self, name):
24 return name.replace('.', '-')[0:32]
25
26 def render_stanza_listen(self, config):
27 listen_stanza = """
28listen {name}
29{indent}bind 0.0.0.0:{port}{tls}
30{indent}default_backend cached-{name}
31"""
32 rendered_output = []
33 for site in config.keys():
34 default_port = 80
35 tls_config = ''
36
37 tls_cert_bundle_path = config[site].get('tls-cert-bundle-path')
38 if tls_cert_bundle_path:
39 default_port = 443
40 tls_config = ' ssl crt {}'.format(tls_cert_bundle_path)
41
42 port = config[site].get('port', default_port)
43
44 output = listen_stanza.format(name=self._generate_stanza_name(site),
45 port=port, tls=tls_config, indent=INDENT)
46 rendered_output.append(output)
47
48 return rendered_output
49
50 def render_stanza_backend(self, config):
51 backend_stanza = """
52backend cached-{name}
53{indent}option httpchk HEAD / HTTP/1.0\\r\\nHost:\\ {site}\\r\\nUser-Agent:\\ haproxy/httpchk
54{indent}http-request set-header Host {site}
55{indent}balance leastconn
56{backends}
57"""
58 rendered_output = []
59 for site in config.keys():
60 tls_config = ''
61 if config[site].get('backend-tls'):
62 tls_config = ' ssl sni str({site}) check-sni {site} verify required ca-file ca-certificates.crt' \
63 .format(site=site)
64 backends = []
65 count = 0
66 for backend in config[site]['backends']:
67 count += 1
68 name = 'server_{}'.format(count)
69 backends.append('{indent}server {name} {backend} check inter 5000 rise 2 fall 5 maxconn 16{tls}'
70 .format(name=name, backend=backend, tls=tls_config, indent=INDENT))
71
72 output = backend_stanza.format(name=self._generate_stanza_name(site),
73 site=site, backends='\n'.join(backends), indent=INDENT)
74
75 rendered_output.append(output)
76
77 return rendered_output
78
79 def render(self, config, num_procs):
80 base = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
81 env = jinja2.Environment(loader=jinja2.FileSystemLoader(base))
82 template = env.get_template('templates/haproxy_cfg.tmpl')
83 return template.render({
84 'listen': self.render_stanza_listen(config),
85 'backend': self.render_stanza_backend(config),
86 'num_procs': num_procs,
87 })
88
89 def write(self, content):
90 # Check if contents changed
91 try:
92 with open(self.conf_file, 'r', encoding='utf-8') as f:
93 current = f.read()
94 except FileNotFoundError:
95 current = ''
96 if content == current:
97 return False
98 with open(self.conf_file, 'w', encoding='utf-8') as f:
99 f.write(content)
100 return True
diff --git a/reactive/content_cache.py b/reactive/content_cache.py
index d620521..78f6933 100644
--- a/reactive/content_cache.py
+++ b/reactive/content_cache.py
@@ -1,9 +1,12 @@
1import multiprocessing
1import yaml2import yaml
23
3from charms import reactive4from charms import reactive
4from charms.layer import status5from charms.layer import status
5from charmhelpers.core import hookenv, host6from charmhelpers.core import hookenv, host
7
6from lib import nginx8from lib import nginx
9from lib import haproxy as HAProxy
710
811
9@reactive.hook('upgrade-charm')12@reactive.hook('upgrade-charm')
@@ -81,6 +84,10 @@ def configure_haproxy():
81 reactive.clear_flag('content_cache.active')84 reactive.clear_flag('content_cache.active')
82 return85 return
8386
84 # TODO: Configure up and start/restart HAProxy87 haproxy = HAProxy.HAProxyConf()
88 num_procs = multiprocessing.cpu_count()
89 conf = yaml.safe_load(config.get('sites'))
90 if haproxy.write(haproxy.render(conf, num_procs)):
91 service_start_or_restart('haproxy')
8592
86 reactive.set_flag('content_cache.haproxy.configured')93 reactive.set_flag('content_cache.haproxy.configured')
diff --git a/templates/haproxy_cfg.tmpl b/templates/haproxy_cfg.tmpl
87new file mode 10064494new file mode 100644
index 0000000..e6d367f
--- /dev/null
+++ b/templates/haproxy_cfg.tmpl
@@ -0,0 +1,46 @@
1global
2 nbproc {{num_procs}}
3 log /dev/log local0
4 log /dev/log local1 notice
5 chroot /var/lib/haproxy
6 stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
7 stats timeout 30s
8 user haproxy
9 group haproxy
10 daemon
11
12 # Default SSL material locations
13 ca-base /etc/ssl/certs
14 crt-base /etc/ssl/private
15
16 # Default ciphers to use on SSL-enabled listening sockets.
17 # For more information, see ciphers(1SSL). This list is from:
18 # https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
19 # An alternative list with additional directives can be obtained from
20 # https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=haproxy
21 ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
22 ssl-default-bind-options no-sslv3
23
24defaults
25 log global
26 mode http
27 option httplog
28 option dontlognull
29 timeout connect 5000
30 timeout client 50000
31 timeout server 50000
32 errorfile 400 /etc/haproxy/errors/400.http
33 errorfile 403 /etc/haproxy/errors/403.http
34 errorfile 408 /etc/haproxy/errors/408.http
35 errorfile 500 /etc/haproxy/errors/500.http
36 errorfile 502 /etc/haproxy/errors/502.http
37 errorfile 503 /etc/haproxy/errors/503.http
38 errorfile 504 /etc/haproxy/errors/504.http
39
40{% for stanza in listen -%}
41{{stanza}}
42{%- endfor -%}
43
44{% for stanza in backend -%}
45{{stanza}}
46{%- endfor -%}
diff --git a/tests/unit/files/config_test_config.txt b/tests/unit/files/config_test_config.txt
0new file mode 10064447new file mode 100644
index 0000000..b27ec91
--- /dev/null
+++ b/tests/unit/files/config_test_config.txt
@@ -0,0 +1,23 @@
1# Test 1: The basic port and backends (HTTP)
2site1.local:
3 port: 80
4 backends:
5 - 127.0.1.10:80
6 - 127.0.1.11:80
7 - 127.0.1.12:80
8
9# Test 2: TLS/SSL as well as backends (HTTPS)
10site2.local:
11 tls-cert-bundle-path: /etc/haproxy/some-bundle.crt
12 backend-tls: True
13 backends:
14 - 127.0.1.10:443
15 - 127.0.1.11:443
16 - 127.0.1.12:443
17
18# Test 3: No port, just backends (HTTP)
19site3.local:
20 backends:
21 - 127.0.1.10:80
22 - 127.0.1.11:80
23 - 127.0.1.12:80
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
0new file mode 10064424new file mode 100644
index 0000000..6b76e4e
--- /dev/null
+++ b/tests/unit/files/haproxy_config_rendered_backends_stanzas_test_output.txt
@@ -0,0 +1,24 @@
1
2backend cached-site1-local
3 option httpchk HEAD / HTTP/1.0\r\nHost:\ site1.local\r\nUser-Agent:\ haproxy/httpchk
4 http-request set-header Host site1.local
5 balance leastconn
6 server server_1 127.0.1.10:80 check inter 5000 rise 2 fall 5 maxconn 16
7 server server_2 127.0.1.11:80 check inter 5000 rise 2 fall 5 maxconn 16
8 server server_3 127.0.1.12:80 check inter 5000 rise 2 fall 5 maxconn 16
9
10backend cached-site2-local
11 option httpchk HEAD / HTTP/1.0\r\nHost:\ site2.local\r\nUser-Agent:\ haproxy/httpchk
12 http-request set-header Host site2.local
13 balance leastconn
14 server server_1 127.0.1.10:443 check inter 5000 rise 2 fall 5 maxconn 16 ssl sni str(site2.local) check-sni site2.local verify required ca-file ca-certificates.crt
15 server server_2 127.0.1.11:443 check inter 5000 rise 2 fall 5 maxconn 16 ssl sni str(site2.local) check-sni site2.local verify required ca-file ca-certificates.crt
16 server server_3 127.0.1.12:443 check inter 5000 rise 2 fall 5 maxconn 16 ssl sni str(site2.local) check-sni site2.local verify required ca-file ca-certificates.crt
17
18backend cached-site3-local
19 option httpchk HEAD / HTTP/1.0\r\nHost:\ site3.local\r\nUser-Agent:\ haproxy/httpchk
20 http-request set-header Host site3.local
21 balance leastconn
22 server server_1 127.0.1.10:80 check inter 5000 rise 2 fall 5 maxconn 16
23 server server_2 127.0.1.11:80 check inter 5000 rise 2 fall 5 maxconn 16
24 server server_3 127.0.1.12:80 check inter 5000 rise 2 fall 5 maxconn 16
diff --git a/tests/unit/files/haproxy_config_rendered_listen_stanzas_test_output.txt b/tests/unit/files/haproxy_config_rendered_listen_stanzas_test_output.txt
0new file mode 10064425new file mode 100644
index 0000000..e60ae6c
--- /dev/null
+++ b/tests/unit/files/haproxy_config_rendered_listen_stanzas_test_output.txt
@@ -0,0 +1,12 @@
1
2listen site1-local
3 bind 0.0.0.0:80
4 default_backend cached-site1-local
5
6listen site2-local
7 bind 0.0.0.0:443 ssl crt /etc/haproxy/some-bundle.crt
8 default_backend cached-site2-local
9
10listen site3-local
11 bind 0.0.0.0:80
12 default_backend cached-site3-local
diff --git a/tests/unit/files/haproxy_config_rendered_test_output.txt b/tests/unit/files/haproxy_config_rendered_test_output.txt
0new file mode 10064413new file mode 100644
index 0000000..71954ac
--- /dev/null
+++ b/tests/unit/files/haproxy_config_rendered_test_output.txt
@@ -0,0 +1,75 @@
1global
2 nbproc 4
3 log /dev/log local0
4 log /dev/log local1 notice
5 chroot /var/lib/haproxy
6 stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
7 stats timeout 30s
8 user haproxy
9 group haproxy
10 daemon
11
12 # Default SSL material locations
13 ca-base /etc/ssl/certs
14 crt-base /etc/ssl/private
15
16 # Default ciphers to use on SSL-enabled listening sockets.
17 # For more information, see ciphers(1SSL). This list is from:
18 # https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
19 # An alternative list with additional directives can be obtained from
20 # https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=haproxy
21 ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
22 ssl-default-bind-options no-sslv3
23
24defaults
25 log global
26 mode http
27 option httplog
28 option dontlognull
29 timeout connect 5000
30 timeout client 50000
31 timeout server 50000
32 errorfile 400 /etc/haproxy/errors/400.http
33 errorfile 403 /etc/haproxy/errors/403.http
34 errorfile 408 /etc/haproxy/errors/408.http
35 errorfile 500 /etc/haproxy/errors/500.http
36 errorfile 502 /etc/haproxy/errors/502.http
37 errorfile 503 /etc/haproxy/errors/503.http
38 errorfile 504 /etc/haproxy/errors/504.http
39
40
41listen site1-local
42 bind 0.0.0.0:80
43 default_backend cached-site1-local
44
45listen site2-local
46 bind 0.0.0.0:443 ssl crt /etc/haproxy/some-bundle.crt
47 default_backend cached-site2-local
48
49listen site3-local
50 bind 0.0.0.0:80
51 default_backend cached-site3-local
52
53backend cached-site1-local
54 option httpchk HEAD / HTTP/1.0\r\nHost:\ site1.local\r\nUser-Agent:\ haproxy/httpchk
55 http-request set-header Host site1.local
56 balance leastconn
57 server server_1 127.0.1.10:80 check inter 5000 rise 2 fall 5 maxconn 16
58 server server_2 127.0.1.11:80 check inter 5000 rise 2 fall 5 maxconn 16
59 server server_3 127.0.1.12:80 check inter 5000 rise 2 fall 5 maxconn 16
60
61backend cached-site2-local
62 option httpchk HEAD / HTTP/1.0\r\nHost:\ site2.local\r\nUser-Agent:\ haproxy/httpchk
63 http-request set-header Host site2.local
64 balance leastconn
65 server server_1 127.0.1.10:443 check inter 5000 rise 2 fall 5 maxconn 16 ssl sni str(site2.local) check-sni site2.local verify required ca-file ca-certificates.crt
66 server server_2 127.0.1.11:443 check inter 5000 rise 2 fall 5 maxconn 16 ssl sni str(site2.local) check-sni site2.local verify required ca-file ca-certificates.crt
67 server server_3 127.0.1.12:443 check inter 5000 rise 2 fall 5 maxconn 16 ssl sni str(site2.local) check-sni site2.local verify required ca-file ca-certificates.crt
68
69backend cached-site3-local
70 option httpchk HEAD / HTTP/1.0\r\nHost:\ site3.local\r\nUser-Agent:\ haproxy/httpchk
71 http-request set-header Host site3.local
72 balance leastconn
73 server server_1 127.0.1.10:80 check inter 5000 rise 2 fall 5 maxconn 16
74 server server_2 127.0.1.11:80 check inter 5000 rise 2 fall 5 maxconn 16
75 server server_3 127.0.1.12:80 check inter 5000 rise 2 fall 5 maxconn 16
diff --git a/tests/unit/test_content_cache.py b/tests/unit/test_content_cache.py
index 2366df8..618e3cd 100644
--- a/tests/unit/test_content_cache.py
+++ b/tests/unit/test_content_cache.py
@@ -5,12 +5,13 @@ import tempfile
5import unittest5import unittest
6from unittest import mock6from unittest import mock
77
8# Add path to where our reactive layer lives and import.
9sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))))
10# We also need to mock up charms.layer so we can run unit tests without having8# We also need to mock up charms.layer so we can run unit tests without having
11# to build the charm and pull in layers such as layer-status.9# to build the charm and pull in layers such as layer-status.
12sys.modules['charms.layer'] = mock.MagicMock()10sys.modules['charms.layer'] = mock.MagicMock()
11
13from charms.layer import status # NOQA: E40212from charms.layer import status # NOQA: E402
13# Add path to where our reactive layer lives and import.
14sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))))
14from reactive import content_cache # NOQA: E40215from reactive import content_cache # NOQA: E402
1516
1617
@@ -41,6 +42,11 @@ class TestCharm(unittest.TestCase):
41 self.addCleanup(patcher.stop)42 self.addCleanup(patcher.stop)
42 self.mock_config.return_value = {}43 self.mock_config.return_value = {}
4344
45 patcher = mock.patch('multiprocessing.cpu_count')
46 self.mock_cpu_count = patcher.start()
47 self.addCleanup(patcher.stop)
48 self.mock_cpu_count.return_value = 4
49
44 @mock.patch('charms.reactive.clear_flag')50 @mock.patch('charms.reactive.clear_flag')
45 def test_hook_upgrade_charm_flags(self, clear_flag):51 def test_hook_upgrade_charm_flags(self, clear_flag):
46 '''Test correct flags set via upgrade-charm hook'''52 '''Test correct flags set via upgrade-charm hook'''
@@ -142,10 +148,24 @@ class TestCharm(unittest.TestCase):
142148
143 @mock.patch('reactive.content_cache.service_start_or_restart')149 @mock.patch('reactive.content_cache.service_start_or_restart')
144 def test_configure_haproxy_sites(self, service_start_or_restart):150 def test_configure_haproxy_sites(self, service_start_or_restart):
145 with open('tests/unit/files/nginx_config_test_config.txt', 'r', encoding='utf-8') as f:151 with open('tests/unit/files/config_test_config.txt', 'r', encoding='utf-8') as f:
146 ngx_config = f.read()152 ngx_config = f.read()
147 self.mock_config.return_value = {'sites': ngx_config}153 self.mock_config.return_value = {'sites': ngx_config}
148 content_cache.configure_haproxy()154
155 with open('tests/unit/files/haproxy_config_rendered_test_output.txt', 'r', encoding='utf-8') as f:
156 expected = f.read()
157 with mock.patch('lib.haproxy.HAProxyConf.conf_file', new_callable=mock.PropertyMock) as mock_conf_file:
158 mock_conf_file.return_value = os.path.join(self.tmpdir, 'haproxy.cfg')
159 content_cache.configure_haproxy()
160 with open(os.path.join(self.tmpdir, 'haproxy.cfg'), 'r', encoding='utf-8') as f:
161 current = f.read()
162 self.assertEqual(expected, current)
163 self.assertFalse(service_start_or_restart.assert_called_with('haproxy'))
164
165 # Again, this time should be no change so no need to restart HAProxy
166 service_start_or_restart.reset_mock()
167 content_cache.configure_haproxy()
168 self.assertFalse(service_start_or_restart.assert_not_called())
149169
150170
151if __name__ == '__main__':171if __name__ == '__main__':
diff --git a/tests/unit/test_haproxy.py b/tests/unit/test_haproxy.py
152new file mode 100644172new file mode 100644
index 0000000..19025a3
--- /dev/null
+++ b/tests/unit/test_haproxy.py
@@ -0,0 +1,72 @@
1import os
2import shutil
3import sys
4import tempfile
5import unittest
6import yaml
7
8sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))))
9from lib import haproxy as HAProxy # NOQA: E402
10
11
12class TestLibHAProxy(unittest.TestCase):
13 def setUp(self):
14 self.maxDiff = None
15 self.tmpdir = tempfile.mkdtemp(prefix='charm-unittests-')
16 self.addCleanup(shutil.rmtree, self.tmpdir)
17 self.charm_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
18 with open('tests/unit/files/config_test_config.txt', 'r', encoding='utf-8') as f:
19 self.site_config = yaml.safe_load(f.read())
20
21 def test_haproxy_config_path(self):
22 conf_path = '/etc/haproxy'
23 haproxy = HAProxy.HAProxyConf()
24 self.assertEqual(haproxy.conf_path, conf_path)
25
26 def test_haproxy_config_file(self):
27 conf_file = '/etc/haproxy/haproxy.cfg'
28 haproxy = HAProxy.HAProxyConf()
29 self.assertEqual(haproxy.conf_file, conf_file)
30
31 def test_haproxy_config_generate_stanza_names(self):
32 haproxy = HAProxy.HAProxyConf(self.tmpdir)
33 self.assertEqual(haproxy._generate_stanza_name('site1'), 'site1')
34 self.assertEqual(haproxy._generate_stanza_name('site1.local'), 'site1-local')
35 self.assertEqual(haproxy._generate_stanza_name('site1-canonical-com-canonical-com'),
36 'site1-canonical-com-canonical-co')
37
38 def test_haproxy_config_rendered_listen_stanzas(self):
39 haproxy = HAProxy.HAProxyConf(self.tmpdir)
40 config = self.site_config
41 with open('tests/unit/files/haproxy_config_rendered_listen_stanzas_test_output.txt', 'r',
42 encoding='utf-8') as f:
43 expected = f.read()
44 self.assertEqual(''.join(haproxy.render_stanza_listen(config)), expected)
45
46 def test_haproxy_config_rendered_backend_stanzas(self):
47 haproxy = HAProxy.HAProxyConf(self.tmpdir)
48 config = self.site_config
49 with open('tests/unit/files/haproxy_config_rendered_backends_stanzas_test_output.txt', 'r',
50 encoding='utf-8') as f:
51 expected = f.read()
52 self.assertEqual(''.join(haproxy.render_stanza_backend(config)), expected)
53
54 def test_haproxy_config_rendered_full_config(self):
55 haproxy = HAProxy.HAProxyConf(self.tmpdir)
56 config = self.site_config
57 num_procs = 4
58 self.assertTrue(haproxy.write(haproxy.render(config, num_procs)))
59 with open(haproxy.conf_file, 'r') as f:
60 new_conf = f.read()
61 with open('tests/unit/files/haproxy_config_rendered_test_output.txt', 'r') as f:
62 expected = f.read()
63 self.assertEqual(new_conf, expected)
64
65 def test_haproxy_config_write(self):
66 haproxy = HAProxy.HAProxyConf(self.tmpdir)
67 with open('tests/unit/files/haproxy_config_rendered_test_output.txt', 'r', encoding='utf-8') as f:
68 conf = f.read()
69 self.assertTrue(haproxy.write(conf))
70 # Write again with same contents, this time it should return 'False'
71 # as there should be no change.
72 self.assertFalse(haproxy.write(conf))

Subscribers

People subscribed via source and target branches