summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRanjib Dey <dey.ranjib@gmail.com>2015-07-16 09:39:02 -0700
committerRanjib Dey <dey.ranjib@gmail.com>2015-07-16 09:39:02 -0700
commit339dbb2d05bee1a0525388941ca2409be8f62ce1 (patch)
tree3cce812a51d371c432bce02b140b96ce5389c1d5
parent9a3e6e04f3bb39c2b2f5749719f0c21dd3f3f2ec (diff)
parentfce939cbafe91eaade275d44c4e1cc5bfa724b2c (diff)
downloadchef-339dbb2d05bee1a0525388941ca2409be8f62ce1.tar.gz
Merge pull request #3242 from ranjib/chef_handler
[RFC-039] chef handler dsl
-rw-r--r--lib/chef.rb2
-rw-r--r--lib/chef/chef_class.rb7
-rw-r--r--lib/chef/event_dispatch/dsl.rb64
-rw-r--r--lib/chef/exceptions.rb1
-rw-r--r--spec/unit/chef_class_spec.rb23
-rw-r--r--spec/unit/event_dispatch/dsl_spec.rb87
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