Skip to content

Commit

Permalink
Merge pull request #508 from yakatz/feature/allow_collecting_maps
Browse files Browse the repository at this point in the history
Allow specifying mapfile entries to be collected later
  • Loading branch information
petergmurphy authored Jun 13, 2022
2 parents 24049ec + 5175e8e commit f2ab613
Show file tree
Hide file tree
Showing 6 changed files with 354 additions and 25 deletions.
37 changes: 22 additions & 15 deletions manifests/mapfile.pp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
#
# @note
# A map file contains one key + value per line. These key-value pairs are
# specified in the `mappings` array.
#
# specified in the `mappings` array or by additional `haproxy::mapfile::entry`
# definitions.
#
# @param name
# The namevar of the defined resource type is the filename of the map file
Expand Down Expand Up @@ -35,24 +35,31 @@
# multiple HAproxy instances. Default: `[ 'haproxy' ]`
#
define haproxy::mapfile (
Array $mappings = [],
Enum['present', 'absent'] $ensure = 'present',
$owner = 'root',
$group = 'root',
$mode = '0644',
Array $instances = ['haproxy'],
Array[Variant[String, Hash]] $mappings = [],
Enum['present', 'absent'] $ensure = 'present',
$owner = 'root',
$group = 'root',
$mode = '0644',
Array $instances = ['haproxy'],
) {
$mapfile_name = $title

$_instances = flatten($instances)

file { "haproxy_mapfile_${mapfile_name}":
ensure => $ensure,
owner => $owner,
group => $group,
mode => $mode,
$_mapfile_name = "${::haproxy::config_dir}/${mapfile_name}.map"

concat { "haproxy_mapfile_${mapfile_name}":
ensure => $ensure,
owner => $owner,
group => $group,
mode => $mode,
path => $_mapfile_name,
notify => Haproxy::Service[$_instances],
}

concat::fragment { "haproxy_mapfile_${mapfile_name}-top":
target => $_mapfile_name,
content => template('haproxy/haproxy_mapfile.erb'),
path => "${haproxy::config_dir}/${mapfile_name}.map",
notify => Haproxy::Service[$_instances],
order => '00',
}
}
31 changes: 31 additions & 0 deletions manifests/mapfile/entry.pp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# @summary
# Manage an HAProxy map file as documented in
# https://cbonte.github.io/haproxy-dconv/configuration-1.5.html#7.3.1-map
#
# @note
# A map file contains one key + value per line. These key-value pairs are
# specified in the `mappings` array.
#
#
# @param name
# The namevar of the defined resource type is the filename of the map file
# (without any extension), relative to the `haproxy::config_dir` directory.
# A '.map' extension will be added automatically.
#
# @param mappings
# An array of mappings for this map file. Array elements may be Hashes with a
# single key-value pair each (preferably) or simple Strings. Default: `[]`
#
define haproxy::mapfile::entry (
String $mapfile,
Array[Variant[String, Hash]] $mappings = [$title],
Variant[String, Integer] $order = '10',
) {
$_mapfile_name = "${::haproxy::config_dir}/${mapfile}.map"

concat::fragment { "haproxy_mapfile_${mapfile}-${title}":
target => $_mapfile_name,
content => template('haproxy/haproxy_mapfile.erb'),
order => $order,
}
}
242 changes: 242 additions & 0 deletions spec/acceptance/mapfile_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
# frozen_string_literal: true

require 'spec_helper_acceptance'

describe 'create mapfiles' do
describe 'one mapfile' do
let(:pp) do
<<-MANIFEST
include ::haproxy
haproxy::mapfile { 'single-mapfile':
ensure => 'present',
mappings => [
{ 'example.com' => 'bk_com' },
{ 'example.net' => 'bk_net' },
{ 'example.edu' => 'bk_edu' },
],
}
MANIFEST
end

it 'applies the manifest twice with no stderr' do
idempotent_apply(pp)
expect(file('/etc/haproxy/single-mapfile.map')).to be_file
expect(file('/etc/haproxy/single-mapfile.map').content).to match "example.com bk_com\nexample.net bk_net\nexample.edu bk_edu\n"
end
end

describe 'multiple mapfiles' do
let(:pp) do
<<-MANIFEST
include ::haproxy
haproxy::mapfile { 'multiple-mapfiles':
ensure => 'present',
}
haproxy::mapfile::entry { 'example.com bk_com':
mapfile => 'multiple-mapfiles',
}
haproxy::mapfile::entry { 'org':
mappings => ['example.org bk_org'],
mapfile => 'multiple-mapfiles',
order => '05',
}
haproxy::mapfile::entry { 'net':
mappings => ['example.net bk_net'],
mapfile => 'multiple-mapfiles',
}
haproxy::mapfile::entry { 'edu':
mappings => [{'example.edu' => 'bk_edu'}],
mapfile => 'multiple-mapfiles',
}
MANIFEST
end

it 'applies the manifest twice with no stderr' do
idempotent_apply(pp)
expect(file('/etc/haproxy/multiple-mapfiles.map')).to be_file
expect(file('/etc/haproxy/multiple-mapfiles.map').content).to match "example.org bk_org\nexample.edu bk_edu\nexample.com bk_com\nexample.net bk_net\n"
end
end

describe 'check selection of correct backend' do
describe 'single mapfile' do
let(:pp) do
<<-MANIFEST
$error_page_content = @(ERROR_PAGE)
HTTP/1.1 421 Misdirected Request
Cache-Control: no-cache
Connection: close
Content-Type: text/plain
Error Page
|ERROR_PAGE
file { '/tmp/error.html.http':
content => $error_page_content,
}
file_line {'localhost':
path => '/etc/hosts',
line => '127.0.0.1 localhost host2 host3',
match => '^127\.0\.0\.1',
}
include ::haproxy
haproxy::mapfile { 'single-mapfile':
ensure => 'present',
mappings => [
{ 'host2' => 'backend2' },
{ 'host3' => 'backend3' },
],
}
haproxy::frontend { 'test00':
ipaddress => '127.0.0.1',
ports => '5555',
mode => 'http',
options => {
'use_backend' => '%[req.hdr(host),lower,map_dom(/etc/haproxy/single-mapfile.map,backend1)]'
},
}
haproxy::backend { 'backend1':
mode => 'http',
options => [
{
'errorfile' => [
'503 /tmp/error.html.http',
],
},
],
}
haproxy::backend { 'backend2':
defaults => 'http',
collect_exported => false,
options => { 'mode' => 'http' },
}
haproxy::balancermember { 'port 5556':
listening_service => 'backend2',
server_names => 'test00.example.com',
defaults => 'http',
ports => '5556',
}
haproxy::backend { 'backend3':
defaults => 'http',
collect_exported => false,
options => { 'mode' => 'http' },
}
haproxy::balancermember { 'port 5557':
listening_service => 'backend3',
server_names => 'test01.example.com',
defaults => 'http',
ports => '5557',
}
MANIFEST
end

it 'is able to listen with a mapfile' do
retry_on_error_matching do
apply_manifest(pp, catch_failures: true)
end
end

it 'has a complete mapfile' do
expect(file('/etc/haproxy/single-mapfile.map')).to be_file
expect(file('/etc/haproxy/single-mapfile.map').content).to match "host2 backend2\nhost3 backend3\n"
end

it 'selects the correct backend based on host' do
expect(run_shell('curl localhost:5555').stdout.chomp).to match(%r{Error Page})
expect(run_shell('curl host2:5555').stdout.chomp).to match(%r{Response on 5556})
expect(run_shell('curl host3:5555').stdout.chomp).to match(%r{Response on 5557})
end
end

describe 'multiple mapfiles' do
let(:pp) do
<<-MANIFEST
$error_page_content = @(ERROR_PAGE)
HTTP/1.1 421 Misdirected Request
Cache-Control: no-cache
Connection: close
Content-Type: text/plain
Error Page
|ERROR_PAGE
file { '/tmp/error.html.http':
content => $error_page_content,
}
file_line {'localhost':
path => '/etc/hosts',
line => '127.0.0.1 localhost host2 host3',
match => '^127\.0\.0\.1',
}
include ::haproxy
haproxy::mapfile { 'multiple-mapfiles':
ensure => 'present',
}
haproxy::mapfile::entry { 'host2 backend2':
mapfile => 'multiple-mapfiles',
}
haproxy::mapfile::entry { 'host3 backend3':
mapfile => 'multiple-mapfiles',
}
haproxy::frontend { 'test00':
ipaddress => '127.0.0.1',
ports => '5555',
mode => 'http',
options => {
'use_backend' => '%[req.hdr(host),lower,map_dom(/etc/haproxy/single-mapfile.map,backend1)]'
},
}
haproxy::backend { 'backend1':
mode => 'http',
options => [
{
'errorfile' => [
'503 /tmp/error.html.http',
],
},
],
}
haproxy::backend { 'backend2':
defaults => 'http',
collect_exported => false,
options => { 'mode' => 'http' },
}
haproxy::balancermember { 'port 5556':
listening_service => 'backend2',
server_names => 'test00.example.com',
defaults => 'http',
ports => '5556',
}
haproxy::backend { 'backend3':
defaults => 'http',
collect_exported => false,
options => { 'mode' => 'http' },
}
haproxy::balancermember { 'port 5557':
listening_service => 'backend3',
server_names => 'test01.example.com',
defaults => 'http',
ports => '5557',
}
MANIFEST
end

it 'is able to listen with a mapfile' do
retry_on_error_matching do
apply_manifest(pp, catch_failures: true)
end
end

it 'has a complete mapfile' do
expect(file('/etc/haproxy/multiple-mapfiles.map')).to be_file
expect(file('/etc/haproxy/multiple-mapfiles.map').content).to match "host2 backend2\nhost3 backend3\n"
end

it 'selects the correct backend based on host' do
expect(run_shell('curl localhost:5555').stdout.chomp).to match(%r{Error Page})
expect(run_shell('curl host2:5555').stdout.chomp).to match(%r{Response on 5556})
expect(run_shell('curl host3:5555').stdout.chomp).to match(%r{Response on 5557})
end
end
end
end
32 changes: 32 additions & 0 deletions spec/defines/mapfile_entry_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

require 'spec_helper'

describe 'haproxy::mapfile::entry' do
let(:pre_condition) { 'include haproxy' }
let(:title) { 'example.com example-backend' }
let(:facts) do
{
ipaddress: '1.1.1.1',
osfamily: 'Redhat',
concat_basedir: '/dne',
}
end

context 'when map domains to backends' do
let(:params) do
{
mapfile: 'domains-to-backends',
}
end

it { is_expected.to compile.with_all_deps }
it {
is_expected.to contain_concat__fragment('haproxy_mapfile_domains-to-backends-example.com example-backend').with(
'order' => '10',
'target' => '/etc/haproxy/domains-to-backends.map',
'content' => "example.com example-backend\n",
)
}
end
end
Loading

0 comments on commit f2ab613

Please sign in to comment.