Skip to content

Commit

Permalink
Allow greater flexibility in listen directive.
Browse files Browse the repository at this point in the history
Currently the module does not allow to specify specific bind options
per IP we are binding haproxy onto.
Hence, if a user has a service available across several network, but
with different haproxy bind option according to the network, s/he can't
configure haproxy with the current state of this module.

It aims to make it possible to have configuration as the following:

  bind 192.168.2.1:80 ssl crt public.puppetlabs.com.crt
  bind 10.0.0.1:80 ssl crt private.puppetlabs.com.crt
  bind 178.35.67.12:80
  • Loading branch information
Spredzy committed Sep 8, 2014
1 parent cec351c commit 5f07d77
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 35 deletions.
34 changes: 31 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

##Overview

The haproxy module provides the ability to install, configure, and manage HAProxy.
The haproxy module provides the ability to install, configure, and manage HAProxy.

##Module Description

Expand Down Expand Up @@ -121,6 +121,28 @@ haproxy::listen { 'puppet00':
},
}
```
###Configuring multi-network daemon listener

One might have more advanced needs for the listen block, then use the `$bind` parameter:

```puppet
haproxy::listen { 'puppet00':
mode => 'tcp',
options => {
'option' => [
'tcplog',
'ssl-hello-chk',
],
'balance' => 'roundrobin',
},
bind => {
'10.0.0.1:443' => ['ssl', 'crt', 'puppetlabs.com'],
'168.12.12.12:80' => [],
'192.168.122.42:80' => []
},
}
```
Note: `$ports` or `$ipaddress` and `$bind` are mutually exclusive

###Configuring HAProxy load-balanced member nodes

Expand Down Expand Up @@ -285,7 +307,10 @@ This type sets up a frontend service configuration block in haproxy.cfg. The HAP
**Parameters**

#####`bind_options`
Lists an array of options to be specified after the bind declaration in the bind's configuration block.
Lists an array of options to be specified after the bind declaration in the bind's configuration block. **Deprecated**: This parameter is being deprecated in favor of $bind

#####`bind`
A hash of ipaddress:port, with the haproxy bind options the address will have in the listening service's configuration block.

#####`ipaddress`
Specifies the IP address the proxy binds to. No value, '\*', and '0.0.0.0' mean that the proxy listens to all valid addresses on the system.
Expand Down Expand Up @@ -333,7 +358,10 @@ Using storeconfigs, you can export the `haproxy::balancermember` resources on al
**Parameters:**

#####`bind_options`
Sets the options to be specified after the bind declaration in the listening service's configuration block. Displays as an array.
Sets the options to be specified after the bind declaration in the listening service's configuration block. Displays as an array. **Deprecated**: This parameter is being deprecated in favor of $bind

#####`bind`
A hash of ipaddress:port, with the haproxy bind options the address will have in the listening service's configuration block.

#####`collect_exported`
Enables exported resources from `haproxy::balancermember` to be collected, serving as a form of autodiscovery. Displays as a Boolean and defaults to 'true'.
Expand Down
33 changes: 26 additions & 7 deletions manifests/frontend.pp
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,22 @@
# comma-separated string or an array of strings which may be ports or
# hyphenated port ranges.
#
# [*bind*]
# Set of ip addresses, port and bind options
# $bind = { '10.0.0.1:80' => ['ssl', 'crt', '/path/to/my/crt.pem'] }
#
# [*ipaddress*]
# The ip address the proxy binds to. Empty addresses, '*', and '0.0.0.0'
# mean that the proxy listens to all valid addresses on the system.
# The ip address the proxy binds to.
# Empty addresses, '*', and '0.0.0.0' mean that the proxy listens
# to all valid addresses on the system.
#
# [*mode*]
# The mode of operation for the frontend service. Valid values are undef,
# 'tcp', 'http', and 'health'.
#
# [*bind_options*]
# An array of options to be specified after the bind declaration in the
# bind's configuration block.
# (Deprecated) An array of options to be specified after the bind declaration
# in the listening serivce's configuration block.
#
# [*options*]
# A hash of options that are inserted into the frontend service
Expand Down Expand Up @@ -61,18 +66,32 @@
# Gary Larizza <gary@puppetlabs.com>
#
define haproxy::frontend (
$ports,
$ports = undef,
$ipaddress = [$::ipaddress],
$bind = undef,
$mode = undef,
$bind_options = undef,
$collect_exported = true,
$options = {
'option' => [
'tcplog',
],
}
},
# Deprecated
$bind_options = '',
) {

if $ports and $bind {
fail('The use of $ports and $bind is mutually exclusive, please choose either one')
}
if $ipaddress and $bind {

This comment has been minimized.

Copy link
@bitglue

bitglue Nov 10, 2014

Since $ipaddress always has a value, this is always true if one attempts to use $bind. Setting ipaddress => '' causes the templates to error, and ipaddress => undef asks puppet to use the default value, which isn't helpful.

fail('The use of $ipaddress and $bind is mutually exclusive, please choose either one')
}
if $bind_options {
warning('The $bind_options parameter is deprecated; please use $bind instead')
}
if $bind {
validate_hash($bind)
}
# Template uses: $name, $ipaddress, $ports, $options
concat::fragment { "${name}_frontend_block":
order => "15-${name}-00",
Expand Down
40 changes: 30 additions & 10 deletions manifests/listen.pp
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,13 @@
# hyphenated port ranges.
#
# [*ipaddress*]
# The ip address the proxy binds to. Empty addresses, '*', and '0.0.0.0'
# mean that the proxy listens to all valid addresses on the system.
# The ip address the proxy binds to.
# Empty addresses, '*', and '0.0.0.0' mean that the proxy listens
# to all valid addresses on the system.
#
# [*bind*]
# Set of ip addresses, port and bind options
# $bind = { '10.0.0.1:80' => ['ssl', 'crt', '/path/to/my/crt.pem'] }
#
# [*mode*]
# The mode of operation for the listening service. Valid values are undef,
Expand All @@ -39,8 +44,8 @@
# configuration block.
#
# [*bind_options*]
# An array of options to be specified after the bind declaration in the
# listening serivce's configuration block.
# (Deprecated) An array of options to be specified after the bind declaration
# in the listening serivce's configuration block.
#
# [*collect_exported*]
# Boolean, default 'true'. True means 'collect exported @@balancermember resources'
Expand Down Expand Up @@ -71,20 +76,35 @@
# Gary Larizza <gary@puppetlabs.com>
#
define haproxy::listen (
$ports,
$ipaddress = [$::ipaddress],
$mode = undef,
$collect_exported = true,
$options = {
$ports = undef,
$ipaddress = [$::ipaddress],
$bind = undef,
$mode = undef,
$collect_exported = true,
$options = {
'option' => [
'tcplog',
'ssl-hello-chk'
],
'balance' => 'roundrobin'
},
$bind_options = ''
# Deprecated
$bind_options = '',
) {

if $ports and $bind {
fail('The use of $ports and $bind is mutually exclusive, please choose either one')
}
if $ipaddress and $bind {
fail('The use of $ipaddress and $bind is mutually exclusive, please choose either one')
}
if $bind_options {
warning('The $bind_options parameter is deprecated; please use $bind instead')
}
if $bind {
validate_hash($bind)
}

if defined(Haproxy::Backend[$name]) {
fail("An haproxy::backend resource was discovered with the same name (${name}) which is not supported")
}
Expand Down
13 changes: 13 additions & 0 deletions spec/defines/frontend_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,19 @@
end
end
# C9949
context "when a ports parameter and a bind parameter are passed" do
let(:params) do
{
:name => 'apache',
:bind => {'192.168.0.1:80' => ['ssl']},
:ports => '80'
}
end

it 'should raise error' do
expect { subject }.to raise_error Puppet::Error, /mutually exclusive/
end
end
context "when multiple IPs are provided" do
let(:params) do
{
Expand Down
28 changes: 28 additions & 0 deletions spec/defines/listen_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,34 @@
'content' => "\nlisten apache\n bind *:80 \n balance roundrobin\n option tcplog\n option ssl-hello-chk\n"
) }
end
context "when a bind parameter hash is passed" do
let(:params) do
{
:name => 'apache',
:ipaddress => '',
:bind => {'10.0.0.1:333' => ['ssl', 'crt', 'public.puppetlabs.com'], '192.168.122.1:8082' => []},
}
end

it { should contain_concat__fragment('apache_listen_block').with(
'order' => '20-apache-00',
'target' => '/etc/haproxy/haproxy.cfg',
'content' => "\nlisten apache\n bind 10.0.0.1:333 ssl crt public.puppetlabs.com\n bind 192.168.122.1:8082 \n balance roundrobin\n option tcplog\n option ssl-hello-chk\n"
) }
end
context "when a ports parameter and a bind parameter are passed" do
let(:params) do
{
:name => 'apache',
:bind => {'192.168.0.1:80' => ['ssl']},
:ports => '80'
}
end

it 'should raise error' do
expect { subject }.to raise_error Puppet::Error, /mutually exclusive/
end
end
# C9977
context "when an invalid hostname is passed" do
let(:params) do
Expand Down
48 changes: 33 additions & 15 deletions templates/fragments/_bind.erb
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
<% require 'ipaddr' -%>
<% Array(@ipaddress).uniq.each do |virtual_ip| (@ports.is_a?(Array) ? @ports : Array(@ports.split(","))).each do |port| -%>
<%-
begin
IPAddr.new(virtual_ip)
valid_ip = true
rescue ArgumentError => e
valid_ip = false
end
if ! valid_ip and ! virtual_ip.match(/^[A-Za-z][A-Za-z0-9\.-]+$/) and virtual_ip != "*"
scope.function_fail(["Invalid IP address or hostname [#{virtual_ip}]"])
end
-%>
<%- scope.function_fail(["Port [#{port}] is outside of range 1-65535"]) if port.to_i < 1 or port.to_i > 65535 -%>
bind <%= virtual_ip %>:<%= port %> <%= Array(@bind_options).join(" ") %>
<% end end -%>
<% if @bind
@bind.keys.uniq.sort.each do |virtual_ip|
if ip_port = virtual_ip.match(/^([A-Za-z0-9\.-]+):([0-9]+)$/)
ip = ip_port[1]
port = ip_port[2]
elsif virtual_ip.match(/^([A-Za-z0-9\.-]+)$/)
ip = virtual_ip
end
begin
IPAddr.new(ip)
valid_ip = true
rescue ArgumentError => e
valid_ip = false
end
if ! valid_ip and ! ip.match(/^[A-Za-z][A-Za-z0-9\.-]+$/) and ip != "*"
scope.function_fail(["Invalid IP address or hostname [#{ip}]"])
end
scope.function_fail(["Port #{port} for IP #{ip} is outside of range 1-65535"]) if port and (port.to_i < 1 or port.to_i > 65535) -%>
bind <%= ip -%>:<%= port -%> <%= Array(@bind[virtual_ip]).join(" ") %>
<%- end else
Array(@ipaddress).uniq.each do |virtual_ip| (@ports.is_a?(Array) ? @ports : Array(@ports.split(","))).each do |port|
begin
IPAddr.new(virtual_ip)
valid_ip = true
rescue ArgumentError => e
valid_ip = false
end
if ! valid_ip and ! virtual_ip.match(/^[A-Za-z][A-Za-z0-9\.-]+$/) and virtual_ip != "*"
scope.function_fail(["Invalid IP address or hostname [#{virtual_ip}]"])
end
scope.function_fail(["Port [#{port}] is outside of range 1-65535"]) if port.to_i < 1 or port.to_i > 65535 -%>
bind <%= virtual_ip -%>:<%= port -%> <%= Array(@bind_options).join(" ") %>
<%- end end end -%>

0 comments on commit 5f07d77

Please sign in to comment.