Skip to content

Commit

Permalink
Add new resource rabbitmq_binding
Browse files Browse the repository at this point in the history
  • Loading branch information
ironpinguin authored and globin committed Dec 22, 2014
1 parent ebc20c2 commit d998f94
Show file tree
Hide file tree
Showing 4 changed files with 315 additions and 0 deletions.
110 changes: 110 additions & 0 deletions lib/puppet/provider/rabbitmq_binding/rabbitmqadmin.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
require 'json'
require 'puppet'
Puppet::Type.type(:rabbitmq_binding).provide(:rabbitmqadmin) do

if Puppet::PUPPETVERSION.to_f < 3
commands :rabbitmqctl => 'rabbitmqctl'
commands :rabbitmqadmin => '/usr/local/bin/rabbitmqadmin'
else
has_command(:rabbitmqctl, 'rabbitmqctl') do
environment :HOME => "/tmp"
end
has_command(:rabbitmqadmin, '/usr/local/bin/rabbitmqadmin') do
environment :HOME => "/tmp"
end
end
defaultfor :feature => :posix

def should_vhost
if @should_vhost
@should_vhost
else
@should_vhost = resource[:name].split('@').last
end
end

def self.all_vhosts
vhosts = []
rabbitmqctl('list_vhosts', '-q').split(/\n/).collect do |vhost|
vhosts.push(vhost)
end
vhosts
end

def self.all_bindings(vhost)
rabbitmqctl('list_bindings', '-q', '-p', vhost, 'source_name', 'destination_name', 'destination_kind', 'routing_key', 'arguments').split(/\n/)
end

