haproxy: frontend/backend syntax in config

Ability to generate haproxy config with the frontend/backend
sections rather than the old listen section. This allows
the generation of complex configs, such as for example giving
priority to local backends when routing traffic.

Make the new syntax configurable via a new hiera key
`haproxy_backend_syntax`. The frontend and backend config of
each service can be further tweaked via additional keys
tripleo::haproxy::<service>::frontend_options and
tripleo::haproxy::<service>::backend_options

By default, keep the current 'listen' syntax.

Tested with capability disabled, the haproxy config generated
for undercloud and ha overcloud doesn't change.
Tested with capability enabled, tempest smoke test passed.

Closes-Bug: #1941617

Change-Id: Ieb36f90c6709934aa3aa6668d3929bff872c30f5
This commit is contained in:
Damien Ciabrini 2021-07-16 14:22:39 +00:00
parent 688a80c255
commit 7a6c5281e0
8 changed files with 702 additions and 288 deletions

View File

@ -0,0 +1,30 @@
# This function merges two hashes and concatenate the values of
# identical keys
#
# Example:
# $frontend = { 'option' => [ 'tcpka', 'tcplog' ],
# 'timeout client' => '90m' }
# $backend = { 'option' => [ 'httpchk' ],
# 'timeout server' => '90m' }
#
# Using this function:
# $merge = merge_hash_values($frontend, $backend)
#
# Would return:
# $merge = { 'option' => [ 'tcpka', 'tcplog', 'httpchk' ],
# 'timeout client' => '90m',
# 'timeout server' => '90m' }
#
Puppet::Functions.create_function(:'merge_hash_values') do
dispatch :merge_hash_values do
param 'Hash', :hash1
param 'Hash', :hash2
return_type 'Hash'
end
def merge_hash_values(hash1, hash2)
hh = hash1.merge(hash2) {|k, v1, v2| (v2 + v1).uniq()}
return hh
end
end

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,11 @@
# Options for the balancer member, specified after the server declaration.
# These should go in the member's configuration block.
#
# [*use_backend_syntax*]
# (optional) When set to true, generate a config with frontend and
# backend sections, otherwise use listen sections.
# Defaults to hiera('haproxy_backend_syntax', false)
#
# [*haproxy_port*]
# An alternative port, on which haproxy will listen for incoming requests.
# Defaults to service_port.
@ -65,6 +70,14 @@
# HAproxy terms, the frontend).
# defaults to {'option' => []}
#
# [*frontend_options*]
# Options specified for the frontend service's configuration block
# defaults to {'option' => []}
#
# [*backend_options*]
# Options specified for the service's backend configuration block
# defaults to {'option' => []}
#
# [*public_ssl_port*]
# The port used for the public proxy endpoint if it differs from the default
# one. This is used only if SSL is enabled, and it's used in order to avoid
@ -119,6 +132,7 @@ define tripleo::haproxy::endpoint (
$internal_ip,
$service_port,
$member_options,
$use_backend_syntax = hiera('haproxy_backend_syntax', false),
$haproxy_port = undef,
$base_service_name = undef,
$ip_addresses = hiera("${name}_node_ips", undef),
@ -129,6 +143,12 @@ define tripleo::haproxy::endpoint (
$listen_options = {
'option' => [],
},
$frontend_options = {
'option' => [],
},
$backend_options = {
'option' => [],
},
$public_ssl_port = undef,
$public_certificate = undef,
$use_internal_certificates = false,
@ -160,6 +180,8 @@ define tripleo::haproxy::endpoint (
}
# Let users override the options on a per-service basis
$custom_options = hiera("tripleo::haproxy::${name}::options", undef)
$custom_frontend_options = hiera("tripleo::haproxy::${name}::frontend_options", undef)
$custom_backend_options = hiera("tripleo::haproxy::${name}::backend_options", undef)
$custom_bind_options_public = delete(any2array(hiera("tripleo::haproxy::${name}::public_bind_options", undef)), undef).flatten()
$custom_bind_options_internal = delete(any2array(hiera("tripleo::haproxy::${name}::internal_bind_options", undef)), undef).flatten()
if $public_virtual_ip {
@ -173,13 +195,16 @@ define tripleo::haproxy::endpoint (
'option' => 'forwardfor',
}
$listen_options_precookie = merge($tls_listen_options, $listen_options, $custom_options)
$frontend_options_precookie = merge($tls_listen_options, $frontend_options, $custom_frontend_options)
} else {
$listen_options_precookie = merge($listen_options, $custom_options)
$frontend_options_precookie = merge($frontend_options, $custom_frontend_options)
}
$public_bind_opts = list_to_hash(suffix(any2array($public_virtual_ip), ":${public_ssl_port}"),
union($haproxy_listen_bind_param, ['ssl', 'crt', $public_certificate], $custom_bind_options_public))
} else {
$listen_options_precookie = merge($listen_options, $custom_options)
$frontend_options_precookie = merge($frontend_options, $custom_frontend_options)
$public_bind_opts = list_to_hash(suffix(any2array($public_virtual_ip), ":${haproxy_port_real}"),
union($haproxy_listen_bind_param, $custom_bind_options_public))
}
@ -187,14 +212,17 @@ define tripleo::haproxy::endpoint (
# internal service only
$public_bind_opts = {}
$listen_options_precookie = merge($listen_options, $custom_options)
$frontend_options_precookie = merge($frontend_options, $custom_frontend_options)
}
if $sticky_sessions {
$cookie_options = {
'cookie' => "${session_cookie} insert indirect nocache",
}
$listen_options_real = merge($listen_options_precookie, $cookie_options)
$frontend_options_real = merge($frontend_options_precookie, $cookie_options)
} else {
$listen_options_real = $listen_options_precookie
$frontend_options_real = $frontend_options_precookie
}
if $use_internal_certificates {
if !$service_network {
@ -231,22 +259,45 @@ define tripleo::haproxy::endpoint (
'acl' => "acl Auth${name} http_auth(${authorized_userlist})",
'http-request' => "auth realm ${name} if !Auth${name}",
}
Haproxy::Listen[$name] {
require => Tripleo::Haproxy::Userlist[$authorized_userlist],
if $use_backend_syntax {
Haproxy::Frontend[$name] {
require => Tripleo::Haproxy::Userlist[$authorized_userlist],
}
} else {
Haproxy::Listen[$name] {
require => Tripleo::Haproxy::Userlist[$authorized_userlist],
}
}
} else {
$access_rules = {}
}
$_real_options = merge($listen_options_real, $access_rules)
$_real_frontend_options = merge($frontend_options_real, $access_rules,
{ 'default_backend' => "${name}_be" })
$bind_opts = merge($internal_bind_opts, $public_bind_opts)
haproxy::listen { "${name}":
bind => $bind_opts,
collect_exported => false,
mode => $mode,
options => $_real_options,
if $use_backend_syntax {
haproxy::frontend { "${name}":
bind => $bind_opts,
collect_exported => false,
mode => $mode,
options => $_real_frontend_options,
}
haproxy::backend { "${name}_be":
mode => $mode,
options => merge($backend_options, $custom_backend_options),
}
$listening_service = "${name}_be"
} else {
haproxy::listen { "${name}":
bind => $bind_opts,
collect_exported => false,
mode => $mode,
options => $_real_options,
}
$listening_service = "${name}"
}
if $sticky_sessions {
hash(zip($ip_addresses_real, $server_names_real)).each | $ip, $server | {
@ -254,7 +305,7 @@ define tripleo::haproxy::endpoint (
# which is a reserved character to reference manifests
$non_colon_ip = regsubst($ip, ':', '-', 'G')
haproxy::balancermember { "${name}_${non_colon_ip}_${server}":
listening_service => $name,
listening_service => $listening_service,
ports => $service_port_real,
ipaddresses => $ip,
server_names => $server,
@ -263,7 +314,7 @@ define tripleo::haproxy::endpoint (
}
} else {
haproxy::balancermember { "${name}":
listening_service => $name,
listening_service => $listening_service,
ports => $service_port_real,
ipaddresses => $ip_addresses_real,
server_names => $server_names_real,

View File

@ -36,6 +36,11 @@
# If this service is internal only this should be ommitted.
# Defaults to undef.
#
# [*use_backend_syntax*]
# (optional) When set to true, generate a config with frontend and
# backend sections, otherwise use listen sections.
# Defaults to hiera('haproxy_backend_syntax', false)
#
# [*haproxy_listen_bind_param*]
# A list of params to be added to the HAProxy listener bind directive.
# Defaults to undef.
@ -77,6 +82,7 @@ class tripleo::haproxy::horizon_endpoint (
$server_names,
$member_options,
$public_virtual_ip,
$use_backend_syntax = hiera('haproxy_backend_syntax', false),
$haproxy_listen_bind_param = undef,
$public_certificate = undef,
$use_internal_certificates = false,
@ -86,6 +92,8 @@ class tripleo::haproxy::horizon_endpoint (
) {
# Let users override the options on a per-service basis
$custom_options = hiera('tripleo::haproxy::horizon::options', undef)
$custom_frontend_options = hiera('tripleo::haproxy::horizon::frontend_options', undef)
$custom_backend_options = hiera('tripleo::haproxy::horizon::backend_options', undef)
$custom_bind_options_public = delete(any2array(hiera('tripleo::haproxy::horizon::public_bind_options', undef)), undef).flatten()
$custom_bind_options_internal = delete(any2array(hiera('tripleo::haproxy::horizon::internal_bind_options', undef)), undef).flatten()
# service exposed to the public network
@ -120,26 +128,30 @@ class tripleo::haproxy::horizon_endpoint (
"${public_virtual_ip}:80" => union($haproxy_listen_bind_param, $custom_bind_options_public),
"${public_virtual_ip}:443" => union($haproxy_listen_bind_param, ['ssl', 'crt', $public_certificate], $custom_bind_options_public),
}
$horizon_options = merge({
'cookie' => 'SERVERID insert indirect nocache',
$horizon_frontend_options = {
'rsprep' => '^Location:\ http://(.*) Location:\ https://\1',
# NOTE(jaosorior): We always redirect to https for the public_virtual_ip.
'redirect' => 'scheme https code 301 if !{ ssl_fc }',
'option' => [ 'forwardfor', 'httpchk' ],
'option' => [ 'forwardfor' ],
'http-request' => [
'set-header X-Forwarded-Proto https if { ssl_fc }',
'set-header X-Forwarded-Proto http if !{ ssl_fc }'],
}, $custom_options)
}
} else {
$horizon_bind_opts = {
"${internal_ip}:80" => union($haproxy_listen_bind_param, $custom_bind_options_internal),
"${public_virtual_ip}:80" => union($haproxy_listen_bind_param, $custom_bind_options_public),
}
$horizon_options = merge({
'cookie' => 'SERVERID insert indirect nocache',
'option' => [ 'forwardfor', 'httpchk' ],
}, $custom_options)
$horizon_frontend_options = {
'option' => [ 'forwardfor' ],
}
}
$horizon_backend_options = {
'cookie' => 'SERVERID insert indirect nocache',
'option' => [ 'httpchk' ],
}
$horizon_options = merge_hash_values($horizon_backend_options,
$horizon_frontend_options)
if $use_internal_certificates {
# Use SSL port if TLS in the internal network is enabled.
@ -148,18 +160,33 @@ class tripleo::haproxy::horizon_endpoint (
$backend_port = '80'
}
haproxy::listen { 'horizon':
bind => $horizon_bind_opts,
options => $horizon_options,
mode => 'http',
collect_exported => false,
if $use_backend_syntax {
haproxy::frontend { 'horizon':
bind => $horizon_bind_opts,
options => merge($horizon_frontend_options,
{ default_backend => 'horizon_be' },
$custom_frontend_options),
mode => 'http',
collect_exported => false,
}
haproxy::backend { 'horizon_be':
options => merge($horizon_backend_options, $custom_backend_options),
mode => 'http',
}
} else {
haproxy::listen { 'horizon':
bind => $horizon_bind_opts,
options => merge($horizon_options, $custom_options),
mode => 'http',
collect_exported => false,
}
}
hash(zip($ip_addresses, $server_names)).each | $ip, $server | {
# We need to be sure the IP (IPv6) don't have colons
# which is a reserved character to reference manifests
$non_colon_ip = regsubst($ip, ':', '-', 'G')
haproxy::balancermember { "horizon_${non_colon_ip}_${server}":
listening_service => 'horizon',
listening_service => 'horizon_be',
ports => $backend_port,
ipaddresses => $ip,
server_names => $server,

View File

@ -24,6 +24,11 @@
# IP Address(es) on which the stats interface is listening on.
# Can be a string or a list of ip addresses
#
# [*use_backend_syntax*]
# (optional) When set to true, generate a config with frontend and
# backend sections, otherwise use listen sections.
# Defaults to hiera('haproxy_backend_syntax', false)
#
# [*port*]
# Port on which to listen to for haproxy stats web interface
# Defaults to '1993'
@ -47,6 +52,7 @@
class tripleo::haproxy::stats (
$haproxy_listen_bind_param,
$ip,
$use_backend_syntax = hiera('haproxy_backend_syntax', false),
$port = '1993',
$password = undef,
$certificate = undef,
@ -66,12 +72,29 @@ class tripleo::haproxy::stats (
} else {
$stats_config = $stats_base
}
haproxy::listen { 'haproxy.stats':
bind => $haproxy_stats_bind_opts,
mode => 'http',
options => {
'stats' => $stats_config,
},
collect_exported => false,
if $use_backend_syntax {
haproxy::frontend { 'haproxy.stats':
bind => $haproxy_stats_bind_opts,
mode => 'http',
options => {
'stats' => $stats_config,
},
collect_exported => false,
}
haproxy::backend { 'haproxy.stats_be':
mode => 'http',
options => {
'stats' => $stats_config,
},
}
} else {
haproxy::listen { 'haproxy.stats':
bind => $haproxy_stats_bind_opts,
mode => 'http',
options => {
'stats' => $stats_config,
},
collect_exported => false,
}
}
}

View File

@ -0,0 +1,12 @@
---
features:
- |
Haproxy configuration file can now use the frontend and backend
keywords to describe a service, rather than using the listen
keyword. The new format can be enabled via hiera parameter
`haproxy_backend_syntax`. When enabled, any frontend or backend
configuration can be overriden on a per service-basis via new
hiera keys `tripleo::haproxy::<service>::frontend_options` and
`tripleo::haproxy::<service>::frontend_options`. The original
hiera key `tripleo::haproxy::<service>::options` has no effect
on the frontend and backend sections.

View File

@ -75,6 +75,31 @@ describe 'tripleo::haproxy::endpoint' do
)
end
end
context 'with frontend/backend sections' do
before :each do
params.merge!({
:use_backend_syntax => true,
})
end
it 'should configure haproxy' do
is_expected.to compile.with_all_deps
is_expected.to contain_haproxy__frontend('neutron').with(
:collect_exported => false,
:bind => { "10.0.0.1:9696" => ["transparent"],
"192.168.0.1:9696" => ["transparent"] },
:options => {'option' => [],
'timeout client' => '90m',
'default_backend' => 'neutron_be',
},
)
is_expected.to contain_haproxy__backend('neutron_be').with(
:options => {'option' => [],
'timeout server' => '90m',
},
)
end
end
end
on_supported_os.each do |os, facts|

View File

@ -202,6 +202,10 @@ tripleo::dynamic_stuff::haproxy_endpoints:
tripleo::haproxy::neutron::options:
'timeout client': '90m'
'timeout server': '90m'
tripleo::haproxy::neutron::frontend_options:
'timeout client': '90m'
tripleo::haproxy::neutron::backend_options:
'timeout server': '90m'
tripleo::haproxy_basic_auth::haproxy_endpoints:
starwars: