Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ _Private Classes_
**Resource types**

* [`rabbitmq_binding`](#rabbitmq_binding): Native type for managing rabbitmq bindings rabbitmq_binding { 'binding 1': ensure => present, source => 'myexchange'
* [`rabbitmq_cluster`](#rabbitmq_cluster): Type to manage a rabbitmq cluster
* [`rabbitmq_erlang_cookie`](#rabbitmq_erlang_cookie): Type to manage the rabbitmq erlang cookie securely This is essentially a private type used by the rabbitmq::config class to manage the erlan
* [`rabbitmq_exchange`](#rabbitmq_exchange): Native type for managing rabbitmq exchanges
* [`rabbitmq_parameter`](#rabbitmq_parameter): Type for managing rabbitmq parameters
Expand Down Expand Up @@ -148,6 +149,21 @@ class { 'rabbitmq':
}
```

To create and join the cluster:
```puppet
class { 'rabbitmq':
config_cluster => true,
cluster_nodes => ['rabbit1', 'rabbit2'],
cluster => {
'name' => 'test_cluster',
'init_node' => 'hostname'
},
cluster_node_type => 'ram',
erlang_cookie => 'A_SECRET_COOKIE_STRING',
wipe_db_on_cookie_change => true,
}
```

#### Parameters

The following parameters are available in the `rabbitmq` class.
Expand Down Expand Up @@ -213,6 +229,17 @@ Value to set for `cluster_partition_handling` RabbitMQ configuration variable.

Default value: 'ignore'

##### `cluster`

Data type: `Hash`

If both `name` and `init_node` keys are set then the rabbitmq node is added to
a cluster named after the corresponding key by joining `init_node`.
Note: `init_node` must be included in the [`cluster_nodes`](#cluster_nodes)
parameter.

Default value: '{}'

##### `collect_statistics_interval`

Data type: `Optional[Integer]`
Expand Down Expand Up @@ -1132,6 +1159,30 @@ The password to use to connect to rabbitmq

Default value: guest

### rabbitmq_cluster

Type to manage a rabbitmq cluster

#### Properties

The following properties are available in the `rabbitmq_cluster` type.

#### `init_node`

Data type: `String`

The node to join to initiate the cluster. It is mandatory.

Default value: unset

#### `node_disc_type`

Data type: `Enum['ram', 'disc']`

Choose between disc and ram cluster nodes.

Default value: disc

### rabbitmq_erlang_cookie

Type to manage the rabbitmq erlang cookie securely
Expand Down
1 change: 1 addition & 0 deletions data/common.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
rabbitmq::admin_enable: true
rabbitmq::management_enable: false
rabbitmq::use_config_file_for_plugins: false
rabbitmq::cluster: {}
rabbitmq::cluster_node_type: 'disc'
rabbitmq::cluster_nodes: []
rabbitmq::config: 'rabbitmq/rabbitmq.config.erb'
Expand Down
5 changes: 5 additions & 0 deletions examples/cluster/join_cluster_and_change_name.pp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# This sets the cluster name to `test_cluster`
# If run on another host than host1, this will join the host1's cluster
rabbitmq_cluster { 'test_cluster':
init_node => 'host1',
}
15 changes: 15 additions & 0 deletions lib/facter/rabbitmq_clustername.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Facter.add(:rabbitmq_clustername) do
setcode do
if Facter::Util::Resolution.which('rabbitmqctl')
ret = nil
cluster_status = Facter::Core::Execution.execute('rabbitmqctl -q cluster_status 2>&1')
[%r!{cluster_name,<<"(\S+)">>}!, %r!^Cluster name: (\S+)$!].each do |r|
if (data = r.match(cluster_status))
ret = data[1]
break
end
end
end
ret
end
end
40 changes: 40 additions & 0 deletions lib/puppet/provider/rabbitmq_cluster/rabbitmqctl.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'rabbitmq_cli'))
Puppet::Type.type(:rabbitmq_cluster).provide(
:rabbitmqctl,
parent: Puppet::Provider::RabbitmqCli
) do
confine feature: :posix

def exists?
cluster_name == @resource[:name].to_s
end

def create
storage_type = @resource[:node_disc_type].to_s

init_node = @resource[:init_node].to_s.gsub(%r{^.*@}, '')

if [Facter.value(:hostname), Facter.value(:fqdn)].include? init_node
return rabbitmqctl('set_cluster_name', @resource[:name]) unless cluster_name == resource[:name].to_s
else
rabbitmqctl('stop_app')
rabbitmqctl('join_cluster', "rabbit@#{init_node}", "--#{storage_type}")
rabbitmqctl('start_app')
end
end

def destroy
rabbitmqctl('stop_app')
rabbitmqctl('reset')
rabbitmqctl('start_app')
end

def cluster_name
cluster_status = rabbitmqctl('-q', 'cluster_status')
[%r!{cluster_name,<<"(\S+)">>}!, %r!^Cluster name: (\S+)$!].each do |r|
if (data = r.match(cluster_status))
return data[1]
end
end
end
end
50 changes: 50 additions & 0 deletions lib/puppet/type/rabbitmq_cluster.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
Puppet::Type.newtype(:rabbitmq_cluster) do
desc <<-DESC
Native type for managing rabbitmq cluster

@example Configure a cluster, rabbit_cluster
rabbitmq_cluster { 'rabbit_cluster':
init_node => 'host1'
}

@example Optional parameter tags will set further rabbitmq tags like monitoring, policymaker, etc.
To set the cluster name use cluster_name.
rabbitmq_cluster { 'rabbit_cluster':
init_node => 'host1',
node_disc_type => 'ram',
}
DESC

ensurable do
defaultto(:present)
newvalue(:present) do
provider.create
end
newvalue(:absent) do
provider.destroy
end
end

autorequire(:service) { 'rabbitmq-server' }

newparam(:name, namevar: true) do
desc 'The cluster name'
end

newparam(:init_node) do
desc 'Name of which cluster node to join.'
validate do |value|
resource.validate_init_node(value)
end
end

newparam(:node_disc_type) do
desc 'Storage type of node, default disc.'
newvalues(%r{disc|ram})
defaultto('disc')
end

def validate_init_node(value)
raise ArgumentError, 'init_node must be defined' if value.empty?
end
end
17 changes: 16 additions & 1 deletion manifests/init.pp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@
#
# @example Use RabbitMQ clustering facilities
# class { 'rabbitmq':
# cluster => {
# 'name' => 'test_cluster',
# 'init_node' => 'hostname'
# },
# config_cluster => true,
# cluster_nodes => ['rabbit1', 'rabbit2'],
# cluster_node_type => 'ram',
Expand All @@ -92,6 +96,7 @@
# An array specifying authorization/authentication backend to use. Single quotes should be placed around array entries,
# ex. `['{foo, baz}', 'baz']` Defaults to [rabbit_auth_backend_internal], and if using LDAP defaults to [rabbit_auth_backend_internal,
# rabbit_auth_backend_ldap].
# @param cluster Join cluster and change name of cluster.
# @param cluster_node_type
# Choose between disc and ram nodes.
# @param cluster_nodes
Expand Down Expand Up @@ -313,6 +318,7 @@
Boolean $admin_enable = true,
Boolean $management_enable = false,
Boolean $use_config_file_for_plugins = false,
Hash $cluster = $rabbitmq::cluster,
Enum['ram', 'disc'] $cluster_node_type = 'disc',
Array $cluster_nodes = [],
String $config = 'rabbitmq/rabbitmq.config.erb',
Expand Down Expand Up @@ -525,6 +531,15 @@
Class['rabbitmq::install::rabbitmqadmin'] -> Rabbitmq_exchange<| |>
}

if $config_cluster and $cluster['name'] and $cluster['init_node'] {
create_resources('rabbitmq_cluster', {
$cluster['name'] => {
'init_node' => $cluster['init_node'],
'node_disc_type' => $cluster_node_type,
}
})
}

if ($service_restart) {
Class['rabbitmq::config'] ~> Class['rabbitmq::service']
}
Expand All @@ -535,5 +550,5 @@
-> Class['rabbitmq::management']

# Make sure the various providers have their requirements in place.
Class['rabbitmq::install'] -> Rabbitmq_plugin<| |>
Class['rabbitmq::install'] -> Rabbitmq_plugin<| |> -> Rabbitmq_cluster<| |>
}
16 changes: 16 additions & 0 deletions spec/acceptance/clustering_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
it 'runs successfully' do
pp = <<-EOS
class { 'rabbitmq':
cluster => { 'name' => 'rabbit_cluster', 'init_node' => $facts['fqdn'] },
config_cluster => true,
cluster_nodes => ['rabbit1', 'rabbit2'],
cluster_node_type => 'ram',
environment_variables => { 'RABBITMQ_USE_LONGNAME' => true },
erlang_cookie => 'TESTCOOKIE',
wipe_db_on_cookie_change => false,
}
Expand All @@ -28,9 +30,11 @@ class { 'erlang': epel_enable => true}
it 'runs successfully' do
pp = <<-EOS
class { 'rabbitmq':
cluster => { 'name' => 'rabbit_cluster', 'init_node' => $facts['fqdn'] },
config_cluster => true,
cluster_nodes => ['rabbit1', 'rabbit2'],
cluster_node_type => 'ram',
environment_variables => { 'RABBITMQ_USE_LONGNAME' => true },
erlang_cookie => 'TESTCOOKIE',
wipe_db_on_cookie_change => true,
}
Expand All @@ -55,5 +59,17 @@ class { 'erlang': epel_enable => true}
it { is_expected.to be_file }
it { is_expected.to contain 'TESTCOOKIE' }
end

describe 'rabbitmq_cluster' do
context 'cluster_name => rabbit_cluster' do
it 'cluster has name' do
shell('rabbitmqctl -q cluster_status') do |r|
expect(r.stdout).to match(%r!({cluster_name,<<"rabbit_cluster">>}|^Cluster name: rabbit_cluster$)!)
expect(r.exit_code).to be_zero
end
# rubocop:enable RSpec/MultipleExpectations
end
end
end
end
end
77 changes: 77 additions & 0 deletions spec/unit/facter/util/fact_rabbitmq_clustername_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
require 'spec_helper'

describe Facter::Util::Fact do
before do
Facter.clear
end

describe 'rabbitmq_clusternam' do
context 'with value' do
it do
Facter::Util::Resolution.expects(:which).with('rabbitmqctl').returns(true)
Facter::Core::Execution.expects(:execute).with('rabbitmqctl -q cluster_status 2>&1').returns(' {cluster_name,<<"monty">>},')
expect(Facter.fact(:rabbitmq_clustername).value).to eq('monty')
end
end

context 'with dashes in hostname' do
it do
Facter::Util::Resolution.expects(:which).with('rabbitmqctl').returns(true)
Facter::Core::Execution.expects(:execute).with('rabbitmqctl -q cluster_status 2>&1').returns('Cluster name: rabbit-1')
expect(Facter.fact(:rabbitmq_clustername).value).to eq('rabbit-1')
end
end

context 'with dashes in clustername/hostname' do
it do
Facter::Util::Resolution.expects(:which).with('rabbitmqctl').returns(true)
Facter::Core::Execution.expects(:execute).with('rabbitmqctl -q cluster_status 2>&1').returns(' {cluster_name,<<"monty-python@rabbit-1">>},')
expect(Facter.fact(:rabbitmq_clustername).value).to eq('monty-python@rabbit-1')
end
end

context 'with quotes around node name' do
it do
Facter::Util::Resolution.expects(:which).with('rabbitmqctl').returns(true)
Facter::Core::Execution.expects(:execute).with('rabbitmqctl -q cluster_status 2>&1').returns("monty\npython\nCluster name: 'monty@rabbit-1'\nend\nof\nfile")
expect(Facter.fact(:rabbitmq_clustername).value).to eq("'monty@rabbit-1'")
end
end

context 'rabbitmq is not running' do
it do
error_string = <<-EOS
Status of node 'monty@rabbit-1' ...
Error: unable to connect to node 'monty@rabbit-1': nodedown

DIAGNOSTICS
===========

attempted to contact: ['monty@rabbit-1']

monty@rabbit-1:
* connected to epmd (port 4369) on centos-7-x64
* epmd reports: node 'rabbit' not running at all
no other nodes on centos-7-x64
* suggestion: start the node

current node details:
- node name: 'rabbitmq-cli-73@centos-7-x64'
- home dir: /var/lib/rabbitmq
- cookie hash: 6WdP0nl6d3HYqA5vTKMkIg==

EOS
Facter::Util::Resolution.expects(:which).with('rabbitmqctl').returns(true)
Facter::Core::Execution.expects(:execute).with('rabbitmqctl -q cluster_status 2>&1').returns(error_string)
expect(Facter.fact(:rabbitmq_clustername).value).to be_nil
end
end

context 'rabbitmqctl is not in path' do
it do
Facter::Util::Resolution.expects(:which).with('rabbitmqctl').returns(false)
expect(Facter.fact(:rabbitmq_clustername).value).to be_nil
end
end
end
end
Loading