diff options
-rw-r--r-- | lib/chef.rb | 2 | ||||
-rw-r--r-- | lib/chef/chef_class.rb | 7 | ||||
-rw-r--r-- | lib/chef/event_dispatch/dsl.rb | 64 | ||||
-rw-r--r-- | lib/chef/exceptions.rb | 1 | ||||
-rw-r--r-- | spec/unit/chef_class_spec.rb | 23 | ||||
-rw-r--r-- | spec/unit/event_dispatch/dsl_spec.rb | 87 |
6 files changed, 183 insertions, 1 deletions
diff --git a/lib/chef.rb b/lib/chef.rb index 6bce976439..1a0b802adb 100644 --- a/lib/chef.rb +++ b/lib/chef.rb @@ -31,5 +31,5 @@ require 'chef/daemon' require 'chef/run_status' require 'chef/handler' require 'chef/handler/json_file' - +require 'chef/event_dispatch/dsl' require 'chef/chef_class' diff --git a/lib/chef/chef_class.rb b/lib/chef/chef_class.rb index 5cf4f95af5..458ac82467 100644 --- a/lib/chef/chef_class.rb +++ b/lib/chef/chef_class.rb @@ -52,7 +52,14 @@ class Chef # attr_reader :run_context + # Register an event handler with user specified block # + # @return[Chef::EventDispatch::Base] handler object + def event_handler(&block) + dsl = Chef::EventDispatch::DSL.new('Chef client DSL') + dsl.instance_eval(&block) + end + # Get the array of providers associated with a resource_name for the current node # # @param resource_name [Symbol] name of the resource as a symbol diff --git a/lib/chef/event_dispatch/dsl.rb b/lib/chef/event_dispatch/dsl.rb new file mode 100644 index 0000000000..c6f21c9b45 --- /dev/null +++ b/lib/chef/event_dispatch/dsl.rb @@ -0,0 +1,64 @@ +# +# Author:: Ranjib Dey (<ranjib@linux.com>) +# Copyright:: Copyright (c) 2015 Ranjib Dey +# License:: Apache License, Version 2.0 +# +# 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 +# +# http://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 'chef/event_dispatch/base' +require 'chef/exceptions' +require 'chef/config' + +class Chef + module EventDispatch + class DSL + attr_reader :handler + + def initialize(name) + klass = Class.new(Chef::EventDispatch::Base) do + attr_reader :name + end + @handler = klass.new + @handler.instance_variable_set(:@name, name) + + # Use event.register API to add anonymous handler if Chef.run_context + # and associated event dispatcher is set, else fallback to + # Chef::Config[:hanlder] + if Chef.run_context && Chef.run_context.events + Chef::Log.debug("Registering handler '#{name}' using events api") + Chef.run_context.events.register(handler) + else + Chef::Log.debug("Registering handler '#{name}' using global config") + Chef::Config[:event_handlers] << handler + end + end + + # Adds a new event handler derived from base handler + # with user defined block against a chef event + # + # @return [Chef::EventDispatch::Base] a base handler object + def on(event_type, &block) + validate!(event_type) + handler.define_singleton_method(event_type) do |*args| + instance_exec(*args, &block) + end + end + + private + def validate!(event_type) + all_event_types = (Chef::EventDispatch::Base.instance_methods - Object.instance_methods) + raise Chef::Exceptions::InvalidEventType, "Invalid event type: #{event_type}" unless all_event_types.include?(event_type) + end + end + end +end diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb index d5c4c4832b..e2e36e8162 100644 --- a/lib/chef/exceptions.rb +++ b/lib/chef/exceptions.rb @@ -103,6 +103,7 @@ class Chef class ProviderNotFound < RuntimeError; end NoProviderAvailable = ProviderNotFound class VerificationNotFound < RuntimeError; end + class InvalidEventType < ArgumentError; end class MultipleIdentityError < RuntimeError; end # Can't find a Resource of this type that is valid on this platform. diff --git a/spec/unit/chef_class_spec.rb b/spec/unit/chef_class_spec.rb index 2528246be6..d6bf74572e 100644 --- a/spec/unit/chef_class_spec.rb +++ b/spec/unit/chef_class_spec.rb @@ -88,4 +88,27 @@ describe "Chef class" do expect(Chef.node).to eql(node) end end + + context '#event_handler' do + it 'adds a new handler' do + x = 1 + Chef.event_handler do + on :converge_start do + x = 2 + end + end + expect(Chef::Config[:event_handlers]).to_not be_empty + Chef::Config[:event_handlers].first.send(:converge_start) + expect(x).to eq(2) + end + + it 'raise error if unknown event type is passed' do + expect do + Chef.event_handler do + on :yolo do + end + end + end.to raise_error(Chef::Exceptions::InvalidEventType) + end + end end diff --git a/spec/unit/event_dispatch/dsl_spec.rb b/spec/unit/event_dispatch/dsl_spec.rb new file mode 100644 index 0000000000..f467ea81ea --- /dev/null +++ b/spec/unit/event_dispatch/dsl_spec.rb @@ -0,0 +1,87 @@ +# +# Author:: Ranjib Dey (<ranjib@linux.com>) +# +# Copyright:: Copyright (c) 2015 Ranjib Dey +# License:: Apache License, Version 2.0 +# +# 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 +# +# http://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 'spec_helper' +require 'chef/event_dispatch/dsl' + +describe Chef::EventDispatch::DSL do + let(:events) do + Chef::EventDispatch::Dispatcher.new + end + + let(:run_context) do + Chef::RunContext.new(Chef::Node.new, nil, events) + end + + before do + Chef.set_run_context(run_context) + end + + after do + Chef.reset! + end + + subject{ described_class.new('test') } + + it 'set handler name' do + subject.on(:run_started) {} + expect(events.subscribers.first.name).to eq('test') + end + + it 'raise error when invalid event type is supplied' do + expect do + subject.on(:foo_bar) {} + end.to raise_error(Chef::Exceptions::InvalidEventType) + end + + it 'register user hooks against valid event type' do + subject.on(:run_failed) {'testhook'} + expect(events.subscribers.first.run_failed).to eq('testhook') + end + + it 'preserve state across event hooks' do + calls = [] + Chef.event_handler do + on :resource_updated do + calls << :updated + end + on :resource_action_start do + calls << :started + end + end + resource = Chef::Resource::RubyBlock.new('foo', run_context) + resource.block { } + resource.run_action(:run) + expect(calls).to eq([:started, :updated]) + end + + it 'preserve instance variables across handler callbacks' do + Chef.event_handler do + on :resource_action_start do + @ivar = [1] + end + on :resource_updated do + @ivar << 2 + end + end + resource = Chef::Resource::RubyBlock.new('foo', run_context) + resource.block { } + resource.run_action(:run) + expect(events.subscribers.first.instance_variable_get(:@ivar)).to eq([1, 2]) + end +end |