Commit 7d1ba00d authored by John E. Vincent's avatar John E. Vincent

CLI work

parent f761c3cd
...@@ -21,4 +21,5 @@ platforms :jruby do ...@@ -21,4 +21,5 @@ platforms :jruby do
gem "json-jruby", "= 1.4.6", :require => "json" gem "json-jruby", "= 1.4.6", :require => "json"
gem "warbler", "= 1.2.1" gem "warbler", "= 1.2.1"
gem "jruby-openssl", "= 0.7.3" gem "jruby-openssl", "= 0.7.3"
gem "rack-jetty", "= 0.2.0"
end end
#!/usr/bin/env ruby #!/usr/bin/env ruby
require File.join(File.dirname(__FILE__), './../noah') $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
require 'noah/cli'
NoahApp.run! n = Noah::CLI.new
n.parse_options
#NoahApp.run!
require 'sinatra/base'
require 'sinatra/namespace'
require 'ohm'
require 'ohm/contrib'
begin
require 'yajl'
rescue LoadError
require 'json'
end
require 'haml'
require 'yaml'
require 'noah/config'
require 'noah/helpers'
require 'noah/models'
require 'noah/app'
@db_settings = YAML::load File.new(File.join(File.dirname(__FILE__),'config','db.yml')).read
class NoahApp < Sinatra::Base
register Sinatra::Namespace
helpers Sinatra::NoahHelpers
config_file = YAML::load File.new(File.join(File.dirname(__FILE__),'config','db.yml')).read
db = config_file["#{environment}"]
begin
Ohm.connect(:url => "redis://#{db["host"]}:#{db["port"]}/#{db["db"]}")
Ohm.redis.ping
rescue Errno::ECONNREFUSED => e
puts "Unable to connect to Redis. Shutting down...."
puts e.message
exit 1
end
configure do
set :app_file, __FILE__
set :root, File.dirname(__FILE__)
set :server, %w[thin mongrel webrick Jetty]
set :port, 9291
set :logging, true
set :raise_errors, false
set :show_exceptions, false
log = File.new("logs/noah.log", "a")
STDOUT.reopen(log)
STDERR.reopen(log)
end
configure(:development) do
require 'sinatra/reloader'
register Sinatra::Reloader
also_reload "models.rb"
also_reload "helpers.rb"
set :port, 9292
end
configure(:test) do
set :port, 9294
end
get '/' do
content_type "text/html"
haml :index, :format => :html5
end
before do
content_type "application/json"
end
not_found do
content_type "application/json"
erb :'404'
end
error do
content_type "application/json"
erb :'500'
end
namespace "/h" do
get '/:hostname/:servicename/?' do |hostname, servicename|
h = host_service(hostname, servicename)
if h.nil?
halt 404
else
h.to_json
end
end
get '/:hostname/?' do |hostname|
h = host(:name => hostname)
if h.nil?
halt 404
else
h.to_json
end
end
get '/?' do
hosts.map {|h| h.to_hash}
if hosts.size == 0
halt 404
else
hosts.to_json
end
end
put '/:hostname/?' do |hostname|
required_params = ["name", "status"]
data = JSON.parse(request.body.read)
(data.keys.sort == required_params.sort && data['name'] == hostname) ? (host = Host.find_or_create(:name => data['name'], :status => data['status'])) : (raise "Missing Parameters")
if host.valid?
r = {"result" => "success","id" => "#{host.id}","status" => "#{host.status}", "name" => "#{host.name}", "new_record" => host.is_new?}
r.to_json
else
raise "#{host.errors}"
end
end
delete '/:hostname/?' do |hostname|
host = Host.find(:name => hostname).first
if host
services = []
Service.find(:host_id => host.id).sort.each {|x| services << x; x.delete} if host.services.size > 0
host.delete
r = {"result" => "success", "id" => "#{host.id}", "name" => "#{hostname}", "service_count" => "#{services.size}"}
r.to_json
else
halt 404
end
end
end
namespace "/s" do
get '/:servicename/:hostname/?' do |servicename, hostname|
hs = host_service(hostname, servicename)
if hs.nil?
halt 404
else
hs.to_json
end
end
get '/:servicename/?' do |servicename|
s = services(:name => servicename)
s.map {|x| x.to_hash}
if s.empty?
halt 404
else
s.to_json
end
end
get '/?' do
if services.empty?
halt 404
else
services.map {|s| s.to_hash}
services.to_json
end
end
put '/:servicename/?' do |servicename|
required_params = ["status", "host", "name"]
data = JSON.parse(request.body.read)
if data.keys.sort == required_params.sort
h = Host.find(:name => data['host']).first || (raise "Invalid Host")
service = Service.find_or_create(:name => servicename, :status => data['status'], :host => h)
if service.valid?
action = service.is_new? ? "create" : "update"
service.save
r = {"action" => action, "result" => "success", "id" => service.id, "host" => h.name, "name" => service.name}
r.to_json
else
raise "#{service.errors}"
end
else
raise "Missing Parameters"
end
end
delete '/:servicename/:hostname/?' do |servicename, hostname|
host = Host.find(:name => hostname).first || (halt 404)
service = Service.find(:name => servicename, :host_id => host.id).first || (halt 404)
if host && service
service.delete
r = {"action" => "delete", "result" => "success", "id" => service.id, "host" => host.name, "service" => servicename}
r.to_json
else
halt 404
end
end
end
namespace "/a" do
get '/:appname/:config/?' do |appname, config|
app = Application.find(:name => appname).first
if app.nil?
halt 404
else
c = Configuration.find(:name => config, :application_id => app.id).first
c.to_json
end
end
get '/:appname/?' do |appname|
app = Application.find(:name => appname).first
if app.nil?
halt 404
else
app.to_json
end
end
put '/:appname/?' do |appname|
required_params = ["name"]
data = JSON.parse(request.body.read)
if data.keys.sort == required_params.sort && data['name'] == appname
app = Application.find_or_create(:name => appname)
else
raise "Missing Parameters"
end
if app.valid?
action = app.is_new? ? "create" : "update"
app.save
r = {"result" => "success","id" => app.id, "action" => action, "name" => app.name }
r.to_json
else
raise "#{app.errors}"
end
end
delete '/:appname/?' do |appname|
app = Application.find(:name => appname).first
if app.nil?
halt 404
else
configurations = []
Configuration.find(:application_id => app.id).sort.each {|x| configurations << x; x.delete} if app.configurations.size > 0
app.delete
r = {"result" => "success", "action" => "delete", "id" => "#{app.id}", "name" => "#{appname}", "configurations" => "#{configurations.size}"}
r.to_json
end
end
get '/?' do
apps = []
Application.all.sort.each {|a| apps << a.to_hash}
if apps.empty?
halt 404
else
apps.to_json
end
end
end
namespace '/c' do
# Need to move this out to configuration.
# Maybe bootstrap them from itself?
content_type_mapping = {
:yaml => "text/x-yaml",
:json => "application/json",
:xml => "text/xml",
:string => "text/plain"
}
get '/:appname/:element/?' do |appname, element|
a = Application.find(:name => appname).first
if a.nil?
halt 404
else
c = Configuration.find(:name => element, :application_id => a.id).first
content_type content_type_mapping[c.format.to_sym] if content_type_mapping[c.format.to_sym]
c.body
end
end
get '/:appname/?' do |appname|
config = []
a = Application.find(:name => appname).first
if a.nil?
halt 404
else
Configuration.find(:application_id => a.id).sort.each {|c| config << c.to_hash}
config.to_json
end
end
get '/?' do
configs = []
Configuration.all.sort.each {|c| configs << c.to_hash}
if configs.empty?
halt 404
else
configs.to_json
end
end
put '/:appname/:element?' do |appname, element|
app = Application.find_or_create(:name => appname)
config = Configuration.find_or_create(:name => element, :application_id => app.id)
required_params = ["format", "body"]
data = JSON.parse(request.body.read)
data.keys.sort == required_params.sort ? (config.format = data["format"]; config.body = data["body"]) : (raise "Missing Parameters")
if config.valid?
config.save
action = config.is_new? ? "create" : "update"
dependency_action = app.is_new? ? "created" : "updated"
r = {"result" => "success","id" => "#{config.id}", "action" => action, "dependencies" => dependency_action, "application" => app.name, "item" => config.name}
r.to_json
else
raise "#{config.errors}"
end
end
delete '/:appname/:element?' do |appname, element|
app = Application.find(:name => appname).first
if app
config = Configuration.find(:name=> element, :application_id => app.id).first
if config
config.delete
r = {"result" => "success", "id" => "#{config.id}", "action" => "delete", "application" => "#{app.name}", "item" => "#{element}"}
r.to_json
else
halt 404
end
else
halt 404
end
end
end
run! if app_file == $0
end
module Noah
class Application < Ohm::Model
include Ohm::Typecast
include Ohm::Timestamping
include Ohm::Callbacks
include Ohm::ExtraValidations
attribute :name
collection :configurations, Configuration
index :name
def validate
assert_present :name
assert_unique :name
end
def to_hash
arr = []
configurations.sort.each {|c| arr << c.to_hash}
super.merge(:name => name, :updated_at => updated_at, :configurations => arr)
end
def is_new?
self.created_at == self.updated_at
end
class << self
def find_or_create(opts = {})
begin
find(opts).first.nil? ? (app = create(opts)) : (app = find(opts).first)
if app.valid?
app.save
end
app
rescue Exception => e
e.message
end
end
end
end
class Applications
def self.all(options = {})
options.empty? ? Application.all.sort : Application.find(options).sort
end
end
end
require 'mixlib/cli'
module Noah
class CLI
include Mixlib::CLI
banner "noah (options)"
option :config,
:short => "-c CONFIG",
:long => "--config CONFIG",
:default => "config/config.yml",
:description => "Configuration file with path"
option :log_level,
:short => "-l LEVEL",
:long => "--log-level LEVEL",
:description => "Set the log level (debug, info, warn, error, fatal)",
:default => "info",
:proc => Proc.new { |l| l.to_sym }
option :port,
:short => "-p PORT",
:long => "--port PORT",
:description => "Port number to listen on",
:default => "9292"
option :rack_opts,
:short => "-r RACK_HANDLER",
:long => "--rack RACK_HANDLER",
:description => "Rack Handler to use. i.e. thin, mongrel, webbrick, Jetty (for JRuby)",
:default => "thin"
option :redis_url,
:short => "-u REDIS_URL",
:long => "--url REDIS_URL",
:description => "Ohm-compatibile redis url. i.e. redis://localhost:6379/0",
:default => "redis://lcoalhost:6379/0",
:proc => Proc.new { |u| begin; require 'ohm'; Ohm.connect(:url => u); Ohm.redis.ping; rescue Errno::ECONNREFUSED => e; puts "Unable to connect to Redis. Not starting."; exit 1; end }
option :help,
:short => "-h",
:long => "--help",
:description => "You're looking at it",
:on => :tail,
:boolean => true,
:show_options => true,
:exit => 0
end
end
module Noah module Noah
module Config
@conf ||= "config/config.yml" class Config
# attr_accessor :config_file
# attr_accessor :settings
# attr_accessor :log_file
# attr_accessor :log_level
# attr_accessor :mode
# attr_accessor :database_url
#
# @conf ||= "config/config.yml"
# @logfile ||= "logs/noah.log"
# @log_level ||= "debug"
# @mode ||= "development"
# @database_url ||= "redis://localhost:6379/0"
#
# def self.configured?
# @config_file.nil? ? false : true
# end
#
# def self.configure!
# puts "Configuring Noah from: #{@config_file}"
# parse
# end
#
# private
# def parse(conf_file = @congig_file)
# begin
# @settings = YAML.load_file(conf_file)
# rescue Exception => e
# puts "Unable to parse configuration: #{conf_file}"
# puts e.message
# end
# end
# end
def self.config_file def initialize(data = {})
@conf @data = {}
update!(data)
end end
def self.config_file=(config) def update!(data)
@conf = config data.each do |k,v|
self[k] = v
end
end end
def self.configured? def []=(k,v)
@conf.nil? ? false : true v.class == Hash ? @data[k.to_sym] = Config.new(v) : @data[k.to_sym] = v
end end
def self.configure! def configured?
puts "Configuring Noah from: #{@conf}" @data.size == 0 ? false : true
end end
def method_missing(sym, *args)
if sym.to_s =~ /(.+)=$/
self[$1] = args.first
else
self[sym]
end
end
end end
end
end
module Noah
class Configuration < Ohm::Model
include Ohm::Typecast
include Ohm::Timestamping
include Ohm::Callbacks
include Ohm::ExtraValidations
attribute :name
attribute :format
attribute :body
attribute :new_record
reference :application, Application
index :name
def validate
assert_present :name
assert_present :format
assert_present :body
assert_unique [:name, :application_id]
end
def to_hash
super.merge(:name => name, :format => format, :body => body, :update_at => updated_at, :application => Application[application_id].name)
end
def is_new?
self.created_at == self.updated_at
end
class << self
def find_or_create(opts={})
begin
if find(opts).first.nil?
conf = create(opts)
else
conf = find(opts).first
end
rescue Exception => e
e.message
end
end
end
end
class Configurations
def self.all(options = {})
options.empty? ? Configuration.all.sort : Configuration.find(options).sort
end
end
end
module Noah module Noah
module Helpers module SinatraHelpers
def host(opts = {}) def host(opts = {})
Noah::Models::Host.find(opts).first Noah::Models::Host.find(opts).first
...@@ -51,5 +51,5 @@ module Noah ...@@ -51,5 +51,5 @@ module Noah
Configurations.all(opts) Configurations.all(opts)
end end
end end
helpers NoahHelpers
end end
module Noah
class Host < Ohm::Model
include Ohm::Typecast
include Ohm::Timestamping
include Ohm::Callbacks
include Ohm::ExtraValidations
attribute :name
attribute :status
collection :services, Service
index :name
index :status
def validate
assert_present :name
assert_present :status
assert_unique :name
assert_member :status, ["up","down","pending"]
end
def to_hash
arr = []
services.sort.each {|s| arr << s.to_hash}
h = {:name => name, :status => status, :created_at => created_at, :updated_at => updated_at, :services => arr}
super.merge(h)
end
def is_new?
self.created_at == self.updated_at
end
class << self
def find_or_create(opts = {})
begin
# exclude requested status from lookup
h = find(opts.reject{|key,value| key == :status}).first
host = h.nil? ? create(opts) : h
host.status = opts[:status]
if host.valid?
host.save
end
host
rescue Exception => e
e.message
end
end
end
end
class Hosts
def self.all(options = {})
options.empty? ? Host.all.sort : Host.find(options).sort
end
end
end
require 'ohm/contrib' require 'hosts'
module Noah require 'services'
module Models require 'applications'
require 'configurations'
class Host < Ohm::Model require 'watchers'
include Ohm::Typecast
include Ohm::Timestamping
include Ohm::Callbacks
include Ohm::ExtraValidations
attribute :name
attribute :status
collection :services, Service
index :name
index :status
def validate
assert_present :name
assert_present :status
assert_unique :name
assert_member :status, ["up","down","pending"]
end
def to_hash
arr = []
services.sort.each {|s| arr << s.to_hash}
h = {:name => name, :status => status, :created_at => created_at, :updated_at => updated_at, :services => arr}
super.merge(h)
end
def is_new?
self.created_at == self.updated_at
end
class << self
def find_or_create(opts = {})
begin
# exclude requested status from lookup
h = find(opts.reject{|key,value| key == :status}).first
host = h.nil? ? create(opts) : h
host.status = opts[:status]
if host.valid?
host.save
end
host
rescue Exception => e
e.message
end
end
end
end
class Service < Ohm::Model
include Ohm::Typecast
include Ohm::Timestamping
include Ohm::Callbacks
include Ohm::ExtraValidations
attribute :name
attribute :status
reference :host, Host
index :name
index :status
def validate
assert_present :name
assert_present :status
assert_present :host_id
assert_unique [:name, :host_id]
assert_member :status, ["up", "down", "pending"]
end
def to_hash
super.merge(:name => name, :status => status, :updated_at => updated_at, :host => Host[host_id].name)
end
def is_new?
self.created_at == self.updated_at
end
class << self
def find_or_create(opts = {})
begin
# convert passed host object to host_id if passed
if opts.has_key?(:host)
opts.merge!({:host_id => opts[:host].id})
opts.reject!{|key, value| key == :host}
end
# exclude requested status from lookup
s = find(opts.reject{|key,value| key == :status}).first
service = s.nil? ? create(opts) : s
service.status = opts[:status]
if service.valid?
service.save
end
service
rescue Exception => e
e.message
end
end
end
end
class Configuration < Ohm::Model
include Ohm::Typecast
include Ohm::Timestamping
include Ohm::Callbacks
include Ohm::ExtraValidations
attribute :name
attribute :format
attribute :body
attribute :new_record
reference :application, Application
index :name
def validate
assert_present :name
assert_present :format
assert_present :body
assert_unique [:name, :application_id]
end
def to_hash
super.merge(:name => name, :format => format, :body => body, :update_at => updated_at, :application => Application[application_id].name)
end
def is_new?
self.created_at == self.updated_at
end
class << self
def find_or_create(opts={})
begin
if find(opts).first.nil?
conf = create(opts)
else
conf = find(opts).first
end
rescue Exception => e
e.message
end
end
end
end
class Application < Ohm::Model
include Ohm::Typecast
include Ohm::Timestamping
include Ohm::Callbacks
include Ohm::ExtraValidations
attribute :name
collection :configurations, Configuration
index :name
def validate
assert_present :name
assert_unique :name
end
def to_hash
arr = []
configurations.sort.each {|c| arr << c.to_hash}
super.merge(:name => name, :updated_at => updated_at, :configurations => arr)
end
def is_new?
self.created_at == self.updated_at
end
class << self
def find_or_create(opts = {})
begin
find(opts).first.nil? ? (app = create(opts)) : (app = find(opts).first)
if app.valid?
app.save
end
app
rescue Exception => e
e.message
end
end
end
end
class Watcher < Ohm::Model #NYI
include Ohm::Typecast
include Ohm::Timestamping
include Ohm::Callbacks
attribute :client
attribute :endpoint
attribute :event
attribute :action
index :client
index :event
def validate
assert_present :client, :endpoint, :event, :action
assert_unique [:client, :endpoint, :event, :action]
end
end
# Some pluralized helper models
class Hosts
def self.all(options = {})
options.empty? ? Host.all.sort : Host.find(options).sort
end
end
class Services
def self.all(options = {})
options.empty? ? Service.all.sort : Service.find(options).sort
end
end
class Applications
def self.all(options = {})
options.empty? ? Application.all.sort : Application.find(options).sort
end
end
class Configurations
def self.all(options = {})
options.empty? ? Configuration.all.sort : Configuration.find(options).sort
end
end
end
end
module Noah
class Service < Ohm::Model
include Ohm::Typecast
include Ohm::Timestamping
include Ohm::Callbacks
include Ohm::ExtraValidations
attribute :name
attribute :status
reference :host, Host
index :name
index :status
def validate
assert_present :name
assert_present :status
assert_present :host_id
assert_unique [:name, :host_id]
assert_member :status, ["up", "down", "pending"]
end
def to_hash
super.merge(:name => name, :status => status, :updated_at => updated_at, :host => Host[host_id].name)
end
def is_new?
self.created_at == self.updated_at
end
class << self
def find_or_create(opts = {})
begin
# convert passed host object to host_id if passed
if opts.has_key?(:host)
opts.merge!({:host_id => opts[:host].id})
opts.reject!{|key, value| key == :host}
end
# exclude requested status from lookup
s = find(opts.reject{|key,value| key == :status}).first
service = s.nil? ? create(opts) : s
service.status = opts[:status]
if service.valid?
service.save
end
service
rescue Exception => e
e.message
end
end
end
end
class Services
def self.all(options = {})
options.empty? ? Service.all.sort : Service.find(options).sort
end
end
end
module Noah
class Watcher < Ohm::Model #NYI
include Ohm::Typecast
include Ohm::Timestamping
include Ohm::Callbacks
attribute :client
attribute :endpoint
attribute :event
attribute :action
index :client
index :event
def validate
assert_present :client, :endpoint, :event, :action
assert_unique [:client, :endpoint, :event, :action]
end
end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment