Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Handler (experimental feature) #181

Merged
merged 23 commits into from
Dec 8, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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