Skip to content

Commit b610fca

Browse files
nmaludywyardley
authored andcommitted
Fix rabbitmq_plugin to correctly detect implicitly enabled plugins
Rabbitmq_plugin now correctly detects implicitly enabled plugins to preserve idempotency regardless of plugin install order Cherry-picked from #844 Fixes #930 Signed-off-by: William Yardley <wyardley@users.noreply.github.com>
1 parent 99b08eb commit b610fca

File tree

2 files changed

+75
-17
lines changed

2 files changed

+75
-17
lines changed

lib/puppet/provider/rabbitmq_plugin/rabbitmqplugins.rb

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,30 @@
66
Puppet::Type.type(:rabbitmq_plugin).provide(:rabbitmqplugins, parent: Puppet::Provider::RabbitmqCli) do
77
confine feature: :posix
88

9-
def self.instances
10-
plugin_list = run_with_retries do
11-
rabbitmqplugins('list', '-E', '-m')
9+
def self.plugin_list
10+
list_str = run_with_retries do
11+
# Pass in -e to list both implicitly and explicitly enabled plugins.
12+
# If you pass in -E instead, then only explicitly enabled plugins are listed.
13+
# Implicitly enabled plugins are those that were enabled as a dependency of another plugin/
14+
# If we do not pass in -e then the order if plugin installation matters within the puppet
15+
# code. Example, if Plugin A depends on Plugin B and we install Plugin B first it will
16+
# implicitly enable Plugin A. Then when we go to run Puppet a second time without the
17+
# -e parameter, we won't see Plugin A as being enabled so we'll try to install it again.
18+
# To preserve idempotency we should get all enabled plugins regardless of implicitly or
19+
# explicitly enabled.
20+
rabbitmqplugins('list', '-e', '-m')
1221
end
22+
# Split by newline.
23+
lines = list_str.split(%r{\n})
24+
# Return only lines that are single words because sometimes RabbitMQ likes to output
25+
# information messages. Suppressing those messages via CLI flags is inconsistent between
26+
# versions, so this this regex removes those message without having to use painful
27+
# version switches.
28+
lines.grep(%r{^(\S+)$})
29+
end
1330

14-
plugin_list.split(%r{\n}).map do |line|
15-
next if line.start_with?('Listing plugins')
31+
def self.instances
32+
plugin_list.map do |line|
1633
raise Puppet::Error, "Cannot parse invalid plugins line: #{line}" unless line =~ %r{^(\S+)$}
1734

1835
new(name: Regexp.last_match(1))
@@ -36,6 +53,6 @@ def destroy
3653
end
3754

3855
def exists?
39-
run_with_retries { rabbitmqplugins('list', '-E', '-m') }.split(%r{\n}).include? resource[:name]
56+
self.class.plugin_list.include? resource[:name]
4057
end
4158
end

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

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,66 @@
1111
end
1212
let(:provider) { provider_class.new(resource) }
1313

14-
it 'matches plugins' do
15-
provider.expects(:rabbitmqplugins).with('list', '-E', '-m').returns("foo\n")
16-
expect(provider.exists?).to eq(true)
17-
end
18-
1914
it 'calls rabbitmqplugins to enable when node not running' do
2015
provider.class.expects(:rabbitmq_running).returns false
2116
provider.expects(:rabbitmqplugins).with('enable', 'foo')
2217
provider.create
2318
end
2419

25-
context 'with RabbitMQ version >=3.10.0' do
26-
it 'matches plugins' do
27-
provider.expects(:rabbitmqplugins).with('list', '-E', '-m').returns <<~EOT
28-
Listing plugins with pattern ".*" ...
29-
foo
30-
EOT
20+
describe '#instances' do
21+
it 'exists' do
22+
expect(provider_class).to respond_to :instances
23+
end
24+
25+
it 'retrieves instances' do
26+
provider.class.expects(:plugin_list).returns(%w[foo bar])
27+
instances = provider_class.instances
28+
instances_cmp = instances.map { |prov| { name: prov.get(:name) } }
29+
expect(instances_cmp).to eq(
30+
[
31+
{ name: 'foo' },
32+
{ name: 'bar' }
33+
]
34+
)
35+
end
36+
37+
it 'raises error on invalid line' do
38+
provider_class.expects(:plugin_list).returns([' '])
39+
expect { provider_class.instances }.to raise_error Puppet::Error, %r{Cannot parse invalid plugins line}
40+
end
41+
end
42+
43+
describe '#plugin_list' do
44+
it 'exists' do
45+
expect(provider_class).to respond_to :instances
46+
end
47+
48+
it 'returns a list of plugins' do
49+
provider.class.expects(:rabbitmqplugins).with('list', '-e', '-m').returns("foo\nbar\nbaz\n")
50+
expect(provider.class.plugin_list).to eq(%w[foo bar baz])
51+
end
52+
53+
it 'handles no training newline properly' do
54+
provider.class.expects(:rabbitmqplugins).with('list', '-e', '-m').returns("foo\nbar")
55+
expect(provider.class.plugin_list).to eq(%w[foo bar])
56+
end
57+
58+
it 'handles lines that are not plugins' do
59+
provider.class.expects(:rabbitmqplugins).with('list', '-e', '-m').returns("Listing plugins with pattern \".*\" ...\nfoo\nbar")
60+
expect(provider.class.plugin_list).to eq(%w[foo bar])
61+
end
62+
end
63+
64+
describe '#exists?' do
65+
it 'matches existing plugins' do
66+
provider_class.expects(:plugin_list).returns(%w[foo])
3167
expect(provider.exists?).to eq(true)
3268
end
69+
70+
it 'returns false for missing plugins' do
71+
provider_class.expects(:plugin_list).returns(%w[bar])
72+
expect(provider.exists?).to eq(false)
73+
end
3374
end
3475

3576
context 'with RabbitMQ version >=3.4.0' do

0 commit comments

Comments
 (0)