Skip to content

Commit 4ffdb44

Browse files
committed
Add rabbitmq_policy custom type
Include support for RabbitMQ <3.2.0: * applyto field was not available before 3.2.0 * syntax of the set_policy command changed in 3.2.0
1 parent 92031d6 commit 4ffdb44

File tree

6 files changed

+431
-1
lines changed

6 files changed

+431
-1
lines changed

README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ be changed to latest.
271271
####`package_gpg_key`
272272

273273
RPM package GPG key to import. Uses source method. Should be a URL for Debian/RedHat
274-
OS family, or a file name for RedHat OS family.
274+
OS family, or a file name for RedHat OS family.
275275
Set to http://www.rabbitmq.com/rabbitmq-signing-key-public.asc by default.
276276
Note, that `key_content`, if specified, would override this parameter for Debian OS family.
277277

@@ -418,6 +418,20 @@ rabbitmq_user_permissions { 'dan@myhost':
418418
}
419419
```
420420

421+
### rabbitmq\_policy
422+
423+
```puppet
424+
rabbitmq_policy { 'ha-all@myhost':
425+
pattern => '.*',
426+
priority => 0,
427+
applyto => 'all',
428+
definition => {
429+
'ha-mode' => 'all',
430+
'ha-sync-mode' => 'automatic'
431+
}
432+
}
433+
```
434+
421435
### rabbitmq\_plugin
422436

423437
query all currently enabled plugins `$ puppet resource rabbitmq_plugin`
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
require 'json'
2+
require 'puppet/util/package'
3+
4+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'rabbitmqctl'))
5+
Puppet::Type.type(:rabbitmq_policy).provide(:rabbitmqctl, :parent => Puppet::Provider::Rabbitmqctl) do
6+
7+
defaultfor :feature => :posix
8+
9+
# cache policies
10+
def self.policies(name, vhost)
11+
@policies = {} unless @policies
12+
unless @policies[vhost]
13+
@policies[vhost] = {}
14+
rabbitmqctl('list_policies', '-q', '-p', vhost).split(/\n/).each do |line|
15+
# rabbitmq<3.2 does not support the applyto field
16+
# 1 2 3? 4 5 6
17+
# / ha-all all .* {"ha-mode":"all","ha-sync-mode":"automatic"} 0
18+
if line =~ /^(\S+)\s+(\S+)\s+(all|exchanges|queues)?\s*(\S+)\s+(\S+)\s+(\d+)$/
19+
applyto = $3 || 'all'
20+
@policies[vhost][$2] = {
21+
:applyto => applyto,
22+
:pattern => $4,
23+
:definition => JSON.parse($5),
24+
:priority => $6}
25+
else
26+
raise Puppet::Error, "cannot parse line from list_policies:#{line}"
27+
end
28+
end
29+
end
30+
@policies[vhost][name]
31+
end
32+
33+
def policies(name, vhost)
34+
self.class.policies(vhost, name)
35+
end
36+
37+
def should_policy
38+
if @should_policy
39+
@should_policy
40+
else
41+
@should_policy = resource[:name].rpartition('@').first
42+
end
43+
end
44+
45+
def should_vhost
46+
if @should_vhost
47+
@should_vhost
48+
else
49+
@should_vhost = resource[:name].rpartition('@').last
50+
end
51+
end
52+
53+
def create
54+
set_policy
55+
end
56+
57+
def destroy
58+
rabbitmqctl('clear_policy', '-p', should_vhost, should_policy)
59+
end
60+
61+
def exists?
62+
policies(should_vhost, should_policy)
63+
end
64+
65+
def pattern
66+
policies(should_vhost, should_policy)[:pattern]
67+
end
68+
69+
def pattern=(pattern)
70+
set_policy
71+
end
72+
73+
def applyto
74+
policies(should_vhost, should_policy)[:applyto]
75+
end
76+
77+
def applyto=(applyto)
78+
set_policy
79+
end
80+
81+
def definition
82+
policies(should_vhost, should_policy)[:definition]
83+
end
84+
85+
def definition=(definition)
86+
set_policy
87+
end
88+
89+
def priority
90+
policies(should_vhost, should_policy)[:priority]
91+
end
92+
93+
def priority=(priority)
94+
set_policy
95+
end
96+
97+
def set_policy
98+
unless @set_policy
99+
@set_policy = true
100+
resource[:applyto] ||= applyto
101+
resource[:definition] ||= definition
102+
resource[:pattern] ||= pattern
103+
resource[:priority] ||= priority
104+
# rabbitmq>=3.2.0
105+
if Puppet::Util::Package.versioncmp(self.class.rabbitmq_version, '3.2.0') >= 0
106+
rabbitmqctl('set_policy',
107+
'-p', should_vhost,
108+
'--priority', resource[:priority],
109+
'--apply-to', resource[:applyto].to_s,
110+
should_policy,
111+
resource[:pattern],
112+
resource[:definition].to_json
113+
)
114+
else
115+
rabbitmqctl('set_policy',
116+
'-p', should_vhost,
117+
should_policy,
118+
resource[:pattern],
119+
resource[:definition].to_json,
120+
resource[:priority]
121+
)
122+
end
123+
end
124+
end
125+
end

lib/puppet/provider/rabbitmqctl.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class Puppet::Provider::Rabbitmqctl < Puppet::Provider
2+
initvars
3+
commands :rabbitmqctl => 'rabbitmqctl'
4+
5+
def self.rabbitmq_version
6+
output = rabbitmqctl('-q', 'status')
7+
version = output.match(/\{rabbit,"RabbitMQ","([\d\.]+)"\}/)
8+
version[1] if version
9+
end
10+
end

lib/puppet/type/rabbitmq_policy.rb

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
Puppet::Type.newtype(:rabbitmq_policy) do
2+
desc 'Type for managing rabbitmq policies'
3+
4+
ensurable do
5+
defaultto(:present)
6+
newvalue(:present) do
7+
provider.create
8+
end
9+
newvalue(:absent) do
10+
provider.destroy
11+
end
12+
end
13+
14+
autorequire(:service) { 'rabbitmq-server' }
15+
16+
validate do
17+
fail('pattern parameter is required.') if self[:ensure] == :present and self[:pattern].nil?
18+
fail('definition parameter is required.') if self[:ensure] == :present and self[:definition].nil?
19+
end
20+
21+
newparam(:name, :namevar => true) do
22+
desc 'combination of policy@vhost to create policy for'
23+
newvalues(/^\S+@\S+$/)
24+
end
25+
26+
newproperty(:pattern) do
27+
desc 'policy pattern'
28+
validate do |value|
29+
resource.validate_pattern(value)
30+
end
31+
end
32+
33+
newproperty(:applyto) do
34+
desc 'policy apply to'
35+
newvalue(:all)
36+
newvalue(:exchanges)
37+
newvalue(:queues)
38+
defaultto :all
39+
end
40+
41+
newproperty(:definition) do
42+
desc 'policy definition'
43+
validate do |value|
44+
resource.validate_definition(value)
45+
end
46+
end
47+
48+
newproperty(:priority) do
49+
desc 'policy priority'
50+
newvalues(/^\d+$/)
51+
defaultto 0
52+
end
53+
54+
autorequire(:rabbitmq_vhost) do
55+
[self[:name].split('@')[1]]
56+
end
57+
58+
def validate_pattern(value)
59+
begin
60+
Regexp.new(value)
61+
rescue RegexpError
62+
raise ArgumentError, "Invalid regexp #{value}"
63+
end
64+
end
65+
66+
def validate_definition(definition)
67+
unless [Hash].include?(definition.class)
68+
raise ArgumentError, "Invalid definition"
69+
end
70+
definition.each do |k,v|
71+
unless [String].include?(v.class)
72+
raise ArgumentError, "Invalid definition"
73+
end
74+
end
75+
end
76+
end
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
require 'puppet'
2+
require 'mocha'
3+
4+
RSpec.configure do |config|
5+
config.mock_with :mocha
6+
end
7+
8+
describe Puppet::Type.type(:rabbitmq_policy).provider(:rabbitmqctl) do
9+
10+
let(:resource) do
11+
Puppet::Type.type(:rabbitmq_policy).new(
12+
:name => 'ha-all@/',
13+
:pattern => '.*',
14+
:definition => {
15+
'ha-mode' => 'all'
16+
},
17+
:provider => described_class.name
18+
)
19+
end
20+
21+
let(:provider) { resource.provider }
22+
23+
after(:each) do
24+
described_class.instance_variable_set(:@policies, nil)
25+
end
26+
27+
it 'should accept @ in policy name' do
28+
resource = Puppet::Type.type(:rabbitmq_policy).new(
29+
:name => 'ha@home@/',
30+
:pattern => '.*',
31+
:definition => {
32+
'ha-mode' => 'all'
33+
},
34+
:provider => described_class.name
35+
)
36+
provider = described_class.new(resource)
37+
provider.should_policy.should == 'ha@home'
38+
provider.should_vhost.should == '/'
39+
end
40+
41+
it 'should fail with invalid output from list' do
42+
provider.class.expects(:rabbitmqctl).with('list_policies', '-q', '-p', '/').returns 'foobar'
43+
expect { provider.exists? }.to raise_error(Puppet::Error, /cannot parse line from list_policies/)
44+
end
45+
46+
it 'should match policies from list (>=3.2.0)' do
47+
provider.class.expects(:rabbitmqctl).with('list_policies', '-q', '-p', '/').returns <<-EOT
48+
/ ha-all all .* {"ha-mode":"all","ha-sync-mode":"automatic"} 0
49+
/ test exchanges .* {"ha-mode":"all"} 0
50+
EOT
51+
provider.exists?.should == {
52+
:applyto => 'all',
53+
:pattern => '.*',
54+
:priority => '0',
55+
:definition => {
56+
'ha-mode' => 'all',
57+
'ha-sync-mode' => 'automatic'}
58+
}
59+
end
60+
61+
it 'should match policies from list (<3.2.0)' do
62+
provider.class.expects(:rabbitmqctl).with('list_policies', '-q', '-p', '/').returns <<-EOT
63+
/ ha-all .* {"ha-mode":"all","ha-sync-mode":"automatic"} 0
64+
/ test .* {"ha-mode":"all"} 0
65+
EOT
66+
provider.exists?.should == {
67+
:applyto => 'all',
68+
:pattern => '.*',
69+
:priority => '0',
70+
:definition => {
71+
'ha-mode' => 'all',
72+
'ha-sync-mode' => 'automatic'}
73+
}
74+
end
75+
76+
it 'should not match an empty list' do
77+
provider.class.expects(:rabbitmqctl).with('list_policies', '-q', '-p', '/').returns ''
78+
provider.exists?.should == nil
79+
end
80+
81+
it 'should destroy policy' do
82+
provider.expects(:rabbitmqctl).with('clear_policy', '-p', '/', 'ha-all')
83+
provider.destroy
84+
end
85+
86+
it 'should only call set_policy once (<3.2.0)' do
87+
provider.class.expects(:rabbitmq_version).returns '3.1.0'
88+
provider.resource[:priority] = '10'
89+
provider.resource[:applyto] = 'exchanges'
90+
provider.expects(:rabbitmqctl).with('set_policy',
91+
'-p', '/',
92+
'ha-all',
93+
'.*',
94+
'{"ha-mode":"all"}',
95+
'10').once
96+
provider.priority = '10'
97+
provider.applyto = 'exchanges'
98+
end
99+
100+
it 'should only call set_policy once (>=3.2.0)' do
101+
provider.class.expects(:rabbitmq_version).returns '3.2.0'
102+
provider.resource[:priority] = '10'
103+
provider.resource[:applyto] = 'exchanges'
104+
provider.expects(:rabbitmqctl).with('set_policy',
105+
'-p', '/',
106+
'--priority', '10',
107+
'--apply-to', 'exchanges',
108+
'ha-all',
109+
'.*',
110+
'{"ha-mode":"all"}').once
111+
provider.priority = '10'
112+
provider.applyto = 'exchanges'
113+
end
114+
end

0 commit comments

Comments
 (0)