Skip to content

Commit

Permalink
Merge pull request #181 from itamae-kitchen/handler
Browse files Browse the repository at this point in the history
Introduce Handler (experimental feature)
  • Loading branch information
ryotarai committed Dec 8, 2015
2 parents 447e171 + e9d8428 commit 1c2ed32
Show file tree
Hide file tree
Showing 17 changed files with 367 additions and 61 deletions.
1 change: 1 addition & 0 deletions itamae.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ Gem::Specification.new do |spec|
spec.add_development_dependency "pry-byebug"
spec.add_development_dependency "docker-api", "~> 1.20"
spec.add_development_dependency "fakefs"
spec.add_development_dependency "fluent-logger"
end
2 changes: 2 additions & 0 deletions lib/itamae.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
require "itamae/cli"
require "itamae/recipe"
require "itamae/resource"
require "itamae/handler"
require "itamae/handler_proxy"
require "itamae/recipe_children"
require "itamae/logger"
require "itamae/node"
Expand Down
21 changes: 21 additions & 0 deletions lib/itamae/handler.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require 'itamae/handler/base'

module Itamae
module Handler
def self.from_type(type)
first_time = true

class_name = type.split('_').map(&:capitalize).join
self.const_get(class_name)
rescue NameError
require "itamae/handler/#{type}"

if first_time
first_time = false
retry
else
raise
end
end
end
end
40 changes: 40 additions & 0 deletions lib/itamae/handler/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
require 'socket'

module Itamae
module Handler
class Base
attr_reader :recipes, :resources, :actions

def initialize(options)
@options = options

@recipes = []
@resources = []
@actions = []
end

def event(type, payload = {})
case type
when :recipe_started
@recipes << payload
when :recipe_completed, :recipe_failed
@recipes.pop
when :resource_started
@resources << payload
when :resource_completed, :resource_failed
@resources.pop
when :action_started
@actions << payload
when :action_completed, :action_failed
@actions.pop
end
end

private

def hostname
@hostname ||= @options['hostname'] || Socket.gethostname
end
end
end
end
10 changes: 10 additions & 0 deletions lib/itamae/handler/debug.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module Itamae
module Handler
class Debug < Base
def event(type, payload = {})
super
Itamae.logger.info("EVENT:#{type} #{payload}")
end
end
end
end
44 changes: 44 additions & 0 deletions lib/itamae/handler/fluentd.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
module Itamae
module Handler
class Fluentd < Base
attr_accessor :fluent_logger # for test

def initialize(*)
super
load_fluent_logger
end

def event(type, payload = {})
super

unless @fluent_logger.post(type, payload.merge(hostname: hostname))
Itamae.logger.warn "Sending logs to Fluentd failed: #{@fluent_logger.last_error}"
end
end

private

def load_fluent_logger
begin
require 'fluent-logger'
rescue LoadError
raise "Loading fluent-logger gem failed. Please install 'fluent-logger' gem to use fluentd handler."
end

@fluent_logger = Fluent::Logger::FluentLogger.new(tag_prefix, host: fluentd_host, port: fluentd_port)
end

def tag_prefix
@options['tag_prefix'] || 'itamae_server'
end

def fluentd_host
@options['host'] || 'localhost'
end

def fluentd_port
(@options['port'] || 24224).to_i
end
end
end
end
22 changes: 22 additions & 0 deletions lib/itamae/handler/json.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module Itamae
module Handler
class Json < Base
def initialize(*)
super
require 'time'
open_file
end

def event(type, payload = {})
super
@f.puts({'time' => Time.now.iso8601, 'event' => type, 'payload' => payload}.to_json)
end

private

def open_file
@f = open(@options.fetch('path'), 'a')
end
end
end
end
38 changes: 38 additions & 0 deletions lib/itamae/handler_proxy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module Itamae
class HandlerProxy
def initialize
@instances = []
end

def register_instance(instance)
@instances << instance
end

def event(*args, &block)
if block_given?
_event_with_block(*args, &block)
else
_event(*args)
end
end

private

def _event(*args)
@instances.each do |i|
i.event(*args)
end
end

def _event_with_block(event_name, *args, &block)
event("#{event_name}_started".to_sym, *args)
block.call
rescue
event("#{event_name}_failed".to_sym, *args)
raise
else
event("#{event_name}_completed".to_sym, *args)
end
end
end

8 changes: 5 additions & 3 deletions lib/itamae/recipe.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,11 @@ def load(vars = {})
def run
show_banner

Itamae.logger.with_indent do
@children.run
run_delayed_notifications
@runner.handler.event(:recipe, path: @path) do
Itamae.logger.with_indent do
@children.run
run_delayed_notifications
end
end
end

Expand Down
106 changes: 54 additions & 52 deletions lib/itamae/resource/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,26 +121,31 @@ def initialize(recipe, resource_name, &block)
end

