Handle feature compatibility of HAProxy server-state-file option

https://review.opendev.org/c/openstack/octavia/+/805955 introduced
a new feature that requires HAProxy 1.6 or higher to work. This change
ensures that with older HAProxy version this feature does not get
enabled to avoid errors.

Story: 2010254
Task: 46145
Change-Id: Icc7dc9bb22187f908852c20415227574ded347aa
This commit is contained in:
Tom Weininger 2022-08-25 12:47:38 +02:00 committed by Gregory Thiemonge
parent 6ec76b1282
commit 69ecdb1908
9 changed files with 43 additions and 53 deletions

View File

@ -804,6 +804,7 @@ AMP_NETNS_SVC_PREFIX = 'amphora-netns'
# Amphora Feature Compatibility
HTTP_REUSE = 'has_http_reuse'
SERVER_STATE_FILE = 'has_server_state_file'
POOL_ALPN = 'has_pool_alpn'
INSECURE_FORK = 'requires_insecure_fork'

View File

@ -103,6 +103,7 @@ class JinjaTemplater(object):
# Is it newer than haproxy 1.5?
if versionutils.is_compatible("1.6.0", version, same_major=False):
feature_compatibility[constants.HTTP_REUSE] = True
feature_compatibility[constants.SERVER_STATE_FILE] = True
if versionutils.is_compatible("1.9.0", version, same_major=False):
feature_compatibility[constants.POOL_ALPN] = True
if int(haproxy_versions[0]) >= 2:
@ -172,7 +173,8 @@ class JinjaTemplater(object):
listeners[0].load_balancer.id)
state_file_path = '%s/%s/servers-state' % (
self.base_amp_path,
listeners[0].load_balancer.id)
listeners[0].load_balancer.id) if feature_compatibility.get(
constants.SERVER_STATE_FILE) else ''
prometheus_listener = False
for listener in listeners:
if listener.protocol == lib_consts.PROTOCOL_PROMETHEUS:

View File

@ -20,7 +20,9 @@ global
log {{ log_http | default('/run/rsyslog/octavia/log', true)}} local{{ user_log_facility }}
log {{ log_server | default('/run/rsyslog/octavia/log', true)}} local{{ administrative_log_facility }} notice
stats socket {{ sock_path }} mode 0666 level user
{% if state_file %}
server-state-file {{ state_file }}
{% endif %}
{% if loadbalancer.global_connection_limit is defined %}
maxconn {{ loadbalancer.global_connection_limit }}
{% endif %}

View File

@ -55,7 +55,7 @@ backend prometheus-exporter-internal
{{- frontend_macro(constants, lib_consts, listener, loadbalancer.vip_address,
loadbalancer.additional_vips) }}
{% for pool in listener.pools if pool.enabled %}
{{- backend_macro(constants, lib_consts, listener, pool, loadbalancer) }}
{{- backend_macro(constants, lib_consts, listener, pool, loadbalancer, state_file) }}
{% endfor %}
{% endfor %}
{% endif %}

View File

