Fix PING health-monitor with recent haproxy releases
haproxy 2.2.x requires an "insecure-fork-wanted" global option when using external-check. This commit also fixes a function that builds the list of the features available in haproxy based on its version. Story 2009953 Task 44900 Change-Id: I35c5976c6bdb8828e54bcde00052622a7b6bc96a
This commit is contained in:
parent
309c7e61fe
commit
1ac7818128
@ -796,6 +796,7 @@ AMP_NETNS_SVC_PREFIX = 'amphora-netns'
|
|||||||
# Amphora Feature Compatibility
|
# Amphora Feature Compatibility
|
||||||
HTTP_REUSE = 'has_http_reuse'
|
HTTP_REUSE = 'has_http_reuse'
|
||||||
POOL_ALPN = 'has_pool_alpn'
|
POOL_ALPN = 'has_pool_alpn'
|
||||||
|
INSECURE_FORK = 'requires_insecure_fork'
|
||||||
|
|
||||||
# TODO(johnsom) convert these to octavia_lib constants
|
# TODO(johnsom) convert these to octavia_lib constants
|
||||||
# once octavia is transitioned to use octavia_lib
|
# once octavia is transitioned to use octavia_lib
|
||||||
|
@ -17,6 +17,7 @@ import re
|
|||||||
|
|
||||||
import jinja2
|
import jinja2
|
||||||
from octavia_lib.common import constants as lib_consts
|
from octavia_lib.common import constants as lib_consts
|
||||||
|
from oslo_utils import versionutils
|
||||||
|
|
||||||
from octavia.common.config import cfg
|
from octavia.common.config import cfg
|
||||||
from octavia.common import constants
|
from octavia.common import constants
|
||||||
@ -98,13 +99,17 @@ class JinjaTemplater(object):
|
|||||||
# pair might be running an older amphora version.
|
# pair might be running an older amphora version.
|
||||||
|
|
||||||
feature_compatibility = {}
|
feature_compatibility = {}
|
||||||
|
version = ".".join(haproxy_versions)
|
||||||
# Is it newer than haproxy 1.5?
|
# Is it newer than haproxy 1.5?
|
||||||
if not (int(haproxy_versions[0]) < 2 and int(haproxy_versions[1]) < 6):
|
if versionutils.is_compatible("1.6.0", version, same_major=False):
|
||||||
feature_compatibility[constants.HTTP_REUSE] = True
|
feature_compatibility[constants.HTTP_REUSE] = True
|
||||||
if not (int(haproxy_versions[0]) < 2 and int(haproxy_versions[1]) < 9):
|
if versionutils.is_compatible("1.9.0", version, same_major=False):
|
||||||
feature_compatibility[constants.POOL_ALPN] = True
|
feature_compatibility[constants.POOL_ALPN] = True
|
||||||
if int(haproxy_versions[0]) >= 2:
|
if int(haproxy_versions[0]) >= 2:
|
||||||
feature_compatibility[lib_consts.PROTOCOL_PROMETHEUS] = True
|
feature_compatibility[lib_consts.PROTOCOL_PROMETHEUS] = True
|
||||||
|
# haproxy 2.2 requires insecure-fork-wanted for PING healthchecks
|
||||||
|
if versionutils.is_compatible("2.2.0", version, same_major=False):
|
||||||
|
feature_compatibility[constants.INSECURE_FORK] = True
|
||||||
|
|
||||||
return self.render_loadbalancer_obj(
|
return self.render_loadbalancer_obj(
|
||||||
host_amphora, listeners, tls_certs=tls_certs,
|
host_amphora, listeners, tls_certs=tls_certs,
|
||||||
@ -173,6 +178,8 @@ class JinjaTemplater(object):
|
|||||||
if listener.protocol == lib_consts.PROTOCOL_PROMETHEUS:
|
if listener.protocol == lib_consts.PROTOCOL_PROMETHEUS:
|
||||||
prometheus_listener = True
|
prometheus_listener = True
|
||||||
break
|
break
|
||||||
|
require_insecure_fork = feature_compatibility.get(
|
||||||
|
constants.INSECURE_FORK)
|
||||||
enable_prometheus = prometheus_listener and feature_compatibility.get(
|
enable_prometheus = prometheus_listener and feature_compatibility.get(
|
||||||
lib_consts.PROTOCOL_PROMETHEUS, False)
|
lib_consts.PROTOCOL_PROMETHEUS, False)
|
||||||
return self._get_template().render(
|
return self._get_template().render(
|
||||||
@ -185,7 +192,8 @@ class JinjaTemplater(object):
|
|||||||
CONF.amphora_agent.administrative_log_facility,
|
CONF.amphora_agent.administrative_log_facility,
|
||||||
'user_log_facility': CONF.amphora_agent.user_log_facility,
|
'user_log_facility': CONF.amphora_agent.user_log_facility,
|
||||||
'connection_logging': self.connection_logging,
|
'connection_logging': self.connection_logging,
|
||||||
'enable_prometheus': enable_prometheus},
|
'enable_prometheus': enable_prometheus,
|
||||||
|
'require_insecure_fork': require_insecure_fork},
|
||||||
constants=constants, lib_consts=lib_consts)
|
constants=constants, lib_consts=lib_consts)
|
||||||
|
|
||||||
def _transform_loadbalancer(self, host_amphora, loadbalancer, listeners,
|
def _transform_loadbalancer(self, host_amphora, loadbalancer, listeners,
|
||||||
|
@ -32,6 +32,9 @@ global
|
|||||||
found_ns.found == false %}
|
found_ns.found == false %}
|
||||||
{% set found_ns.found = true %}
|
{% set found_ns.found = true %}
|
||||||
external-check
|
external-check
|
||||||
|
{% if require_insecure_fork %}
|
||||||
|
insecure-fork-wanted
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
import copy
|
import copy
|
||||||
import os
|
import os
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
from octavia_lib.common import constants as lib_consts
|
from octavia_lib.common import constants as lib_consts
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
@ -771,6 +772,37 @@ class TestHaproxyCfg(base.TestCase):
|
|||||||
self.assertEqual(sample_configs_combined.sample_base_expected_config(
|
self.assertEqual(sample_configs_combined.sample_base_expected_config(
|
||||||
backend=be, global_opts=go), rendered_obj)
|
backend=be, global_opts=go), rendered_obj)
|
||||||
|
|
||||||
|
def test_render_template_ping_monitor_http_insecure_fork(self):
|
||||||
|
be = ("backend sample_pool_id_1:sample_listener_id_1\n"
|
||||||
|
" 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"
|
||||||
|
" fullconn {maxconn}\n"
|
||||||
|
" option allbackups\n"
|
||||||
|
" timeout connect 5000\n"
|
||||||
|
" timeout server 50000\n"
|
||||||
|
" server sample_member_id_1 10.0.0.99:82 "
|
||||||
|
"weight 13 check inter 30s fall 3 rise 2 "
|
||||||
|
"cookie sample_member_id_1\n"
|
||||||
|
" server sample_member_id_2 10.0.0.98:82 "
|
||||||
|
"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")
|
||||||
|
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})
|
||||||
|
self.assertEqual(sample_configs_combined.sample_base_expected_config(
|
||||||
|
backend=be, global_opts=go), rendered_obj)
|
||||||
|
|
||||||
def test_render_template_no_monitor_https(self):
|
def test_render_template_no_monitor_https(self):
|
||||||
fe = ("frontend sample_listener_id_1\n"
|
fe = ("frontend sample_listener_id_1\n"
|
||||||
" maxconn {maxconn}\n"
|
" maxconn {maxconn}\n"
|
||||||
@ -1758,3 +1790,106 @@ class TestHaproxyCfg(base.TestCase):
|
|||||||
sample_configs_combined.sample_base_expected_config(
|
sample_configs_combined.sample_base_expected_config(
|
||||||
frontend=fe, backend=be),
|
frontend=fe, backend=be),
|
||||||
rendered_obj)
|
rendered_obj)
|
||||||
|
|
||||||
|
@mock.patch("octavia.common.jinja.haproxy.combined_listeners.jinja_cfg."
|
||||||
|
"JinjaTemplater.render_loadbalancer_obj")
|
||||||
|
def test_build_config(self, mock_render_loadbalancer_obj):
|
||||||
|
mock_amp = mock.Mock()
|
||||||
|
mock_listeners = mock.Mock()
|
||||||
|
mock_tls_certs = mock.Mock()
|
||||||
|
mock_socket_path = mock.Mock()
|
||||||
|
|
||||||
|
j_cfg = jinja_cfg.JinjaTemplater()
|
||||||
|
j_cfg.build_config(mock_amp, mock_listeners, mock_tls_certs,
|
||||||
|
haproxy_versions=("0", "7", "0"),
|
||||||
|
socket_path=mock_socket_path)
|
||||||
|
|
||||||
|
expected_fc = {}
|
||||||
|
mock_render_loadbalancer_obj.assert_called_once_with(
|
||||||
|
mock_amp, mock_listeners, tls_certs=mock_tls_certs,
|
||||||
|
socket_path=mock_socket_path,
|
||||||
|
feature_compatibility=expected_fc)
|
||||||
|
|
||||||
|
mock_render_loadbalancer_obj.reset_mock()
|
||||||
|
|
||||||
|
j_cfg.build_config(mock_amp, mock_listeners, mock_tls_certs,
|
||||||
|
haproxy_versions=("1", "6", "0"),
|
||||||
|
socket_path=mock_socket_path)
|
||||||
|
|
||||||
|
expected_fc = {
|
||||||
|
constants.HTTP_REUSE: True
|
||||||
|
}
|
||||||
|
mock_render_loadbalancer_obj.assert_called_once_with(
|
||||||
|
mock_amp, mock_listeners, tls_certs=mock_tls_certs,
|
||||||
|
socket_path=mock_socket_path,
|
||||||
|
feature_compatibility=expected_fc)
|
||||||
|
|
||||||
|
mock_render_loadbalancer_obj.reset_mock()
|
||||||
|
|
||||||
|
j_cfg.build_config(mock_amp, mock_listeners, mock_tls_certs,
|
||||||
|
haproxy_versions=("1", "9", "0"),
|
||||||
|
socket_path=mock_socket_path)
|
||||||
|
|
||||||
|
expected_fc = {
|
||||||
|
constants.HTTP_REUSE: True,
|
||||||
|
constants.POOL_ALPN: True
|
||||||
|
}
|
||||||
|
mock_render_loadbalancer_obj.assert_called_once_with(
|
||||||
|
mock_amp, mock_listeners, tls_certs=mock_tls_certs,
|
||||||
|
socket_path=mock_socket_path,
|
||||||
|
feature_compatibility=expected_fc)
|
||||||
|
|
||||||
|
mock_render_loadbalancer_obj.reset_mock()
|
||||||
|
|
||||||
|
j_cfg.build_config(mock_amp, mock_listeners, mock_tls_certs,
|
||||||
|
haproxy_versions=("2", "1", "1"),
|
||||||
|
socket_path=mock_socket_path)
|
||||||
|
|
||||||
|
expected_fc = {
|
||||||
|
constants.HTTP_REUSE: True,
|
||||||
|
constants.POOL_ALPN: True,
|
||||||
|
lib_consts.PROTOCOL_PROMETHEUS: True
|
||||||
|
}
|
||||||
|
mock_render_loadbalancer_obj.assert_called_once_with(
|
||||||
|
mock_amp, mock_listeners, tls_certs=mock_tls_certs,
|
||||||
|
socket_path=mock_socket_path,
|
||||||
|
feature_compatibility=expected_fc)
|
||||||
|
|
||||||
|
mock_render_loadbalancer_obj.reset_mock()
|
||||||
|
|
||||||
|
j_cfg.build_config(mock_amp, mock_listeners, mock_tls_certs,
|
||||||
|
haproxy_versions=("2", "2", "1"),
|
||||||
|
socket_path=mock_socket_path)
|
||||||
|
|
||||||
|
expected_fc = {
|
||||||
|
constants.HTTP_REUSE: True,
|
||||||
|
constants.POOL_ALPN: True,
|
||||||
|
lib_consts.PROTOCOL_PROMETHEUS: True,
|
||||||
|
constants.INSECURE_FORK: True
|
||||||
|
}
|
||||||
|
mock_render_loadbalancer_obj.assert_called_once_with(
|
||||||
|
mock_amp, mock_listeners, tls_certs=mock_tls_certs,
|
||||||
|
socket_path=mock_socket_path,
|
||||||
|
feature_compatibility=expected_fc)
|
||||||
|
|
||||||
|
mock_render_loadbalancer_obj.reset_mock()
|
||||||
|
|
||||||
|
j_cfg.build_config(mock_amp, mock_listeners, mock_tls_certs,
|
||||||
|
haproxy_versions=("2", "4", "0"),
|
||||||
|
socket_path=mock_socket_path)
|
||||||
|
|
||||||
|
mock_render_loadbalancer_obj.assert_called_once_with(
|
||||||
|
mock_amp, mock_listeners, tls_certs=mock_tls_certs,
|
||||||
|
socket_path=mock_socket_path,
|
||||||
|
feature_compatibility=expected_fc)
|
||||||
|
|
||||||
|
mock_render_loadbalancer_obj.reset_mock()
|
||||||
|
|
||||||
|
j_cfg.build_config(mock_amp, mock_listeners, mock_tls_certs,
|
||||||
|
haproxy_versions=("3", "1", "0"),
|
||||||
|
socket_path=mock_socket_path)
|
||||||
|
|
||||||
|
mock_render_loadbalancer_obj.assert_called_once_with(
|
||||||
|
mock_amp, mock_listeners, tls_certs=mock_tls_certs,
|
||||||
|
socket_path=mock_socket_path,
|
||||||
|
feature_compatibility=expected_fc)
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
Fix PING health-monitors with recent haproxy releases (>=2.2), haproxy now
|
||||||
|
requires an additional "insecure-fork-wanted" option to authorize the
|
||||||
|
Octavia PING healthcheck.
|
Loading…
Reference in New Issue
Block a user