Sunday, June 24, 2012

Deterministic Chef template

It is very common that in chef recipe, we generate configuration file using template. Let look a simple example

template "/etc/hosts" do
source "hosts.erb"
owner "root"
group "root"
mode 0644
end

#hosts.erb

<% if @node[:hosts][:entries] %>
<% @node[:hosts][:entries].each do |ip, name| %>
<%= ip %> <%= name %>
<% end %>
<% end %>


In which we use fill data in form of hash structure with key as ip and value as hostname to the template.

The caveat here is that by using hash data structure we can potentially get different /etc/hosts each time we run chef client even though there is no change of data. This is due to no order guarantee of elements in hash data structure.

It does matter or not depending on the context. In case of /etc/hosts the order of host in that file may not be important. But a change notification triggers other action like start/stop other services, it can create un-wanted outcome. In addition there is some sort of compliance file monitoring tool like tripwire in place, it may create annoying alarms.

Anyway we can simply avoid it by create more consistent template in case of /etc/hosts, rewrite the template as following

#hosts.erb

<% if @node[:hosts][:entries] %>
<% @node[:hosts][:entries].keys.sort.each do |ip| %>
<% name = @node[:hosts][:entries][ip] %>
<%= ip %> <%= name %>
<% end %>
<% end %>

Sunday, April 8, 2012

Dump backtrace of all threads in ruby

I have created a few lines of code that allow me to dump stacktrace/backtrace of all threads of a running ruby process. If ruby support Thread#backtrace then it will print backtrace of all threads otherwise it will print of current running thread.
To use it first create a file ruby_backtrace.rb with the following content
require 'pp'

def backtrace_for_all_threads(signame)
  File.open("/tmp/ruby_backtrace_#{Process.pid}.txt","a") do |f|
      f.puts "--- got signal #{signame}, dump backtrace for all threads at #{Time.now}"
      if Thread.current.respond_to?(:backtrace)
        Thread.list.each do |t|
          f.puts t.inspect
          PP.pp(t.backtrace.delete_if {|frame| frame =~ /^#{File.expand_path(__FILE__)}/},
               f) # remove frames resulting from calling this method
        end
      else
          PP.pp(caller.delete_if {|frame| frame =~ /^#{File.expand_path(__FILE__)}/},
               f) # remove frames resulting from calling this method
      end
  end
end

Signal.trap(29) do
  backtrace_for_all_threads("INFO")
end
Then require this file to your ruby script you want to inspect e.g. t2.rb
require 'thread'
require './ruby_backtrace'

def foo
   bar
end

def bar
   sleep 100
end

thread1 = Thread.new do
   foo
end

thread2 = Thread.new do
   sleep 100
end

thread1.join
thread2.join
Finally run the script, send INFO signal to it and look at file ruby_backtrace_pid.txt, where pid is process id
$ ruby t2.rb &
[2] 4719
$ kill -29 4719
$ kill -29 4719
$ cat /tmp/ruby_backtrace_4719.txt 
--- got signal INFO, dump backtrace for all threads at 2012-04-07 17:33:14 +0200
#
["t2.rb:21:in `call'", "t2.rb:21:in `join'", "t2.rb:21:in `
'"] # ["t2.rb:9:in `bar'", "t2.rb:5:in `foo'", "t2.rb:13:in `block in
'"] # ["t2.rb:17:in `block in
'"] --- got signal INFO, dump backtrace for all threads at 2012-04-07 17:33:15 +0200 # ["t2.rb:21:in `call'", "t2.rb:21:in `join'", "t2.rb:21:in `
'"] # ["t2.rb:9:in `bar'", "t2.rb:5:in `foo'", "t2.rb:13:in `block in
'"] # ["t2.rb:17:in `block in
'"]