Skip to content

Commit ce13eb5

Browse files
Add support for vhost metadata (#964)
1 parent 8854872 commit ce13eb5

File tree

4 files changed

+224
-18
lines changed

4 files changed

+224
-18
lines changed

REFERENCE.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2080,14 +2080,27 @@ $ puppet resource rabbitmq_vhost`
20802080

20812081
```puppet
20822082
rabbitmq_vhost { 'myvhost':
2083-
ensure => present,
2083+
ensure => present,
2084+
description => 'myvhost description',
2085+
tags => ['tag1', 'tag2'],
2086+
default_queue_type => 'quorum',
20842087
}
20852088
```
20862089

20872090
#### Properties
20882091

20892092
The following properties are available in the `rabbitmq_vhost` type.
20902093

2094+
##### `default_queue_type`
2095+
2096+
Valid values: `classic`, `quorum`, `stream`
2097+
2098+
The default queue type for queues in this vhost
2099+
2100+
##### `description`
2101+
2102+
A description of the vhost
2103+
20912104
##### `ensure`
20922105

20932106
Valid values: `present`, `absent`
@@ -2096,6 +2109,12 @@ The basic property that the resource should be in.
20962109

20972110
Default value: `present`
20982111

2112+
##### `tags`
2113+
2114+
additional tags for the vhost
2115+
2116+
Default value: `[]`
2117+
20992118
#### Parameters
21002119

21012120
The following parameters are available in the `rabbitmq_vhost` type.

lib/puppet/provider/rabbitmq_vhost/rabbitmqctl.rb

Lines changed: 88 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,104 @@
11
# frozen_string_literal: true
22

33
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'rabbitmq_cli'))
4-
Puppet::Type.type(:rabbitmq_vhost).provide(:rabbitmqctl, parent: Puppet::Provider::RabbitmqCli) do
4+
Puppet::Type.type(:rabbitmq_vhost).provide(
5+
:rabbitmqctl,
6+
parent: Puppet::Provider::RabbitmqCli
7+
) do
58
confine feature: :posix
69

7-
def self.instances
8-
vhost_list = run_with_retries do
9-
rabbitmqctl_list('vhosts')
10+
def self.prefetch(resources)
11+
instances.each do |prov|
12+
if (resource = resources[prov.name])
13+
resource.provider = prov
14+
end
1015
end
16+
end
17+
18+
# To maintain compatibility with older versions of RabbitMQ,
19+
# we only deal with vhost metadata >= version 3.11.0
20+
def self.supports_metadata?
21+
Puppet::Util::Package.versioncmp(rabbitmq_version, '3.11') >= 0
22+
end
1123

24+
def supports_metadata?
25+
self.class.supports_metadata?
26+
end
27+
28+
def self.vhost_list
29+
run_with_retries do
30+
if supports_metadata?
31+
rabbitmqctl_list('vhosts', 'name,description,default_queue_type,tags', '-s')
32+
else
33+
rabbitmqctl_list('vhosts')
34+
end
35+
end
36+
end
37+
38+
def self.instances
1239
vhost_list.split(%r{\n}).map do |line|
13-
raise Puppet::Error, "Cannot parse invalid vhost line: #{line}" unless line =~ %r{^(\S+)$}
40+
if supports_metadata?
41+
raise Puppet::Error, "Cannot parse invalid vhost line: #{line}" unless \
42+
(matches = line.match(%r{^(\S+)\t+(.*?)\t+(undefined|quorum|classic|stream)?\t+\[(.*?)\]$}i))
1443

15-
new(name: Regexp.last_match(1))
44+
name, description, default_queue_type, tags = matches.captures
45+
# RMQ returns 'undefined' as default_queue_type if it has never been set
46+
default_queue_type = nil if default_queue_type == 'undefined'
47+
new(ensure: :present, name: name, description: description, default_queue_type: default_queue_type, tags: tags.split(%r{,\s*}))
48+
else
49+
raise Puppet::Error, "Cannot parse invalid vhost line: #{line}" unless line =~ %r{^(\S+)$}
50+
51+
new(ensure: :present, name: Regexp.last_match(1))
52+
end
1653
end
1754
end
1855

1956
def create
20-
rabbitmqctl('add_vhost', resource[:name])
57+
rabbitmqctl('add_vhost', *params)
58+
end
59+
60+
def params
61+
params = [resource[:name]]
62+
if supports_metadata?
63+
params << ['--description', resource[:description]] if resource[:description]
64+
params << ['--default-queue-type', resource[:default_queue_type]] if resource[:default_queue_type] && resource[:default_queue_type] != 'undefined'
65+
params << ['--tags', resource[:tags].join(',')] if resource[:tags]
66+
end
67+
params
68+
end
69+
70+
def description
71+
@property_hash[:description]
72+
end
73+
74+
def tags
75+
@property_hash[:tags]
76+
end
77+
78+
def default_queue_type
79+
@property_hash[:default_queue_type]
80+
end
81+
82+
def tags=(tags)
83+
@property_hash[:tags] = tags
84+
end
85+
86+
def description=(value)
87+
@property_hash[:description] = value
88+
end
89+
90+
def default_queue_type=(value)
91+
@property_hash[:default_queue_type] = value
92+
end
93+
94+
def flush
95+
return if @property_hash.empty? || !supports_metadata? || !exists?
96+
97+
params = [resource[:name]]
98+
params << ['--description', @property_hash[:description]] if @property_hash[:description]
99+
params << ['--default-queue-type', @property_hash[:default_queue_type]] if @property_hash[:default_queue_type]
100+
params << ['--tags', @property_hash[:tags].join(',')] if @property_hash[:tags]
101+
rabbitmqctl('update_vhost_metadata', *params)
21102
end
22103

23104
def destroy

lib/puppet/type/rabbitmq_vhost.rb

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
1010
@example Create a rabbitmq_vhost
1111
rabbitmq_vhost { 'myvhost':
12-
ensure => present,
12+
ensure => present,
13+
description => 'myvhost description',
14+
tags => ['tag1', 'tag2'],
15+
default_queue_type => 'quorum',
1316
}
1417
DESC
1518

@@ -29,4 +32,22 @@
2932
desc 'The name of the vhost to add'
3033
newvalues(%r{^\S+$})
3134
end
35+
36+
newproperty(:description) do
37+
desc 'A description of the vhost'
38+
end
39+
40+
newproperty(:default_queue_type) do
41+
desc 'The default queue type for queues in this vhost'
42+
newvalues(:classic, :quorum, :stream)
43+
munge(&:to_s)
44+
end
45+
46+
newproperty(:tags, array_matching: :all) do
47+
desc 'additional tags for the vhost'
48+
validate do |value|
49+
raise ArgumentError, "Invalid tag: #{value.inspect}" unless value =~ %r{^\S+$}
50+
end
51+
defaultto []
52+
end
3253
end

spec/unit/puppet/provider/rabbitmq_vhost/rabbitmqctl_spec.rb

Lines changed: 94 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
# frozen_string_literal: true
22

33
require 'spec_helper'
4-
5-
provider_class = Puppet::Type.type(:rabbitmq_vhost).provider(:rabbitmqctl)
6-
describe provider_class do
4+
describe Puppet::Type.type(:rabbitmq_vhost).provider(:rabbitmqctl) do
75
let(:resource) do
86
Puppet::Type::Rabbitmq_vhost.new(
9-
name: 'foo'
7+
name: 'foo',
8+
description: 'foo description',
9+
default_queue_type: 'quorum',
10+
tags: %w[foo bar]
1011
)
1112
end
12-
let(:provider) { provider_class.new(resource) }
13+
let(:provider) { described_class.new(resource) }
1314

1415
it 'matches vhost names' do
1516
provider.expects(:rabbitmqctl_list).with('vhosts').returns <<~EOT
@@ -37,12 +38,96 @@
3738
expect(provider.exists?).to eq(false)
3839
end
3940

40-
it 'calls rabbitmqctl to create' do
41-
provider.expects(:rabbitmqctl).with('add_vhost', 'foo')
42-
provider.create
41+
context 'with RabbitMQ version <3.11.0 (no metadata support)' do
42+
it 'calls rabbitmqctl to create' do
43+
provider.expects(:supports_metadata?).at_least_once.returns false
44+
provider.expects(:rabbitmqctl).with('add_vhost', 'foo')
45+
provider.create
46+
end
47+
end
48+
49+
context 'with RabbitMQ version >=3.11.0 (metadata support)' do
50+
it 'parses vhost list with valid metadata' do
51+
provider.class.expects(:supports_metadata?).at_least_once.returns true
52+
provider.class.expects(:vhost_list).returns <<~EOT
53+
inventory classic []
54+
/ Default virtual host undefined []
55+
search quorum []
56+
testing My cool vhost undefined [tag1, tag2]
57+
EOT
58+
instances = provider.class.instances
59+
expect(instances.size).to eq(4)
60+
expect(instances.map do |prov|
61+
{
62+
name: prov.get(:name),
63+
description: prov.get(:description),
64+
default_queue_type: prov.get(:default_queue_type),
65+
tags: prov.get(:tags)
66+
}
67+
end).to eq(
68+
[
69+
{
70+
name: 'inventory',
71+
description: '',
72+
default_queue_type: 'classic',
73+
tags: []
74+
},
75+
{
76+
name: '/',
77+
description: 'Default virtual host',
78+
default_queue_type: :absent,
79+
tags: []
80+
},
81+
{
82+
name: 'search',
83+
description: '',
84+
default_queue_type: 'quorum',
85+
tags: []
86+
},
87+
{
88+
name: 'testing',
89+
description: 'My cool vhost',
90+
default_queue_type: :absent,
91+
tags: %w[tag1 tag2]
92+
}
93+
]
94+
)
95+
end
96+
97+
it 'throws error when parsing invalid vhost metadata' do
98+
provider.class.expects(:supports_metadata?).at_least_once.returns true
99+
provider.class.expects(:vhost_list).returns <<~EOT
100+
inventory undefined []
101+
/ Default virtual host undefined
102+
EOT
103+
expect { print provider.class.instances }.to raise_error(Puppet::Error, %r{Cannot parse invalid vhost line: / Default virtual host undefined})
104+
end
105+
106+
it 'calls rabbitmqctl to create with metadata' do
107+
provider.expects(:supports_metadata?).at_least_once.returns true
108+
provider.expects(:rabbitmqctl).with('add_vhost', 'foo', ['--description', 'foo description'], \
109+
['--default-queue-type', 'quorum'], ['--tags', 'foo,bar'])
110+
provider.create
111+
end
112+
113+
it 'updates tags' do
114+
provider.set(tags: %w[tag1 tag2])
115+
provider.expects(:exists?).at_least_once.returns true
116+
provider.expects(:supports_metadata?).at_least_once.returns true
117+
provider.expects(:rabbitmqctl).with('update_vhost_metadata', 'foo', ['--tags', 'tag1,tag2'])
118+
provider.flush
119+
end
120+
121+
it 'updates description' do
122+
provider.set(description: 'this is the new description')
123+
provider.expects(:exists?).at_least_once.returns true
124+
provider.expects(:supports_metadata?).at_least_once.returns true
125+
provider.expects(:rabbitmqctl).with('update_vhost_metadata', 'foo', ['--description', 'this is the new description'])
126+
provider.flush
127+
end
43128
end
44129

45-
it 'calls rabbitmqctl to create' do
130+
it 'calls rabbitmqctl to delete' do
46131
provider.expects(:rabbitmqctl).with('delete_vhost', 'foo')
47132
provider.destroy
48133
end

0 commit comments

Comments
 (0)