def self.instances
resources = []
all_vhosts.each do |vhost|
all_bindings(vhost).collect do |line|
source_name, destination_name, destination_type, routing_key, arguments = line.split(/\t/)
# Convert output of arguments from the rabbitmqctl command to a json string.
if !arguments.nil?
arguments = arguments.gsub(/^\[(.*)\]$/, "").gsub(/\{("(?:.|\\")*?"),/, '{\1:').gsub(/\},\{/, ",")
if arguments == ""
arguments = '{}'
end
else
arguments = '{}'
end
if (source_name != '')
binding = {
:destination_type => destination_type,
:routing_key => routing_key,
:arguments => JSON.parse(arguments),
:ensure => :present,
:name => "%s@%s@%s" % [source_name, destination_name, vhost],
}
resources << new(binding) if binding[:name]
end
end
end
resources
end

def self.prefetch(resources)
packages = instances
resources.keys.each do |name|
if provider = packages.find{ |pkg| pkg.name == name }
resources[name].provider = provider
end
end
end

def exists?
@property_hash[:ensure] == :present
end

def create
vhost_opt = should_vhost ? "--vhost=#{should_vhost}" : ''
name = resource[:name].split('@').first
destination = resource[:name].split('@')[1]
arguments = resource[:arguments]
if arguments.nil?
arguments = {}
end
rabbitmqadmin('declare',
'binding',
vhost_opt,
"--user=#{resource[:user]}",
"--password=#{resource[:password]}",
"source=#{name}",
"destination=#{destination}",
"arguments=#{arguments.to_json}",
"routing_key=#{resource[:routing_key]}",
"destination_type=#{resource[:destination_type]}"
)
@property_hash[:ensure] = :present
end

def destroy
vhost_opt = should_vhost ? "--vhost=#{should_vhost}" : ''
name = resource[:name].split('@').first
destination = resource[:name].split('@')[1]
rabbitmqadmin('delete', 'binding', vhost_opt, "--user=#{resource[:user]}", "--password=#{resource[:password]}", "source=#{name}", "destination_type=#{resource[:destination_type]}", "destination=#{destination}")
@property_hash[:ensure] = :absent
end

end
96 changes: 96 additions & 0 deletions lib/puppet/type/rabbitmq_binding.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
Puppet::Type.newtype(:rabbitmq_binding) do
desc 'Native type for managing rabbitmq bindings'

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

newparam(:name, :namevar => true) do
desc 'source and destination of bind'
newvalues(/^\S*@\S+@\S+$/)
end

newparam(:destination_type) do
desc 'binding destination_type'
newvalues(/queue|exchange/)
defaultto('queue')
end

newparam(:routing_key) do
desc 'binding routing_key'
newvalues(/^\S*$/)
end

newparam(:arguments) do
desc 'binding arguments'
defaultto {}
validate do |value|
resource.validate_argument(value)
end
end

newparam(:user) do
desc 'The user to use to connect to rabbitmq'
defaultto('guest')
newvalues(/^\S+$/)
end

newparam(:password) do
desc 'The password to use to connect to rabbitmq'
defaultto('guest')
newvalues(/\S+/)
end

autorequire(:rabbitmq_vhost) do
[self[:name].split('@')[2]]
end

autorequire(:rabbitmq_exchange) do
setup_autorequire('exchange')
end

autorequire(:rabbitmq_queue) do
setup_autorequire('queue')
end

autorequire(:rabbitmq_user) do
[self[:user]]
end

autorequire(:rabbitmq_user_permissions) do
[
"#{self[:user]}@#{self[:name].split('@')[1]}",
"#{self[:user]}@#{self[:name].split('@')[0]}"
]
end

def setup_autorequire(type)
destination_type = value(:destination_type)
if type == 'exchange'
rval = ["#{self[:name].split('@')[0]}@#{self[:name].split('@')[2]}"]
if destination_type == type
rval.push("#{self[:name].split('@')[1]}@#{self[:name].split('@')[2]}")
end
else
if destination_type == type
rval = ["#{self[:name].split('@')[1]}@#{self[:name].split('@')[2]}"]
else
rval = []
end
end
rval
end

def validate_argument(argument)
unless [Hash].include?(argument.class)
raise ArgumentError, "Invalid argument"
end
end

end
59 changes: 59 additions & 0 deletions spec/unit/puppet/provider/rabbitmq_binding/rabbitmqadmin_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
require 'puppet'
require 'mocha/api'
RSpec.configure do |config|
config.mock_with :mocha
end
provider_class = Puppet::Type.type(:rabbitmq_binding).provider(:rabbitmqadmin)
describe provider_class do
before :each do
@resource = Puppet::Type::Rabbitmq_binding.new(
{:name => 'source@target@/',
:destination_type => :queue,
:routing_key => 'blablub',
:arguments => {}
}
)
@provider = provider_class.new(@resource)
end

it 'should return instances' do
provider_class.expects(:rabbitmqctl).with('list_vhosts', '-q').returns <<-EOT
/
EOT
provider_class.expects(:rabbitmqctl).with('list_bindings', '-q', '-p', '/', 'source_name', 'destination_name', 'destination_kind', 'routing_key', 'arguments').returns <<-EOT
queue queue queue []
EOT
instances = provider_class.instances
instances.size.should == 1
end

it 'should call rabbitmqadmin to create' do
@provider.expects(:rabbitmqadmin).with('declare', 'binding', '--vhost=/', '--user=guest', '--password=guest', 'source=source', 'destination=target', 'arguments={}', 'routing_key=blablub', 'destination_type=queue')
@provider.create
end

it 'should call rabbitmqadmin to destroy' do
@provider.expects(:rabbitmqadmin).with('delete', 'binding', '--vhost=/', '--user=guest', '--password=guest', 'source=source', 'destination_type=queue', 'destination=target')
@provider.destroy
end

context 'specifying credentials' do
before :each do
@resource = Puppet::Type::Rabbitmq_binding.new(
{:name => 'source@test2@/',
:destination_type => :queue,
:routing_key => 'blablubd',
:arguments => {},
:user => 'colin',
:password => 'secret'
}
)
@provider = provider_class.new(@resource)
end

it 'should call rabbitmqadmin to create' do
@provider.expects(:rabbitmqadmin).with('declare', 'binding', '--vhost=/', '--user=colin', '--password=secret', 'source=source', 'destination=test2', 'arguments={}', 'routing_key=blablubd', 'destination_type=queue')
@provider.create
end
end
end
50 changes: 50 additions & 0 deletions spec/unit/puppet/type/rabbitmq_binding_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
require 'puppet'
require 'puppet/type/rabbitmq_binding'
describe Puppet::Type.type(:rabbitmq_binding) do
before :each do
@binding = Puppet::Type.type(:rabbitmq_binding).new(
:name => 'foo@blub@bar',
:destination_type => :queue
)
end
it 'should accept an queue name' do
@binding[:name] = 'dan@dude@pl'
@binding[:name].should == 'dan@dude@pl'
end
it 'should require a name' do
expect {
Puppet::Type.type(:rabbitmq_binding).new({})
}.to raise_error(Puppet::Error, 'Title or name must be provided')
end
it 'should not allow whitespace in the name' do
expect {
@binding[:name] = 'b r'
}.to raise_error(Puppet::Error, /Valid values match/)
end
it 'should not allow names without one @' do
expect {
@binding[:name] = 'b_r'
}.to raise_error(Puppet::Error, /Valid values match/)
end

it 'should not allow names without two @' do
expect {
@binding[:name] = 'b@r'
}.to raise_error(Puppet::Error, /Valid values match/)
end

it 'should accept an binding destination_type' do
@binding[:destination_type] = :exchange
@binding[:destination_type].should == :exchange
end

it 'should accept a user' do
@binding[:user] = :root
@binding[:user].should == :root
end

it 'should accept a password' do
@binding[:password] = :PaSsw0rD
@binding[:password].should == :PaSsw0rD
end
end

0 comments on commit d998f94

Please sign in to comment.