def run(specific_action = nil)
Itamae.logger.debug "#{resource_type}[#{resource_name}]"

Itamae.logger.with_indent_if(Itamae.logger.debug?) do
if do_not_run_because_of_only_if?
Itamae.logger.debug "#{resource_type}[#{resource_name}] Execution skipped because of only_if attribute"
return
elsif do_not_run_because_of_not_if?
Itamae.logger.debug "#{resource_type}[#{resource_name}] Execution skipped because of not_if attribute"
return
end
runner.handler.event(:resource, resource_type: resource_type, resource_name: resource_name) do
Itamae.logger.debug "#{resource_type}[#{resource_name}]"

Itamae.logger.with_indent_if(Itamae.logger.debug?) do
if do_not_run_because_of_only_if?
Itamae.logger.debug "#{resource_type}[#{resource_name}] Execution skipped because of only_if attribute"
return
elsif do_not_run_because_of_not_if?
Itamae.logger.debug "#{resource_type}[#{resource_name}] Execution skipped because of not_if attribute"
return
end

[specific_action || attributes.action].flatten.each do |action|
run_action(action)
end

[specific_action || attributes.action].flatten.each do |action|
run_action(action)
verify unless runner.dry_run?
if updated?
notify
runner.handler.event(:resource_updated)
end
end

verify unless runner.dry_run?
notify if updated?
@updated = false
end

@updated = false
rescue Backend::CommandExecutionError
Itamae.logger.error "#{resource_type}[#{resource_name}] Failed."
exit 2
Expand All @@ -151,61 +156,58 @@ def action_nothing
end

def resource_type
humps = []
self.class.name.split("::").last.each_char do |c|
if "A" <= c && c <= "Z"
humps << c.downcase
else
humps.last << c
end
end
humps.join('_')
self.class.name.split("::").last.scan(/[A-Z][^A-Z]+/).map(&:downcase).join('_')
end

private

alias_method :current, :current_attributes

def run_action(action)
original_attributes = @attributes # preserve and restore later
@current_action = action
runner.handler.event(:action, action: action) do
original_attributes = @attributes # preserve and restore later
@current_action = action

clear_current_attributes
clear_current_attributes

Itamae.logger.debug "#{resource_type}[#{resource_name}] action: #{action}"
Itamae.logger.debug "#{resource_type}[#{resource_name}] action: #{action}"

return if action == :nothing
return if action == :nothing

Itamae.logger.with_indent_if(Itamae.logger.debug?) do
Itamae.logger.debug "(in pre_action)"
pre_action
Itamae.logger.with_indent_if(Itamae.logger.debug?) do
Itamae.logger.debug "(in pre_action)"
pre_action

Itamae.logger.debug "(in set_current_attributes)"
set_current_attributes
Itamae.logger.debug "(in set_current_attributes)"
set_current_attributes

Itamae.logger.debug "(in show_differences)"
show_differences
Itamae.logger.debug "(in show_differences)"
show_differences

method_name = "action_#{action}"
if runner.dry_run?
unless respond_to?(method_name)
Itamae.logger.error "action #{action.inspect} is unavailable"
end
else
args = [method_name]
if method(method_name).arity == 1
# for plugin compatibility
args << runner.options
method_name = "action_#{action}"
if runner.dry_run?
unless respond_to?(method_name)
Itamae.logger.error "action #{action.inspect} is unavailable"
end
else
args = [method_name]
if method(method_name).arity == 1
# for plugin compatibility
args << runner.options
end

public_send(*args)
end

public_send(*args)
if different?
updated!
runner.handler.event(:attribute_changed, from: @current_attributes, to: @attributes)
end
end

updated! if different?
@current_action = nil
@attributes = original_attributes
end

@current_action = nil
@attributes = original_attributes
end

def clear_current_attributes
Expand Down
15 changes: 11 additions & 4 deletions lib/itamae/resource/file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ def show_differences

super

if current.exist && @temppath
show_file_diff
if @temppath
compare_file
end
end

Expand Down Expand Up @@ -113,8 +113,14 @@ def action_edit(options)

private

def show_file_diff
diff = run_command(["diff", "-u", attributes.path, @temppath], error: false)
def compare_file
compare_to = if current.exist
attributes.path
else
'/dev/null'
end

diff = run_command(["diff", "-u", compare_to, @temppath], error: false)
if diff.exit_status == 0
# no change
Itamae.logger.debug "file content will not change"
Expand All @@ -132,6 +138,7 @@ def show_file_diff
Itamae.logger.info line.chomp
end
end
runner.handler.event(:file_content_changed, diff: diff.stdout)
end
end

Expand Down
Loading

0 comments on commit 1c2ed32

Please sign in to comment.