Skip to content

Commit f2ab613

Browse files
authored
Merge pull request #508 from yakatz/feature/allow_collecting_maps
Allow specifying mapfile entries to be collected later
2 parents 24049ec + 5175e8e commit f2ab613

File tree

6 files changed

+354
-25
lines changed

6 files changed

+354
-25
lines changed

manifests/mapfile.pp

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
#
55
# @note
66
# A map file contains one key + value per line. These key-value pairs are
7-
# specified in the `mappings` array.
8-
#
7+
# specified in the `mappings` array or by additional `haproxy::mapfile::entry`
8+
# definitions.
99
#
1010
# @param name
1111
# The namevar of the defined resource type is the filename of the map file
@@ -35,24 +35,31 @@
3535
# multiple HAproxy instances. Default: `[ 'haproxy' ]`
3636
#
3737
define haproxy::mapfile (
38-
Array $mappings = [],
39-
Enum['present', 'absent'] $ensure = 'present',
40-
$owner = 'root',
41-
$group = 'root',
42-
$mode = '0644',
43-
Array $instances = ['haproxy'],
38+
Array[Variant[String, Hash]] $mappings = [],
39+
Enum['present', 'absent'] $ensure = 'present',
40+
$owner = 'root',
41+
$group = 'root',
42+
$mode = '0644',
43+
Array $instances = ['haproxy'],
4444
) {
4545
$mapfile_name = $title
4646

4747
$_instances = flatten($instances)
4848

49-
file { "haproxy_mapfile_${mapfile_name}":
50-
ensure => $ensure,
51-
owner => $owner,
52-
group => $group,
53-
mode => $mode,
49+
$_mapfile_name = "${::haproxy::config_dir}/${mapfile_name}.map"
50+
51+
concat { "haproxy_mapfile_${mapfile_name}":
52+
ensure => $ensure,
53+
owner => $owner,
54+
group => $group,
55+
mode => $mode,
56+
path => $_mapfile_name,
57+
notify => Haproxy::Service[$_instances],
58+
}
59+
60+
concat::fragment { "haproxy_mapfile_${mapfile_name}-top":
61+
target => $_mapfile_name,
5462
content => template('haproxy/haproxy_mapfile.erb'),
55-
path => "${haproxy::config_dir}/${mapfile_name}.map",
56-
notify => Haproxy::Service[$_instances],
63+
order => '00',
5764
}
5865
}

manifests/mapfile/entry.pp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# @summary
2+
# Manage an HAProxy map file as documented in
3+
# https://cbonte.github.io/haproxy-dconv/configuration-1.5.html#7.3.1-map
4+
#
5+
# @note
6+
# A map file contains one key + value per line. These key-value pairs are
7+
# specified in the `mappings` array.
8+
#
9+
#
10+
# @param name
11+
# The namevar of the defined resource type is the filename of the map file
12+
# (without any extension), relative to the `haproxy::config_dir` directory.
13+
# A '.map' extension will be added automatically.
14+
#
15+
# @param mappings
16+
# An array of mappings for this map file. Array elements may be Hashes with a
17+
# single key-value pair each (preferably) or simple Strings. Default: `[]`
18+
#
19+
define haproxy::mapfile::entry (
20+
String $mapfile,
21+
Array[Variant[String, Hash]] $mappings = [$title],
22+
Variant[String, Integer] $order = '10',
23+
) {
24+
$_mapfile_name = "${::haproxy::config_dir}/${mapfile}.map"
25+
26+
concat::fragment { "haproxy_mapfile_${mapfile}-${title}":
27+
target => $_mapfile_name,
28+
content => template('haproxy/haproxy_mapfile.erb'),
29+
order => $order,
30+
}
31+
}

spec/acceptance/mapfile_spec.rb

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper_acceptance'
4+
5+
describe 'create mapfiles' do
6+
describe 'one mapfile' do
7+
let(:pp) do
8+
<<-MANIFEST
9+
include ::haproxy
10+
haproxy::mapfile { 'single-mapfile':
11+
ensure => 'present',
12+
mappings => [
13+
{ 'example.com' => 'bk_com' },
14+
{ 'example.net' => 'bk_net' },
15+
{ 'example.edu' => 'bk_edu' },
16+
],
17+
}
18+
MANIFEST
19+
end
20+
21+
it 'applies the manifest twice with no stderr' do
22+
idempotent_apply(pp)
23+
expect(file('/etc/haproxy/single-mapfile.map')).to be_file
24+
expect(file('/etc/haproxy/single-mapfile.map').content).to match "example.com bk_com\nexample.net bk_net\nexample.edu bk_edu\n"
25+
end
26+
end
27+
28+
describe 'multiple mapfiles' do
29+
let(:pp) do
30+
<<-MANIFEST
31+
include ::haproxy
32+
haproxy::mapfile { 'multiple-mapfiles':
33+
ensure => 'present',
34+
}
35+
haproxy::mapfile::entry { 'example.com bk_com':
36+
mapfile => 'multiple-mapfiles',
37+
}
38+
haproxy::mapfile::entry { 'org':
39+
mappings => ['example.org bk_org'],
40+
mapfile => 'multiple-mapfiles',
41+
order => '05',
42+
}
43+
haproxy::mapfile::entry { 'net':
44+
mappings => ['example.net bk_net'],
45+
mapfile => 'multiple-mapfiles',
46+
}
47+
haproxy::mapfile::entry { 'edu':
48+
mappings => [{'example.edu' => 'bk_edu'}],
49+
mapfile => 'multiple-mapfiles',
50+
}
51+
MANIFEST
52+
end
53+
54+
it 'applies the manifest twice with no stderr' do
55+
idempotent_apply(pp)
56+
expect(file('/etc/haproxy/multiple-mapfiles.map')).to be_file
57+
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"
58+
end
59+
end
60+
61+
describe 'check selection of correct backend' do
62+
describe 'single mapfile' do
63+
let(:pp) do
64+
<<-MANIFEST
65+
$error_page_content = @(ERROR_PAGE)
66+
HTTP/1.1 421 Misdirected Request
67+
Cache-Control: no-cache
68+
Connection: close
69+
Content-Type: text/plain
70+
71+
Error Page
72+
|ERROR_PAGE
73+
74+
file { '/tmp/error.html.http':
75+
content => $error_page_content,
76+
}
77+
file_line {'localhost':
78+
path => '/etc/hosts',
79+
line => '127.0.0.1 localhost host2 host3',
80+
match => '^127\.0\.0\.1',
81+
}
82+
include ::haproxy
83+
haproxy::mapfile { 'single-mapfile':
84+
ensure => 'present',
85+
mappings => [
86+
{ 'host2' => 'backend2' },
87+
{ 'host3' => 'backend3' },
88+
],
89+
}
90+
haproxy::frontend { 'test00':
91+
ipaddress => '127.0.0.1',
92+
ports => '5555',
93+
mode => 'http',
94+
options => {
95+
'use_backend' => '%[req.hdr(host),lower,map_dom(/etc/haproxy/single-mapfile.map,backend1)]'
96+
},
97+
}
98+
haproxy::backend { 'backend1':
99+
mode => 'http',
100+
options => [
101+
{
102+
'errorfile' => [
103+
'503 /tmp/error.html.http',
104+
],
105+
},
106+
],
107+
}
108+
haproxy::backend { 'backend2':
109+
defaults => 'http',
110+
collect_exported => false,
111+
options => { 'mode' => 'http' },
112+
}
113+
haproxy::balancermember { 'port 5556':
114+
listening_service => 'backend2',
115+
server_names => 'test00.example.com',
116+
defaults => 'http',
117+
ports => '5556',
118+
}
119+
haproxy::backend { 'backend3':
120+
defaults => 'http',
121+
collect_exported => false,
122+
options => { 'mode' => 'http' },
123+
}
124+
haproxy::balancermember { 'port 5557':
125+
listening_service => 'backend3',
126+
server_names => 'test01.example.com',
127+
defaults => 'http',
128+
ports => '5557',
129+
}
130+
MANIFEST
131+
end
132+
133+
it 'is able to listen with a mapfile' do
134+
retry_on_error_matching do
135+
apply_manifest(pp, catch_failures: true)
136+
end
137+
end
138+
139+
it 'has a complete mapfile' do
140+
expect(file('/etc/haproxy/single-mapfile.map')).to be_file
141+
expect(file('/etc/haproxy/single-mapfile.map').content).to match "host2 backend2\nhost3 backend3\n"
142+
end
143+
144+
it 'selects the correct backend based on host' do
145+
expect(run_shell('curl localhost:5555').stdout.chomp).to match(%r{Error Page})
146+
expect(run_shell('curl host2:5555').stdout.chomp).to match(%r{Response on 5556})
147+
expect(run_shell('curl host3:5555').stdout.chomp).to match(%r{Response on 5557})
148+
end
149+
end
150+
151+
describe 'multiple mapfiles' do
152+
let(:pp) do
153+
<<-MANIFEST
154+
$error_page_content = @(ERROR_PAGE)
155+
HTTP/1.1 421 Misdirected Request
156+
Cache-Control: no-cache
157+
Connection: close
158+
Content-Type: text/plain
159+
160+
Error Page
161+
|ERROR_PAGE
162+
163+
file { '/tmp/error.html.http':
164+
content => $error_page_content,
165+
}
166+
file_line {'localhost':
167+
path => '/etc/hosts',
168+
line => '127.0.0.1 localhost host2 host3',
169+
match => '^127\.0\.0\.1',
170+
}
171+
include ::haproxy
172+
haproxy::mapfile { 'multiple-mapfiles':
173+
ensure => 'present',
174+
}
175+
haproxy::mapfile::entry { 'host2 backend2':
176+
mapfile => 'multiple-mapfiles',
177+
}
178+
haproxy::mapfile::entry { 'host3 backend3':
179+
mapfile => 'multiple-mapfiles',
180+
}
181+
haproxy::frontend { 'test00':
182+
ipaddress => '127.0.0.1',
183+
ports => '5555',
184+
mode => 'http',
185+
options => {
186+
'use_backend' => '%[req.hdr(host),lower,map_dom(/etc/haproxy/single-mapfile.map,backend1)]'
187+
},
188+
}
189+
haproxy::backend { 'backend1':
190+
mode => 'http',
191+
options => [
192+
{
193+
'errorfile' => [
194+
'503 /tmp/error.html.http',
195+
],
196+
},
197+
],
198+
}
199+
haproxy::backend { 'backend2':
200+
defaults => 'http',
201+
collect_exported => false,
202+
options => { 'mode' => 'http' },
203+
}
204+
haproxy::balancermember { 'port 5556':
205+
listening_service => 'backend2',
206+
server_names => 'test00.example.com',
207+
defaults => 'http',
208+
ports => '5556',
209+
}
210+
haproxy::backend { 'backend3':
211+
defaults => 'http',
212+
collect_exported => false,
213+
options => { 'mode' => 'http' },
214+
}
215+
haproxy::balancermember { 'port 5557':
216+
listening_service => 'backend3',
217+
server_names => 'test01.example.com',
218+
defaults => 'http',
219+
ports => '5557',
220+
}
221+
MANIFEST
222+
end
223+
224+
it 'is able to listen with a mapfile' do
225+
retry_on_error_matching do
226+
apply_manifest(pp, catch_failures: true)
227+
end
228+
end
229+
230+
it 'has a complete mapfile' do
231+
expect(file('/etc/haproxy/multiple-mapfiles.map')).to be_file
232+
expect(file('/etc/haproxy/multiple-mapfiles.map').content).to match "host2 backend2\nhost3 backend3\n"
233+
end
234+
235+
it 'selects the correct backend based on host' do
236+
expect(run_shell('curl localhost:5555').stdout.chomp).to match(%r{Error Page})
237+
expect(run_shell('curl host2:5555').stdout.chomp).to match(%r{Response on 5556})
238+
expect(run_shell('curl host3:5555').stdout.chomp).to match(%r{Response on 5557})
239+
end
240+
end
241+
end
242+
end

spec/defines/mapfile_entry_spec.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
describe 'haproxy::mapfile::entry' do
6+
let(:pre_condition) { 'include haproxy' }
7+
let(:title) { 'example.com example-backend' }
8+
let(:facts) do
9+
{
10+
ipaddress: '1.1.1.1',
11+
osfamily: 'Redhat',
12+
concat_basedir: '/dne',
13+
}
14+
end
15+
16+
context 'when map domains to backends' do
17+
let(:params) do
18+
{
19+
mapfile: 'domains-to-backends',
20+
}
21+
end
22+
23+
it { is_expected.to compile.with_all_deps }
24+
it {
25+
is_expected.to contain_concat__fragment('haproxy_mapfile_domains-to-backends-example.com example-backend').with(
26+
'order' => '10',
27+
'target' => '/etc/haproxy/domains-to-backends.map',
28+
'content' => "example.com example-backend\n",
29+
)
30+
}
31+
end
32+
end

0 commit comments

Comments
 (0)