Skip to content

Instantly share code, notes, and snippets.

@ezmobius
Created May 11, 2011 00:56
Show Gist options
  • Save ezmobius/965708 to your computer and use it in GitHub Desktop.
Save ezmobius/965708 to your computer and use it in GitHub Desktop.
# chef library for booting a Thin server during the chef-client or chef-solo run
# that returns JSON status messages of what the recipes run is currently working on
# comes in handy if you have a small tool that pushes recipes with ssh to a server
# and runs then, you can use rest-client or something like it to poll the status
# server for status, the status server requires a shared secret token during the run
# in order to poll it and only runs while the chef client/solo run is going and dies
# when the client run is over so its not always on. Also it runs the thin server
# within the chef process rather then requiring a whole separate process.
# feel free to use this under the MIT license
require 'rack'
require 'json'
require 'pp'
module Status
class JsonWrap
attr_reader :json
def initialize(file)
@file = file
reload
reset unless @json && @json['token']
end
def save
File.open(@file, 'wb') do |f|
f.write @json.to_json
end
end
def reset
@json = {'status' => "initializing\n", 'token' => StatusServer.token}
File.open(@file, 'wb') {|f| f.write @json.to_json }
@json
end
def reload
@json = JSON.parse(IO.read(@file)) rescue nil
end
def method_missing(sym, *args, &blk)
@json.send sym, *args, &blk
end
end
class StatusServer
def self.token
@@token
end
def self.file
@@file
end
def initialize(host='0.0.0.0', port='80', token=nil)
@host, @port, @@token = host, port, token
end
def run(file='/etc/chef/status.json')
@@file = file
@db = JsonWrap.new(@@file)
@thread = Thread.new { Rack::Handler::Thin.run(::Status::StatusApp.new(@db), :Host => @host, :Port => @port) }
end
end
class StatusApp
def initialize(db)
@db = db
end
def call(env)
@db.reload
req = Rack::Request.new(env)
if StatusServer.token == req.params['token']
[200, {'Content-Type' => 'application/json'}, [{:status => @db['status']}.to_json]]
else
[401, {'Content-Type' => 'application/json'}, [{:status => 'No Token'}.to_json]]
end
end
end
class StatusUpdate
def self.update(status)
@db = JsonWrap.new(StatusServer.file)
@db['status'] << "#{status}\n"
@db.save
end
end
end
def status(status)
ruby_block status do
block do
::Status::StatusUpdate.update(status)
end
end
end
def start_status_server(host, port, token)
$status_server_thread = VCAP::StatusServer.new(host, port, token).run
r = ruby_block "setup chef recipes" do
block do
::VCAP::StatusUpdate.new('running chef recipes...')
end
action :nothing
end
r.run_action(:create)
$status_server_thread
end
def kill_status_server
$status_server_thread.kill
end
# -----------------------------------------
# Usage
# -----------------------------------------
# at the beginning of your chef run
start_status_server('0.0.0.0', '8080', node[:status_server_token])
# update status throughout your recipes:
status('Settting up something mumble mumble....')
status('Settting up something else mumble mumble....')
status('Settting up yet another mumble mumble....')
# at the end of your chef run
kill_status_server
# meanwhile during the run you can have your client
# poll with rest client or somethign else to display status of the run
msg = JSON.parse(RestClient.get("http://#{host}:#{port}/?token=#{token}"))
puts msg['status']
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment