
In some circonstances, if an existing vrrp instance is on the network it will try to join our vrrp instance, thus adding an authentication method ensures that we won't face this situation. Signed-off-by: Sébastien Han <sebastien.han@enovance.com>
511 lines
19 KiB
Ruby
511 lines
19 KiB
Ruby
#
|
|
# Copyright (C) 2014 eNovance SAS <licensing@enovance.com>
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
#
|
|
# Unit tests for cloud::loadbalancer class
|
|
#
|
|
|
|
require 'spec_helper'
|
|
|
|
describe 'cloud::loadbalancer' do
|
|
|
|
shared_examples_for 'openstack loadbalancer' do
|
|
|
|
let :params do
|
|
{ :ceilometer_api => true,
|
|
:cinder_api => true,
|
|
:glance_api => true,
|
|
:neutron_api => true,
|
|
:heat_api => true,
|
|
:heat_cfn_api => true,
|
|
:heat_cloudwatch_api => true,
|
|
:nova_api => true,
|
|
:ec2_api => true,
|
|
:metadata_api => true,
|
|
:swift_api => true,
|
|
:keystone_api_admin => true,
|
|
:keystone_api => true,
|
|
:trove_api => true,
|
|
:horizon => true,
|
|
:spice => true,
|
|
:ceilometer_bind_options => [],
|
|
:cinder_bind_options => [],
|
|
:ec2_bind_options => [],
|
|
:glance_api_bind_options => [],
|
|
:glance_registry_bind_options => [],
|
|
:heat_cfn_bind_options => [],
|
|
:heat_cloudwatch_bind_options => [],
|
|
:heat_api_bind_options => [],
|
|
:keystone_bind_options => [],
|
|
:keystone_admin_bind_options => [],
|
|
:metadata_bind_options => [],
|
|
:neutron_bind_options => [],
|
|
:trove_bind_options => [],
|
|
:swift_bind_options => [],
|
|
:spice_bind_options => [],
|
|
:horizon_bind_options => [],
|
|
:galera_bind_options => [],
|
|
:haproxy_auth => 'root:secrete',
|
|
:keepalived_state => 'BACKUP',
|
|
:keepalived_priority => 50,
|
|
:keepalived_vrrp_interface => false,
|
|
:keepalived_public_interface => 'eth0',
|
|
:keepalived_public_ipvs => ['10.0.0.1', '10.0.0.2'],
|
|
:keepalived_auth_type => 'PASS',
|
|
:keepalived_auth_pass => 'secret',
|
|
:horizon_port => '80',
|
|
:spice_port => '6082',
|
|
:vip_public_ip => '10.0.0.1',
|
|
:galera_ip => '10.0.0.2',
|
|
:galera_slave => false,
|
|
:horizon_ssl => false,
|
|
:horizon_ssl_port => false,
|
|
:ks_ceilometer_public_port => '8777',
|
|
:ks_nova_public_port => '8774',
|
|
:ks_ec2_public_port => '8773',
|
|
:ks_metadata_public_port => '8777',
|
|
:ks_glance_api_public_port => '9292',
|
|
:ks_glance_registry_internal_port => '9191',
|
|
:ks_swift_public_port => '8080',
|
|
:ks_keystone_public_port => '5000',
|
|
:ks_keystone_admin_port => '35357',
|
|
:ks_cinder_public_port => '8776',
|
|
:ks_neutron_public_port => '9696',
|
|
:ks_trove_public_port => '8779',
|
|
:ks_heat_public_port => '8004',
|
|
:ks_heat_cfn_public_port => '8000',
|
|
:ks_heat_cloudwatch_public_port => '8003' }
|
|
end
|
|
|
|
it 'configure haproxy server' do
|
|
is_expected.to contain_class('haproxy')
|
|
end # configure haproxy server
|
|
|
|
it 'configure keepalived server' do
|
|
is_expected.to contain_class('keepalived')
|
|
end # configure keepalived server
|
|
|
|
it 'configure sysctl to allow HAproxy to bind to a non-local IP address' do
|
|
is_expected.to contain_exec('exec_sysctl_net.ipv4.ip_nonlocal_bind').with_command(
|
|
'sysctl -w net.ipv4.ip_nonlocal_bind=1'
|
|
)
|
|
end
|
|
|
|
context 'configure an internal VIP' do
|
|
before do
|
|
params.merge!(:keepalived_internal_ipvs => ['192.168.0.1'])
|
|
end
|
|
it 'configure an internal VRRP instance' do
|
|
is_expected.to contain_keepalived__instance('2').with({
|
|
'interface' => 'eth1',
|
|
'virtual_ips' => ['192.168.0.1 dev eth1'],
|
|
'track_script' => ['haproxy'],
|
|
'state' => 'BACKUP',
|
|
'priority' => params[:keepalived_priority],
|
|
'auth_type' => 'PASS',
|
|
'auth_pass' => 'secret',
|
|
'notify_master' => '"/etc/init.d/haproxy start"',
|
|
'notify_backup' => '"/etc/init.d/haproxy stop"',
|
|
})
|
|
end
|
|
end
|
|
|
|
context 'configure keepalived with deprecated parameters' do
|
|
before do
|
|
params.merge!(
|
|
:keepalived_ipvs => ['192.168.0.2'],
|
|
:vip_public_ip => '192.168.0.2',
|
|
:galera_ip => '192.168.0.2',
|
|
:keepalived_interface => 'eth3'
|
|
)
|
|
end
|
|
it 'configure a public VRRP instance with deprecated parameters' do
|
|
is_expected.to contain_keepalived__instance('1').with({
|
|
'interface' => 'eth3',
|
|
'virtual_ips' => ['192.168.0.2 dev eth3'],
|
|
'track_script' => ['haproxy'],
|
|
'state' => 'BACKUP',
|
|
'priority' => params[:keepalived_priority],
|
|
'auth_type' => 'PASS',
|
|
'auth_pass' => 'secret',
|
|
'notify_master' => '"/etc/init.d/haproxy start"',
|
|
'notify_backup' => '"/etc/init.d/haproxy stop"',
|
|
})
|
|
end
|
|
end
|
|
|
|
context 'configure keepalived vrrp on dedicated interface' do
|
|
before do
|
|
params.merge!(:keepalived_vrrp_interface => 'eth2')
|
|
end
|
|
it 'configure keepalived with a dedicated interface for vrrp' do
|
|
is_expected.to contain_keepalived__instance('1').with({
|
|
'interface' => 'eth2',
|
|
})
|
|
end
|
|
end
|
|
|
|
context 'when keepalived and HAproxy are in backup' do
|
|
it 'configure vrrp_instance with BACKUP state' do
|
|
is_expected.to contain_keepalived__instance('1').with({
|
|
'interface' => params[:keepalived_public_interface],
|
|
'virtual_ips' => ['10.0.0.1 dev eth0', '10.0.0.2 dev eth0'],
|
|
'track_script' => ['haproxy'],
|
|
'state' => params[:keepalived_state],
|
|
'priority' => params[:keepalived_priority],
|
|
'auth_type' => 'PASS',
|
|
'auth_pass' => 'secret',
|
|
'notify_master' => '"/etc/init.d/haproxy start"',
|
|
'notify_backup' => '"/etc/init.d/haproxy stop"',
|
|
})
|
|
end # configure vrrp_instance with BACKUP state
|
|
it 'configure haproxy server without service managed' do
|
|
is_expected.to contain_class('haproxy').with(:service_manage => true)
|
|
end # configure haproxy server
|
|
end # configure keepalived in backup
|
|
|
|
context 'configure keepalived in master' do
|
|
before do
|
|
params.merge!( :keepalived_state => 'MASTER' )
|
|
end
|
|
it 'configure vrrp_instance with MASTER state' do
|
|
is_expected.to contain_keepalived__instance('1').with({
|
|
'interface' => params[:keepalived_public_interface],
|
|
'track_script' => ['haproxy'],
|
|
'state' => 'MASTER',
|
|
'priority' => params[:keepalived_priority],
|
|
'auth_type' => 'PASS',
|
|
'auth_pass' => 'secret',
|
|
'notify_master' => '"/etc/init.d/haproxy start"',
|
|
'notify_backup' => '"/etc/init.d/haproxy stop"',
|
|
})
|
|
end
|
|
it 'configure haproxy server with service managed' do
|
|
is_expected.to contain_class('haproxy').with(:service_manage => true)
|
|
end # configure haproxy server
|
|
end # configure keepalived in master
|
|
|
|
context 'configure logrotate file' do
|
|
it { is_expected.to contain_file('/etc/logrotate.d/haproxy').with(
|
|
:source => 'puppet:///modules/cloud/logrotate/haproxy',
|
|
:mode => '0644',
|
|
:owner => 'root',
|
|
:group => 'root'
|
|
)}
|
|
end # configure logrotate file
|
|
|
|
context 'configure monitor haproxy listen' do
|
|
it { is_expected.to contain_haproxy__listen('monitor').with(
|
|
:ipaddress => params[:vip_public_ip],
|
|
:ports => '9300'
|
|
)}
|
|
end # configure monitor haproxy listen
|
|
|
|
context 'configure galera haproxy listen' do
|
|
it { is_expected.to contain_haproxy__listen('galera_cluster').with(
|
|
:ipaddress => params[:galera_ip],
|
|
:ports => '3306',
|
|
:options => {
|
|
'maxconn' => '1000',
|
|
'mode' => 'tcp',
|
|
'balance' => 'roundrobin',
|
|
'option' => ['tcpka','tcplog','httpchk'],
|
|
'timeout client' => '400s',
|
|
'timeout server' => '400s'
|
|
}
|
|
)}
|
|
end # configure monitor haproxy listen
|
|
|
|
context 'not configure galera slave haproxy listen' do
|
|
it { is_expected.not_to contain_haproxy__listen('galera_readonly_cluster') }
|
|
end # configure monitor haproxy listen
|
|
|
|
context 'configure galera slave haproxy listen' do
|
|
before do
|
|
params.merge!( :galera_slave => true )
|
|
end
|
|
it { is_expected.to contain_haproxy__listen('galera_readonly_cluster').with(
|
|
:ipaddress => params[:galera_ip],
|
|
:ports => '3307',
|
|
:options => {
|
|
'maxconn' => '1000',
|
|
'mode' => 'tcp',
|
|
'balance' => 'roundrobin',
|
|
'option' => ['tcpka','tcplog','httpchk'],
|
|
'timeout client' => '400s',
|
|
'timeout server' => '400s'
|
|
}
|
|
)}
|
|
end # configure monitor haproxy listen
|
|
|
|
# test backward compatibility
|
|
context 'configure OpenStack binding on public network only' do
|
|
it { is_expected.to contain_haproxy__listen('spice_cluster').with(
|
|
:ipaddress => [params[:vip_public_ip]],
|
|
:ports => '6082',
|
|
:options => {
|
|
'mode' => 'tcp',
|
|
'balance' => 'source',
|
|
'option' => ['tcpka', 'tcplog', 'forwardfor'],
|
|
'timeout server' => '120m',
|
|
'timeout client' => '120m'
|
|
}
|
|
)}
|
|
end
|
|
|
|
context 'configure OpenStack binding on both public and internal networks' do
|
|
before do
|
|
params.merge!(
|
|
:nova_api => true,
|
|
:galera_ip => '172.16.0.1',
|
|
:vip_public_ip => '172.16.0.1',
|
|
:vip_internal_ip => '192.168.0.1',
|
|
:keepalived_public_ipvs => ['172.16.0.1', '172.16.0.2'],
|
|
:keepalived_internal_ipvs => ['192.168.0.1', '192.168.0.2']
|
|
)
|
|
end
|
|
it { is_expected.to contain_haproxy__listen('nova_api_cluster').with(
|
|
:ipaddress => ['172.16.0.1', '192.168.0.1'],
|
|
:ports => '8774'
|
|
)}
|
|
end
|
|
|
|
context 'configure OpenStack binding on IPv4 and IPv6 public ip' do
|
|
before do
|
|
params.merge!(
|
|
:nova_api => true,
|
|
:galera_ip => '172.16.0.1',
|
|
:vip_public_ip => ['172.16.0.1', '2001:0db8:85a3:0000:0000:8a2e:0370:7334'],
|
|
:vip_internal_ip => '192.168.0.1',
|
|
:keepalived_public_ipvs => ['172.16.0.1', '172.16.0.2', '2001:0db8:85a3:0000:0000:8a2e:0370:7334'],
|
|
:keepalived_internal_ipvs => ['192.168.0.1', '192.168.0.2']
|
|
)
|
|
end
|
|
it { is_expected.to contain_haproxy__listen('nova_api_cluster').with(
|
|
:ipaddress => ['172.16.0.1', '2001:0db8:85a3:0000:0000:8a2e:0370:7334', '192.168.0.1'],
|
|
:ports => '8774'
|
|
)}
|
|
end
|
|
|
|
context 'disable an OpenStack service binding' do
|
|
before do
|
|
params.merge!(:metadata_api => false)
|
|
end
|
|
it { is_expected.not_to contain_haproxy__listen('metadata_api_cluster') }
|
|
end
|
|
|
|
context 'should fail to configure OpenStack binding when vip_public_ip and vip_internal_ip are missing' do
|
|
before do
|
|
params.merge!(
|
|
:nova_api => true,
|
|
:galera_ip => '172.16.0.1',
|
|
:vip_public_ip => false,
|
|
:vip_internal_ip => false,
|
|
:keepalived_public_ipvs => ['172.16.0.1', '172.16.0.2']
|
|
)
|
|
end
|
|
it_raises 'a Puppet::Error', /vip_public_ip and vip_internal_ip are both set to false, no binding is possible./
|
|
end
|
|
|
|
context 'should fail to configure OpenStack binding when given VIP is not in the VIP pool list' do
|
|
before do
|
|
params.merge!(
|
|
:nova_api => '10.0.0.1',
|
|
:galera_ip => '172.16.0.1',
|
|
:vip_public_ip => '172.16.0.1',
|
|
:vip_internal_ip => false,
|
|
:keepalived_public_ipvs => ['172.16.0.1', '172.16.0.2']
|
|
)
|
|
end
|
|
it_raises 'a Puppet::Error', /10.0.0.1 is not part of VIP pools./
|
|
end
|
|
|
|
context 'with a public OpenStack VIP not in the keepalived VIP list' do
|
|
before do
|
|
params.merge!(
|
|
:vip_public_ip => '172.16.0.1',
|
|
:keepalived_public_ipvs => ['192.168.0.1', '192.168.0.2']
|
|
)
|
|
end
|
|
it_raises 'a Puppet::Error', /vip_public_ip should be part of keepalived_public_ipvs./
|
|
end
|
|
|
|
context 'with an internal OpenStack VIP not in the keepalived VIP list' do
|
|
before do
|
|
params.merge!(
|
|
:vip_internal_ip => '172.16.0.1',
|
|
:keepalived_internal_ipvs => ['192.168.0.1', '192.168.0.2']
|
|
)
|
|
end
|
|
it_raises 'a Puppet::Error', /vip_internal_ip should be part of keepalived_internal_ipvs./
|
|
end
|
|
|
|
context 'with a Galera VIP not in the keepalived VIP list' do
|
|
before do
|
|
params.merge!(
|
|
:galera_ip => '172.16.0.1',
|
|
:vip_public_ip => '192.168.0.1',
|
|
:keepalived_public_ipvs => ['192.168.0.1', '192.168.0.2'],
|
|
:keepalived_internal_ipvs => ['192.168.1.1', '192.168.1.2']
|
|
)
|
|
end
|
|
it_raises 'a Puppet::Error', /galera_ip should be part of keepalived_public_ipvs or keepalived_internal_ipvs./
|
|
end
|
|
|
|
context 'configure OpenStack binding with HTTPS and SSL offloading' do
|
|
before do
|
|
params.merge!(
|
|
:nova_bind_options => ['ssl', 'crt']
|
|
)
|
|
end
|
|
it { is_expected.to contain_haproxy__listen('nova_api_cluster').with(
|
|
:ipaddress => [params[:vip_public_ip]],
|
|
:ports => '8774',
|
|
:options => {
|
|
'mode' => 'http',
|
|
'option' => ['tcpka','forwardfor','tcplog','httpchk'],
|
|
'http-check' => 'expect ! rstatus ^5',
|
|
'balance' => 'roundrobin',
|
|
},
|
|
:bind_options => ['ssl', 'crt']
|
|
)}
|
|
end
|
|
|
|
context 'configure OpenStack binding with HTTP options' do
|
|
before do
|
|
params.merge!(
|
|
:cinder_bind_options => 'something not secure',
|
|
)
|
|
end
|
|
it { is_expected.to contain_haproxy__listen('cinder_api_cluster').with(
|
|
:ipaddress => [params[:vip_public_ip]],
|
|
:ports => '8776',
|
|
:options => {
|
|
'mode' => 'http',
|
|
'option' => ['tcpka','forwardfor','tcplog', 'httpchk'],
|
|
'http-check' => 'expect ! rstatus ^5',
|
|
'balance' => 'roundrobin',
|
|
},
|
|
:bind_options => ['something not secure']
|
|
)}
|
|
end
|
|
|
|
context 'configure OpenStack Horizon' do
|
|
it { is_expected.to contain_haproxy__listen('horizon_cluster').with(
|
|
:ipaddress => [params[:vip_public_ip]],
|
|
:ports => '80',
|
|
:options => {
|
|
'mode' => 'http',
|
|
'http-check' => 'expect ! rstatus ^5',
|
|
'option' => ["tcpka", "forwardfor", "tcplog", "httpchk GET /#{platform_params[:auth_url]} \"HTTP/1.0\\r\\nUser-Agent: HAproxy-myhost\""],
|
|
'cookie' => 'sessionid prefix',
|
|
'balance' => 'leastconn',
|
|
}
|
|
)}
|
|
end
|
|
|
|
context 'configure OpenStack Horizon with SSL termination on HAProxy' do
|
|
before do
|
|
params.merge!(
|
|
:horizon_port => '443',
|
|
:horizon_ssl => false,
|
|
:horizon_ssl_port => false,
|
|
:horizon_bind_options => ['ssl', 'crt']
|
|
)
|
|
end
|
|
it { is_expected.to contain_haproxy__listen('horizon_cluster').with(
|
|
:ipaddress => [params[:vip_public_ip]],
|
|
:ports => '443',
|
|
:options => {
|
|
'mode' => 'http',
|
|
'http-check' => 'expect ! rstatus ^5',
|
|
'option' => ["tcpka", "forwardfor", "tcplog", "httpchk GET /#{platform_params[:auth_url]} \"HTTP/1.0\\r\\nUser-Agent: HAproxy-myhost\""],
|
|
'cookie' => 'sessionid prefix',
|
|
'balance' => 'leastconn',
|
|
'reqadd' => 'X-Forwarded-Proto:\ https if { ssl_fc }'
|
|
},
|
|
:bind_options => ['ssl', 'crt']
|
|
)}
|
|
end
|
|
|
|
context 'configure OpenStack Horizon SSL with termination on the webserver' do
|
|
before do
|
|
params.merge!(
|
|
:horizon_ssl => true,
|
|
:horizon_ssl_port => '443'
|
|
)
|
|
end
|
|
it { is_expected.to contain_haproxy__listen('horizon_ssl_cluster').with(
|
|
:ipaddress => [params[:vip_public_ip]],
|
|
:ports => '443',
|
|
:options => {
|
|
'mode' => 'tcp',
|
|
'option' => ["tcpka", "forwardfor", "tcplog", "ssl-hello-chk"],
|
|
'cookie' => 'sessionid prefix',
|
|
'balance' => 'leastconn',
|
|
}
|
|
)}
|
|
end
|
|
|
|
context 'configure OpenStack Heat API SSL binding' do
|
|
before do
|
|
params.merge!(
|
|
:heat_api_bind_options => ['ssl', 'crt']
|
|
)
|
|
end
|
|
it { is_expected.to contain_haproxy__listen('heat_api_cluster').with(
|
|
:ipaddress => [params[:vip_public_ip]],
|
|
:ports => '8004',
|
|
:options => {
|
|
'reqadd' => 'X-Forwarded-Proto:\ https if { ssl_fc }',
|
|
'mode' => 'http',
|
|
'option' => ['tcpka','forwardfor','tcplog', 'httpchk'],
|
|
'http-check' => 'expect ! rstatus ^5',
|
|
'balance' => 'roundrobin'
|
|
},
|
|
:bind_options => ['ssl', 'crt']
|
|
)}
|
|
end
|
|
end # shared:: openstack loadbalancer
|
|
|
|
context 'on Debian platforms' do
|
|
let :facts do
|
|
{ :osfamily => 'Debian',
|
|
:hostname => 'myhost',
|
|
:concat_basedir => '/var/lib/puppet/concat' }
|
|
end
|
|
|
|
let :platform_params do
|
|
{ :auth_url => 'horizon' }
|
|
end
|
|
|
|
it_configures 'openstack loadbalancer'
|
|
end
|
|
|
|
context 'on RedHat platforms' do
|
|
let :facts do
|
|
{ :osfamily => 'RedHat',
|
|
:hostname => 'myhost',
|
|
:concat_basedir => '/var/lib/puppet/concat' }
|
|
end
|
|
|
|
let :platform_params do
|
|
{ :auth_url => 'dashboard' }
|
|
end
|
|
|
|
it_configures 'openstack loadbalancer'
|
|
end
|
|
|
|
end
|