Commit 05f9e7f6 authored by John E. Vincent's avatar John E. Vincent

Moving to modular Sinatra app

parent 84073744
...@@ -9,6 +9,7 @@ group :development do ...@@ -9,6 +9,7 @@ group :development do
gem "sinatra-reloader", "= 0.5.0" gem "sinatra-reloader", "= 0.5.0"
gem "rspec", "= 2.4.0" gem "rspec", "= 2.4.0"
gem "rcov", "= 0.9.9" gem "rcov", "= 0.9.9"
gem "rack-test", "= 0.5.7", :require => "rack/test"
end end
platforms :mri do platforms :mri do
gem "yajl-ruby", "= 0.7.9", :require => "yajl" gem "yajl-ruby", "= 0.7.9", :require => "yajl"
......
...@@ -16,6 +16,8 @@ GEM ...@@ -16,6 +16,8 @@ GEM
ohm-contrib (0.1.0) ohm-contrib (0.1.0)
ohm ohm
rack (1.2.1) rack (1.2.1)
rack-test (0.5.7)
rack (>= 1.0)
rake (0.8.7) rake (0.8.7)
rcov (0.9.9) rcov (0.9.9)
rcov (0.9.9-java) rcov (0.9.9-java)
...@@ -55,6 +57,7 @@ DEPENDENCIES ...@@ -55,6 +57,7 @@ DEPENDENCIES
json-jruby (= 1.4.6) json-jruby (= 1.4.6)
ohm (= 0.1.3) ohm (= 0.1.3)
ohm-contrib (= 0.1.0) ohm-contrib (= 0.1.0)
rack-test (= 0.5.7)
rake (= 0.8.7) rake (= 0.8.7)
rcov (= 0.9.9) rcov (= 0.9.9)
rspec (= 2.4.0) rspec (= 2.4.0)
......
require 'sinatra/reloader' if development?
require 'ohm'
begin
require 'yajl'
rescue LoadError
require 'json'
end
require 'haml'
require File.join(File.dirname(__FILE__), 'config/db')
require File.join(File.dirname(__FILE__), 'models')
require File.join(File.dirname(__FILE__), 'helpers')
get '/' do
before do
content_type "text/html"
end
haml :index, :format => :html5
end
before do
content_type "application/json"
end
not_found do
erb :'404'
end
error do
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
required_params = ["name", "status"]
data = JSON.parse(request.body.read)
data.keys.sort == required_params.sort ? (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}"}
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|
# message format: {"status":"initial_status", "host":"hostname"}
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.create(:name => servicename, :status => data['status'], :host => h)
if service.valid?
service.save
r = {"action" => "add", "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 or invalid 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" => "#{app.name}", "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"
}
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?
action = config.is_new? ? "create" : "update"
dependency_action = app.is_new? ? "created" : "updated"
config.save
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
#!/usr/bin/env ruby
require 'ohm' require 'ohm'
begin begin
require 'yajl' require 'yajl'
...@@ -5,8 +6,8 @@ rescue LoadError ...@@ -5,8 +6,8 @@ rescue LoadError
require 'json' require 'json'
end end
require File.join(File.dirname(__FILE__), 'config/db') require File.join(File.dirname(__FILE__), '../config/db')
require File.join(File.dirname(__FILE__), 'models') require File.join(File.dirname(__FILE__), '../lib/models')
# Flush the DB # Flush the DB
Ohm.redis.flushdb Ohm.redis.flushdb
......
require 'rubygems' require File.join(File.dirname(__FILE__), 'noah')
require 'sinatra' run NoahApp
require 'sinatra/namespace'
require File.join(File.dirname(__FILE__), 'app')
set :env, :development
set :root, File.dirname(__FILE__)
set :server, %[thin mongrel webrick]
set :logging, true
set :raise_errors, false
set :show_exceptions, false
disable :run
run Sinatra::Application
helpers do
def host(opts = {})
Host.find(opts).first
end
def hosts(opts = {})
Hosts.all(opts)
end
def service(opts = {})
Service.find(options)
end
def services(opts = {})
Services.all(opts)
end
def host_service(hostname, servicename)
h = Host.find(:name => hostname).first
if h.nil?
nil
else
Service.find(:host_id => h.id, :name => servicename).first
end
end
def host_services(hostname)
h = Host.find(:name => hostname).first
if h.nil?
nil
else
Services.all(:host_id => id)
end
end
def application(opts = {})
Application.find(opts).first
end
def applications(opts = {})
Applications.all(opts)
end
def configuration(opts = {})
Configuration.find(opts).first
end
def configurations(opts = {})
Configurations.all(opts)
end
end
...@@ -8,5 +8,5 @@ rescue LoadError ...@@ -8,5 +8,5 @@ rescue LoadError
end end
require File.join(File.dirname(__FILE__), 'config/db') require File.join(File.dirname(__FILE__), 'config/db')
require File.join(File.dirname(__FILE__), 'models') require File.join(File.dirname(__FILE__), 'lib/models')
module Sinatra
module NoahHelpers
def host(opts = {})
Host.find(opts).first
end
def hosts(opts = {})
Hosts.all(opts)
end
def service(opts = {})
Service.find(options)
end
def services(opts = {})
Services.all(opts)
end
def host_service(hostname, servicename)
h = Host.find(:name => hostname).first
if h.nil?
nil
else
Service.find(:host_id => h.id, :name => servicename).first
end
end
def host_services(hostname)
h = Host.find(:name => hostname).first
if h.nil?
nil
else
Services.all(:host_id => id)
end
end
def application(opts = {})
Application.find(opts).first
end
def applications(opts = {})
Applications.all(opts)
end
def configuration(opts = {})
Configuration.find(opts).first
end
def configurations(opts = {})
Configurations.all(opts)
end
end
helpers NoahHelpers
end
...@@ -64,7 +64,9 @@ class Service < Ohm::Model ...@@ -64,7 +64,9 @@ class Service < Ohm::Model
index :status index :status
def validate def validate
assert_present :name, :status assert_present :name
assert_present :status
assert_present :host_id
assert_unique [:name, :host_id] assert_unique [:name, :host_id]
assert_member :status, ["up", "down", "pending"] assert_member :status, ["up", "down", "pending"]
end end
...@@ -76,6 +78,30 @@ class Service < Ohm::Model ...@@ -76,6 +78,30 @@ class Service < Ohm::Model
def is_new? def is_new?
self.created_at == self.updated_at self.created_at == self.updated_at
end 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
service
else
raise service.errors
end
rescue Exception => e
e.message
end
end
end
end end
class Configuration < Ohm::Model class Configuration < Ohm::Model
......
require 'sinatra/base'
require 'sinatra/reloader'
require 'sinatra/namespace'
require 'ohm'
begin
require 'yajl'
rescue LoadError
require 'json'
end
require 'haml'
require File.join(File.dirname(__FILE__), 'config/db')
require File.join(File.dirname(__FILE__), 'lib/models')
require File.join(File.dirname(__FILE__), 'lib/helpers')
class NoahApp < Sinatra::Base
register Sinatra::Namespace
helpers Sinatra::NoahHelpers
configure(:development) do
register Sinatra::Reloader
also_reload "models.rb"
also_reload "helpers.rb"
end
configure do
set :app_file, __FILE__
set :root, File.dirname(__FILE__)
set :server, %w[thin mongrel webrick]
set :port, 9292
set :logging, true
set :raise_errors, false
set :show_exceptions, false
end
get '/' do
content_type "text/html"
haml :index, :format => :html5
end
before do
content_type "application/json"
end
not_found do
erb :'404'
end
error do
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
required_params = ["name", "status"]
data = JSON.parse(request.body.read)
data.keys.sort == required_params.sort ? (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}"}
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|
# message format: {"status":"initial_status", "host":"hostname"}
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.create(:name => servicename, :status => data['status'], :host => h)
if service.valid?
service.save
r = {"action" => "add", "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 or invalid 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" => "#{app.name}", "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"
}
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?
action = config.is_new? ? "create" : "update"
dependency_action = app.is_new? ? "created" : "updated"
config.save
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
...@@ -48,7 +48,7 @@ describe "Host", :reset_redis => true do ...@@ -48,7 +48,7 @@ describe "Host", :reset_redis => true do
host.valid?.should == true host.valid?.should == true
host.save host.save
host.is_new?.should == true host.is_new?.should == true
sleep 3 sleep 1
host1 = Host.find_or_create(:name => hostname, :status => newstatus) host1 = Host.find_or_create(:name => hostname, :status => newstatus)
host1.save host1.save
host1.is_new?.should == false host1.is_new?.should == false
......
...@@ -9,7 +9,7 @@ describe "Service", :reset_redis => true do ...@@ -9,7 +9,7 @@ describe "Service", :reset_redis => true do
hoststatus = "up" hoststatus = "up"
host = Host.create(:name => hostname, :status => hoststatus) host = Host.create(:name => hostname, :status => hoststatus)
host.save host.save
service = Service.create(:name => "myservice", :status => "up", :host => host) service = Service.create(:name => servicename, :status => servicestatus, :host => host)
service.valid?.should == true service.valid?.should == true
service.save service.save
service.name.should == servicename service.name.should == servicename
...@@ -18,4 +18,87 @@ describe "Service", :reset_redis => true do ...@@ -18,4 +18,87 @@ describe "Service", :reset_redis => true do
host.services[1].name.should == servicename host.services[1].name.should == servicename
end end
it "should not create a new Service when missing a Host" do
servicename = "myservice1"
servicestatus = "up"
service = Service.create(:name => servicename, :status => servicestatus)
service.valid?.should == false
service.errors.should == [[:host_id, :not_present]]
end
it "should not create a new Service when missing a name" do
host = Host.create(:name => "host1.domain.com", :status => "up")
host.save
service = Service.create(:status => "up", :host => host)
service.valid?.should == false
service.errors.should == [[:name, :not_present]]
end
it "should not create a new Service when missing a status" do
host = Host.create(:name => "host1.domain.com", :status => "up")
host.save
service = Service.create(:name => 'foo', :host => host)
service.valid?.should == false
service.errors.should == [[:status, :not_present], [:status, :not_member]]
end
it "should not create a new Service with an invalid status" do
host = Host.create(:name => "host1.domain.com", :status => "up")
host.save
service = Service.create(:name => "myservice", :status => "invalid_status", :host => host)
service.valid?.should == false
service.errors.should == [[:status, :not_member]]
end
it "should not create a duplicate Service" do
host = Host.create(:name => "host1.domain.com", :status => "up")
host.save
s = Service.create(:name => "myservice", :status => "up", :host => host)
s.save
s1 = Service.create(:name => "myservice", :status => "up", :host => host)
s1.valid?.should == false
s1.errors.should == [[[:name, :host_id], :not_unique]]
end
it "should create a new Service with find_or_create" do
host = Host.create(:name => "h1", :status => "up")
host.save
service = Service.find_or_create(:name => "s1", :status => "up", :host => host)
service.save
service.is_new?.should == true
end
it "should update an existing Service with find_or_create" do
host = Host.create(:name => "h2", :status => "up")
host.save
service = Service.find_or_create(:name => "s2", :status => "up", :host => host)
service.save
sleep 1
service2 = Service.find_or_create(:name => "s2", :status => "up", :host => host)
service2.save
service2.is_new?.should == false
end
it "should delete a Service" do
h = Host.create(:name => "h1", :status => "up")
h.save
s = Service.create(:name => "s1", :status => "up", :host => h)
s.save
s = Service.find(:name => "s1").first
s.delete
s = Service.find(:name => "s1").first
s.should == nil
end
it "should find multiple Services" do
h = Host.create(:name => "h1", :status => "up")
if h.valid?
h.services << Service.create(:name => "s1", :status => "up", :host => h)
h.services << Service.create(:name => "s2", :status => "up", :host => h)
h.save
end
Services.all.size.should == 2
Services.all.first.name.should == "s1"
Services.all.last.name.should == "s2"
end
end end
...@@ -6,7 +6,7 @@ rescue LoadError ...@@ -6,7 +6,7 @@ rescue LoadError
require 'json' require 'json'
end end
require File.join(File.dirname(__FILE__), '..', 'config','db') require File.join(File.dirname(__FILE__), '..', 'config','db')
require File.join(File.dirname(__FILE__), '..', 'models') require File.join(File.dirname(__FILE__), '..', 'lib', 'models')
require 'rspec' require 'rspec'
RSpec.configure do |config| RSpec.configure do |config|
......
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