diff --git a/README.md b/README.md index 9dd450c3..3dab89a7 100644 --- a/README.md +++ b/README.md @@ -515,14 +515,13 @@ Sets or clears a RabbitMQ [policy](https://www.rabbitmq.com/parameters.html#poli - `:set` sets a `policy` - `:clear` clears a `policy` -- `:list` lists `policy`s ### Examples ``` ruby rabbitmq_policy "queue-length-limit" do pattern "^limited\\.*" - parameters ({"max-length" => "3000"}) + definition ({"max-length" => "3000"}) priority 1 action :set end diff --git a/libraries/default.rb b/libraries/default.rb index 69ec0bc5..48663ccf 100644 --- a/libraries/default.rb +++ b/libraries/default.rb @@ -40,6 +40,17 @@ def format_kernel_parameters # rubocop:disable all rendered.join(",\n") end + def get_policy(name, vhost) + # Returns JSON of a policy in a vhost + # Returns false if the policy does not exist + cmd = "rabbitmqctl list_policies --vhost #{Shellwords.escape vhost} --formatter json" + cmd = Mixlib::ShellOut.new(cmd, env: shell_environment).run_command + pol = JSON.parse(cmd.stdout).select { |p| p['name'] == name }.first + return false unless pol + pol['definition'] = JSON.parse(pol['definition']) unless pol['definition'].is_a?(Hash) + pol + end + def format_ssl_versions Array(node['rabbitmq']['ssl_versions']).map { |n| "'#{n}'" }.join(',') end diff --git a/providers/erlang_package_from_cloudsmith.rb b/providers/erlang_package_from_cloudsmith.rb index bc55710d..d9e71f61 100644 --- a/providers/erlang_package_from_cloudsmith.rb +++ b/providers/erlang_package_from_cloudsmith.rb @@ -53,12 +53,9 @@ retries new_resource.retries retry_delay new_resource.retry_delay unless new_resource.retry_delay.nil? action :install + notifies :reload, 'ohai[reload_packages]', :immediately end end - - ohai 'reload' do - action :reload - end end if platform_family?('rhel', 'fedora', 'amazon') diff --git a/providers/policy.rb b/providers/policy.rb deleted file mode 100644 index 7e6a4554..00000000 --- a/providers/policy.rb +++ /dev/null @@ -1,100 +0,0 @@ -# frozen_string_literal: true -# -# Cookbook Name:: rabbitmq -# Provider:: policy -# -# Author: Robert Choi -# Copyright 2013 by Robert Choi -# Copyright 2019-2021, VMware, Inc or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -require 'shellwords' - -include RabbitMQ::CoreHelpers - -def policy_exists?(vhost, name) - cmd = if Gem::Version.new(installed_rabbitmq_version) >= Gem::Version.new('3.7.10') - 'rabbitmqctl list_policies -s' - else - 'rabbitmqctl list_policies -q' - end - cmd += " -p #{Shellwords.escape vhost}" unless vhost.nil? - cmd += " |grep '#{name}\\b'" - - cmd = Mixlib::ShellOut.new(cmd, :env => shell_environment) - cmd.run_command - begin - cmd.error! - true - rescue - false - end -end - -action :set do - cmd = 'rabbitmqctl -q set_policy' - cmd += " -p #{new_resource.vhost}" unless new_resource.vhost.nil? - cmd += " --apply-to #{new_resource.apply_to}" if new_resource.apply_to - cmd += " #{new_resource.policy}" - cmd += " \"#{new_resource.pattern}\"" - cmd += " '{" - - first_param = true - new_resource.parameters.each do |key, value| - cmd += ',' unless first_param - - cmd += if value.is_a? String - "\"#{key}\":\"#{value}\"" - else - "\"#{key}\":#{value}" - end - first_param = false - end - - cmd += "}'" - cmd += " --priority #{new_resource.priority}" if new_resource.priority - - execute "set_policy #{new_resource.policy}" do - command cmd - environment shell_environment - end - - new_resource.updated_by_last_action(true) - Chef::Log.info "Done setting RabbitMQ policy '#{new_resource.policy}'." -end - -action :clear do - if policy_exists?(new_resource.vhost, new_resource.policy) - cmd = "rabbitmqctl clear_policy #{new_resource.policy}" - cmd += " -p #{new_resource.vhost}" unless new_resource.vhost.nil? - execute "clear_policy #{new_resource.policy}" do - command cmd - environment shell_environment - end - - new_resource.updated_by_last_action(true) - Chef::Log.info "Done clearing RabbitMQ policy '#{new_resource.policy}'." - end -end - -action :list do - execute 'list_policies' do - cmd = 'rabbitmqctl list_parameters -q' - command cmd - environment shell_environment - end - - new_resource.updated_by_last_action(true) -end diff --git a/recipes/policies.rb b/recipes/policies.rb index 2c0bcb34..f651d4be 100644 --- a/recipes/policies.rb +++ b/recipes/policies.rb @@ -26,7 +26,7 @@ node['rabbitmq']['policies'].each do |name, policy| rabbitmq_policy name do pattern policy['pattern'] - parameters policy['params'] + definition policy['params'] priority policy['priority'] vhost policy['vhost'] apply_to policy['apply_to'] diff --git a/resources/policy.rb b/resources/policy.rb index e95cba00..f1baacf0 100644 --- a/resources/policy.rb +++ b/resources/policy.rb @@ -20,14 +20,63 @@ # limitations under the License. # +include RabbitMQ::CoreHelpers + unified_mode true if respond_to?(:unified_mode) -actions :set, :clear, :list default_action :set -attribute :policy, :kind_of => String, :name_attribute => true -attribute :pattern, :kind_of => String -attribute :parameters, :kind_of => Hash -attribute :priority, :kind_of => Integer -attribute :vhost, :kind_of => String -attribute :apply_to, :kind_of => String, :equal_to => %w(all queues exchanges) +property :policy, String, name_property: true +property :pattern, String +property :definition, Hash +property :priority, Integer, default: 0 +property :apply_to, String, :equal_to => %w(all queues exchanges), default: 'all' +property :vhost, String, default: '/' + +deprecated_property_alias 'parameters', + 'definition', + 'The \"parameters\" property has been renamed \"definition\". '\ + 'Please update your cookbooks to use the new property name.' + +load_current_value do |new_resource| + p = get_policy(new_resource.policy, new_resource.vhost) + + current_value_does_not_exist! unless p + + pattern p['pattern'] + definition p['definition'] + apply_to p['apply-to'] + priority p['priority'] +end + +action :set do + # These properties are only required for the :set action + [:pattern, :definition].each do |prop| + raise( + Chef::Exceptions::ValidationFailed, + "#{prop} is a required property" + ) unless property_is_set?(prop) + end + + converge_if_changed do + cmd = "rabbitmqctl -q set_policy -p #{new_resource.vhost}" + cmd += " --apply-to #{new_resource.apply_to}" + cmd += " #{new_resource.policy}" + cmd += " \"#{new_resource.pattern}\"" + cmd += " '#{new_resource.definition.to_json}'" + cmd += " --priority #{new_resource.priority}" + + execute "set_policy #{new_resource.policy} on vhost #{new_resource.vhost}" do + command cmd + environment shell_environment + end + end +end + +action :clear do + execute "clear_policy #{new_resource.policy} from vhost #{new_resource.vhost}" do + command "rabbitmqctl clear_policy #{new_resource.policy} -p #{new_resource.vhost}" + environment shell_environment + only_if { get_policy(new_resource.policy, new_resource.vhost) } + end +end diff --git a/spec/policy_spec.rb b/spec/policy_spec.rb new file mode 100644 index 00000000..5f0fa2f7 --- /dev/null +++ b/spec/policy_spec.rb @@ -0,0 +1,108 @@ +require 'spec_helper' + +describe 'rabbitmq_policy' do + step_into :rabbitmq_policy + + platform 'ubuntu' + + context 'when policies do not exist' do + before do + shellout = double + allow(Mixlib::ShellOut).to receive(:new).with( + /rabbitmqctl list_policies/, {env: { 'HOME' => // } } + ).and_return(shellout) + allow(shellout).to receive(:run_command).and_return(shellout) + allow(shellout).to receive(:stdout).and_return('[]') + end + + recipe do + rabbitmq_policy 'potato' do + pattern '^(?!amq\\.).*' + definition( + 'ha-mode' => 'exactly', + 'ha-params' => 2, + 'ha-sync-mode' => 'automatic' + ) + action :set + end + + rabbitmq_policy 'tomato' do + action :clear + end + end + + it 'sets the policy' do + is_expected.to run_execute('set_policy potato on vhost /').with( + command: 'rabbitmqctl -q set_policy -p / --apply-to all potato "^(?!amq\\.).*" '\ + '\'{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}\' --priority 0' + ) + is_expected.to_not run_execute('clear_policy tomato from vhost /') + end + end + + context 'when policies already exist' do + before do + shellout = double + allow(Mixlib::ShellOut).to receive(:new).with( + /rabbitmqctl list_policies/, {env: { 'HOME' => // } } + ).and_return(shellout) + allow(shellout).to receive(:run_command).and_return(shellout) + allow(shellout).to receive(:stdout).and_return( + '[ + { + "name":"policy1", + "pattern":"pattern1", + "definition":{"key1":"val1"}, + "apply-to":"all", + "priority":0 + }, + { + "name":"policy2", + "pattern":"pattern2", + "definition":{"key2":"val2"}, + "apply-to":"queues", + "priority":1 + }, + { + "name":"policy3", + "pattern":"pattern3", + "definition":{"key3":"val3"}, + "apply-to":"queues", + "priority":2 + } + ]' + ) + end + + recipe do + rabbitmq_policy 'policy1' do + pattern 'pattern1' + definition('key1' => 'val1', 'extrakey' => 'extraval') + apply_to 'all' + priority 0 + action :set + end + + rabbitmq_policy 'policy2' do + pattern 'pattern2' + definition('key2' => 'val2') + apply_to 'queues' + priority 1 + action :set + end + + rabbitmq_policy 'policy3' do + action :clear + end + end + + it 'amends only the policies that have changed' do + is_expected.to run_execute('set_policy policy1 on vhost /').with( + command: 'rabbitmqctl -q set_policy -p / --apply-to all policy1 "pattern1" '\ + '\'{"key1":"val1","extrakey":"extraval"}\' --priority 0' + ) + is_expected.to_not run_execute('set_policy policy2 on vhost /') + is_expected.to run_execute('clear_policy policy3 from vhost /') + end + end +end diff --git a/test/cookbooks/rabbitmq_test/recipes/lwrps.rb b/test/cookbooks/rabbitmq_test/recipes/lwrps.rb index 57f1fd49..ff40d490 100644 --- a/test/cookbooks/rabbitmq_test/recipes/lwrps.rb +++ b/test/cookbooks/rabbitmq_test/recipes/lwrps.rb @@ -72,7 +72,7 @@ rabbitmq_policy 'queue_length_limit' do pattern 'limited.*' - parameters 'max-length' => 1000 + definition 'max-length' => 1000 apply_to 'queues' action :set end