@ -305,7 +305,7 @@ frontend {{ listener.id }}
{% endmacro %}
{% macro backend_macro(constants, lib_consts, listener, pool, loadbalancer) %}
{% macro backend_macro(constants, lib_consts, listener, pool, loadbalancer, state_file) %}
backend {{ pool.id }}:{{ listener.id }}
{% if pool.proxy_protocol is not none %}
mode {{ listener.protocol_mode }}
@ -347,7 +347,9 @@ backend {{ pool.id }}:{{ listener.id }}
{% endif %}
{% endif %}
{% if pool.health_monitor and pool.health_monitor.enabled %}
{% if state_file %}
load-server-state-from-file global
{% endif %}
timeout check {{ pool.health_monitor.timeout }}s
{% if (pool.health_monitor.type ==
constants.HEALTH_MONITOR_HTTP or pool.health_monitor.type ==

View File

@ -31,8 +31,6 @@ class HAProxyCompatTestCase(base.TestCase):
" log /run/rsyslog/octavia/log local1 notice\n"
" stats socket /var/lib/octavia/sample_loadbalancer_id_1.sock"
" mode 0666 level user\n"
" server-state-file /var/lib/octavia/sample_loadbalancer_id_1"
"/servers-state\n"
" maxconn {maxconn}\n\n"
"defaults\n"
" log global\n"

View File

@ -66,7 +66,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -121,7 +120,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -172,7 +170,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -228,7 +225,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -282,7 +278,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -336,7 +331,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -388,7 +382,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -424,7 +417,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -518,7 +510,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -555,7 +546,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -591,7 +581,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -620,7 +609,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -661,7 +649,6 @@ class TestHaproxyCfg(base.TestCase):
" mode tcp\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -698,7 +685,6 @@ class TestHaproxyCfg(base.TestCase):
" mode tcp\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option ssl-hello-chk\n"
" fullconn {maxconn}\n"
@ -766,7 +752,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option external-check\n"
" external-check command /var/lib/octavia/ping-wrapper.sh\n"
@ -810,14 +795,18 @@ class TestHaproxyCfg(base.TestCase):
"weight 13 check inter 30s fall 3 rise 2 "
"cookie sample_member_id_2\n\n").format(
maxconn=constants.HAPROXY_DEFAULT_MAXCONN)
go = (f" maxconn {constants.HAPROXY_DEFAULT_MAXCONN}\n"
" external-check\n insecure-fork-wanted\n\n")
go = (
" server-state-file /var/lib/octavia/sample_loadbalancer_id_1/"
"servers-state\n"
f" maxconn {constants.HAPROXY_DEFAULT_MAXCONN}\n"
" external-check\n insecure-fork-wanted\n\n")
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs_combined.sample_amphora_tuple(),
[sample_configs_combined.sample_listener_tuple(
proto='HTTP', monitor_proto='PING')],
feature_compatibility={
"requires_insecure_fork": True})
"requires_insecure_fork": True,
constants.SERVER_STATE_FILE: True})
self.assertEqual(sample_configs_combined.sample_base_expected_config(
backend=be, global_opts=go), rendered_obj)
@ -858,7 +847,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.1\\r\\nHost:\\ "
"testlab.com\n"
@ -936,7 +924,6 @@ class TestHaproxyCfg(base.TestCase):
" balance roundrobin\n"
" stick-table type ip size 10k\n"
" stick on src\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -964,7 +951,6 @@ class TestHaproxyCfg(base.TestCase):
" stick-table type string len 64 size 10k\n"
" stick store-response res.cook(JSESSIONID)\n"
" stick match req.cook(JSESSIONID)\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -1088,7 +1074,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -1105,7 +1090,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /healthmon.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -1127,7 +1111,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -1156,7 +1139,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -1187,7 +1169,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" fullconn {maxconn}\n"
" option allbackups\n"
@ -1215,7 +1196,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -1256,7 +1236,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -1296,7 +1275,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -1337,7 +1315,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -1375,7 +1352,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -1409,7 +1385,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -1440,7 +1415,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -1652,7 +1626,7 @@ class TestHaproxyCfg(base.TestCase):
defaults=defaults, logging="\n"),
rendered_obj)
def test_http_reuse(self):
def test_haproxy_cfg_1_8_vs_1_5(self):
j_cfg = jinja_cfg.JinjaTemplater(
base_amp_path='/var/lib/octavia',
base_crt_dir='/var/lib/octavia/certs')
@ -1660,7 +1634,12 @@ class TestHaproxyCfg(base.TestCase):
sample_amphora = sample_configs_combined.sample_amphora_tuple()
sample_proxy_listener = sample_configs_combined.sample_listener_tuple(
be_proto='PROXY')
# With http-reuse
# With http-reuse and server-state-file
go = (
" server-state-file /var/lib/octavia/sample_loadbalancer_id_1/"
"servers-state\n"
" maxconn {maxconn}\n\n").format(
maxconn=constants.HAPROXY_DEFAULT_MAXCONN)
be = ("backend {pool_id}:{listener_id}\n"
" mode http\n"
" http-reuse safe\n"
@ -1687,15 +1666,15 @@ class TestHaproxyCfg(base.TestCase):
tls_certs=None,
haproxy_versions=("1", "8", "1"))
self.assertEqual(
sample_configs_combined.sample_base_expected_config(backend=be),
sample_configs_combined.sample_base_expected_config(
global_opts=go, backend=be),
rendered_obj)
# Without http-reuse
# Without http-reuse and server-state-file
be = ("backend {pool_id}:{listener_id}\n"
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" fullconn {maxconn}\n"
" option allbackups\n"
@ -1769,7 +1748,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -1785,7 +1763,6 @@ class TestHaproxyCfg(base.TestCase):
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /healthmon.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -1835,7 +1812,8 @@ class TestHaproxyCfg(base.TestCase):
socket_path=mock_socket_path)
expected_fc = {
constants.HTTP_REUSE: True
constants.HTTP_REUSE: True,
constants.SERVER_STATE_FILE: True
}
mock_render_loadbalancer_obj.assert_called_once_with(
mock_amp, mock_listeners, tls_certs=mock_tls_certs,
@ -1850,7 +1828,8 @@ class TestHaproxyCfg(base.TestCase):
expected_fc = {
constants.HTTP_REUSE: True,
constants.POOL_ALPN: True
constants.POOL_ALPN: True,
constants.SERVER_STATE_FILE: True
}
mock_render_loadbalancer_obj.assert_called_once_with(
mock_amp, mock_listeners, tls_certs=mock_tls_certs,
@ -1866,7 +1845,8 @@ class TestHaproxyCfg(base.TestCase):
expected_fc = {
constants.HTTP_REUSE: True,
constants.POOL_ALPN: True,
lib_consts.PROTOCOL_PROMETHEUS: True
lib_consts.PROTOCOL_PROMETHEUS: True,
constants.SERVER_STATE_FILE: True
}
mock_render_loadbalancer_obj.assert_called_once_with(
mock_amp, mock_listeners, tls_certs=mock_tls_certs,
@ -1883,7 +1863,8 @@ class TestHaproxyCfg(base.TestCase):
constants.HTTP_REUSE: True,
constants.POOL_ALPN: True,
lib_consts.PROTOCOL_PROMETHEUS: True,
constants.INSECURE_FORK: True
constants.INSECURE_FORK: True,
constants.SERVER_STATE_FILE: True
}
mock_render_loadbalancer_obj.assert_called_once_with(
mock_amp, mock_listeners, tls_certs=mock_tls_certs,

View File

@ -1236,7 +1236,6 @@ def sample_base_expected_config(frontend=None, logging=None, backend=None,
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" load-server-state-from-file global\n"
" timeout check 31s\n"
" option httpchk GET /index.html HTTP/1.0\\r\\n\n"
" http-check expect rstatus 418\n"
@ -1270,7 +1269,5 @@ def sample_base_expected_config(frontend=None, logging=None, backend=None,
" log /run/rsyslog/octavia/log local0\n"
" log /run/rsyslog/octavia/log local1 notice\n"
" stats socket /var/lib/octavia/sample_loadbalancer_id_1.sock"
" mode 0666 level user\n"
" server-state-file /var/lib/octavia/sample_loadbalancer_id_1"
"/servers-state\n" +
" mode 0666 level user\n" +
global_opts + defaults + peers + frontend + logging + backend)

View File

@ -0,0 +1,7 @@
---
fixes:
- |
Fixed backwards compatibility issue with the feature that preserves HAProxy
server states between reloads.
HAProxy version 1.5 or below do not support this feature, so Octavia
will not to activate it on amphorae with those versions.