noah 0.0.5-jruby → 0.1-jruby

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. data/.gemtest +0 -0
  2. data/.gitignore +10 -0
  3. data/LICENSE +201 -0
  4. data/README.md +68 -212
  5. data/Rakefile +65 -41
  6. data/TODO.md +65 -0
  7. data/bin/noah +2 -1
  8. data/bin/noah-watcher.rb +103 -0
  9. data/config.ru +6 -3
  10. data/config/warble.rb +18 -0
  11. data/examples/README.md +116 -0
  12. data/examples/cluster.ru +2 -0
  13. data/examples/custom-watcher.rb +10 -0
  14. data/examples/httpclient-server.rb +7 -0
  15. data/examples/httpclient.rb +12 -0
  16. data/examples/httpclient2.rb +28 -0
  17. data/examples/js/FABridge.js +1452 -0
  18. data/examples/js/WebSocketMain.swf +830 -0
  19. data/examples/js/swfobject.js +851 -0
  20. data/examples/js/web_socket.js +312 -0
  21. data/examples/logger.rb +11 -0
  22. data/examples/reconfiguring-sinatra-watcher.rb +11 -0
  23. data/examples/reconfiguring-sinatra.rb +33 -0
  24. data/examples/simple-post.rb +17 -0
  25. data/examples/websocket.html +24 -0
  26. data/examples/websocket.rb +41 -0
  27. data/lib/noah.rb +6 -8
  28. data/lib/noah/app.rb +20 -268
  29. data/lib/noah/application_routes.rb +70 -0
  30. data/lib/noah/ark.rb +0 -0
  31. data/lib/noah/configuration_routes.rb +81 -0
  32. data/lib/noah/custom_watcher.rb +79 -0
  33. data/lib/noah/ephemeral_routes.rb +47 -0
  34. data/lib/noah/helpers.rb +37 -14
  35. data/lib/noah/host_routes.rb +69 -0
  36. data/lib/noah/models.rb +86 -5
  37. data/lib/noah/models/applications.rb +41 -0
  38. data/lib/noah/models/configurations.rb +49 -0
  39. data/lib/noah/models/ephemerals.rb +54 -0
  40. data/lib/noah/models/hosts.rb +56 -0
  41. data/lib/noah/models/services.rb +54 -0
  42. data/lib/noah/models/watchers.rb +62 -0
  43. data/lib/noah/passthrough.rb +11 -0
  44. data/lib/noah/service_routes.rb +71 -0
  45. data/lib/noah/validations.rb +1 -0
  46. data/lib/noah/validations/watcher_validations.rb +48 -0
  47. data/lib/noah/version.rb +1 -1
  48. data/lib/noah/watcher_routes.rb +45 -0
  49. data/noah.gemspec +25 -17
  50. data/spec/application_spec.rb +30 -30
  51. data/spec/configuration_spec.rb +78 -14
  52. data/spec/ephemeral_spec.rb +59 -0
  53. data/spec/host_spec.rb +21 -21
  54. data/spec/noahapp_application_spec.rb +6 -6
  55. data/spec/noahapp_configuration_spec.rb +5 -5
  56. data/spec/noahapp_ephemeral_spec.rb +115 -0
  57. data/spec/noahapp_host_spec.rb +3 -3
  58. data/spec/noahapp_service_spec.rb +10 -10
  59. data/spec/noahapp_watcher_spec.rb +123 -0
  60. data/spec/service_spec.rb +27 -27
  61. data/spec/spec_helper.rb +13 -22
  62. data/spec/support/db/.keep +0 -0
  63. data/spec/support/test-redis.conf +8 -0
  64. data/spec/watcher_spec.rb +62 -0
  65. data/views/index.haml +21 -15
  66. metadata +189 -146
  67. data/Gemfile.lock +0 -83
  68. data/doc/coverage/index.html +0 -138
  69. data/doc/coverage/jquery-1.3.2.min.js +0 -19
  70. data/doc/coverage/jquery.tablesorter.min.js +0 -15
  71. data/doc/coverage/lib-helpers_rb.html +0 -393
  72. data/doc/coverage/lib-models_rb.html +0 -1449
  73. data/doc/coverage/noah_rb.html +0 -2019
  74. data/doc/coverage/print.css +0 -12
  75. data/doc/coverage/rcov.js +0 -42
  76. data/doc/coverage/screen.css +0 -270
  77. data/lib/noah/applications.rb +0 -46
  78. data/lib/noah/configurations.rb +0 -49
  79. data/lib/noah/hosts.rb +0 -54
  80. data/lib/noah/services.rb +0 -57
  81. data/lib/noah/watchers.rb +0 -18
@@ -0,0 +1,79 @@
1
+ require 'eventmachine'
2
+ require 'uri'
3
+ require 'logger'
4
+ begin
5
+ require 'em-hiredis'
6
+ rescue LoadError
7
+ puts "Please install: em-hiredis"
8
+ end
9
+
10
+ @log = Logger.new(STDOUT)
11
+ @log.level = Logger::DEBUG
12
+
13
+ require File.join(File.dirname(__FILE__), 'passthrough')
14
+
15
+ module Noah
16
+
17
+ class CustomWatcher
18
+ extend Passthrough
19
+
20
+ passthrough :redis_host, :pattern, :destination, :run!, :run_watcher
21
+
22
+ attr_accessor :my_pattern, :my_destination, :my_redis
23
+
24
+ def self.watch(&blk)
25
+ watcher = Noah::Watcher.new
26
+ watcher.instance_eval(&blk) if block_given?
27
+ watcher
28
+ end
29
+
30
+ def initialize
31
+ @my_redis ||= ENV['REDIS_URL']
32
+ @my_pattern ||= '//noah'
33
+ end
34
+
35
+ def self.redis_host(host)
36
+ @my_redis = host
37
+ end
38
+
39
+ def self.pattern(pattern)
40
+ @my_pattern = pattern
41
+ end
42
+
43
+ def self.destination(destination)
44
+ @my_destination = destination
45
+ end
46
+
47
+ def self.run!
48
+ @my_destination.nil? ? (raise ArgumentError) : run_watcher(@my_destination)
49
+ end
50
+
51
+ private
52
+ def self.run_watcher(dest)
53
+ log = Logger.new(STDOUT)
54
+ log.level = Logger::INFO
55
+ log.debug "#{dest.inspect}"
56
+ redis_url = URI.parse(@my_redis)
57
+ db = redis_url.path.gsub(/\//,'')
58
+
59
+ EventMachine.run do
60
+ trap("TERM") { log.info "Killed"; EventMachine.stop }
61
+ trap("INT") { log.info "Interrupted"; EventMachine.stop }
62
+ channel = EventMachine::Channel.new
63
+ r = EventMachine::Hiredis::Client.connect(redis_url.host, redis_url.port)
64
+ # Pulling out dbnum for now. Need to rethink it
65
+ #log.info "Binding to pattern #{db}:#{@my_pattern}"
66
+ log.info "Binding to pattern #{@my_pattern}"
67
+ r.psubscribe("#{@my_pattern}*")
68
+ r.on(:pmessage) do |pattern, event, message|
69
+ log.debug "Got message"
70
+ channel.push "#{message}"
71
+ end
72
+ r.errback { log.info "Something went tango-uniform" }
73
+
74
+ sub = channel.subscribe {|msg| log.info "Calling message handler"; dest.call(msg)}
75
+ end
76
+ end
77
+ end
78
+
79
+ end
@@ -0,0 +1,47 @@
1
+ class Noah::App
2
+ get '/e/?' do
3
+ halt 404
4
+ end
5
+
6
+ get '/e/*' do
7
+ params["splat"].size == 0 ? (halt 404) : (e=Noah::Ephemeral.find(:path => "/#{params["splat"][0]}").first)
8
+ (halt 404) if e.nil?
9
+ content_type "application/octet-stream"
10
+ e.data.nil? ? "" : "#{e.data}"
11
+ end
12
+
13
+ put '/e/*/watch' do
14
+ required_params = ["endpoint"]
15
+ data = JSON.parse(request.body.read)
16
+ (data.keys.sort == required_params.sort) ? (e = Noah::Watcher.find(:path => params[:splat][0]).first) : (raise "Missing Parameters")
17
+ e.nil? ? (halt 404) : (w = e.watch!(:endpoint => data["endpoint"]))
18
+ w.to_json
19
+ end
20
+
21
+ put '/e/*' do
22
+ raise("Data too large") if request.body.size > 512
23
+ d = request.body.read || nil
24
+ opts = {:path => "/#{params[:splat][0]}", :data => d}
25
+ e = Noah::Ephemeral.find_or_create(opts)
26
+ if e.valid?
27
+ action = e.is_new? ? "create" : "update"
28
+ r = {"action" => action, "result" => "success", "id" => e.id, "path" => e.path, "data" => e.data}
29
+ r.to_json
30
+ else
31
+ raise "#{format_errors(e)}"
32
+ end
33
+ end
34
+
35
+ delete '/e/*' do
36
+ p = params[:splat][0]
37
+ e = Noah::Ephemeral.find(:path => "/"+p).first
38
+ if e
39
+ e.delete
40
+ r = {"result" => "success", "id" => "#{e.id}", "action" => "delete", "path" => e.name}
41
+ r.to_json
42
+ else
43
+ halt 404
44
+ end
45
+ end
46
+
47
+ end
data/lib/noah/helpers.rb CHANGED
@@ -1,56 +1,79 @@
1
- require File.join(File.dirname(__FILE__), 'models')
2
1
  module Noah
3
2
  module SinatraHelpers
4
- extend(Ohm)
3
+
4
+ def format_errors(model)
5
+ error_messages = model.errors.present do |e|
6
+ # Missing attributes
7
+ e.on [:name, :not_present], "Name attribute missing"
8
+ e.on [:status, :not_present], "Status attribute missing"
9
+ e.on [:format, :not_present], "Format attribute missing"
10
+ e.on [:body, :not_present], "Body attribute missing"
11
+ e.on [:application_id, :not_present], "Application attribute missing"
12
+ e.on [:path, :not_present], "Path attribute missing"
13
+ e.on [:pattern, :not_present], "Pattern attribute missing"
14
+ e.on [:endpoint, :not_present], "Endpoint attribute missing"
15
+ # Invalid option
16
+ e.on [:status, :not_member], "Status must be up, down or pending"
17
+ # Duplicate keys
18
+ e.on [[:name, :application_id], :not_unique], "Record already exists"
19
+ e.on [[:name, :host_id], :not_unique], "Record already exists"
20
+ e.on [[:endpoint, :pattern], :not_unique], "Record already exists"
21
+ e.on [:path, :not_unique], "Record already exists"
22
+ # Custom exceptions
23
+ e.on [:pattern, :already_provided], "Pattern is already provided"
24
+ e.on [:pattern, :replaces_existing], "Pattern would overwrite existing"
25
+ end
26
+ error_messages.first
27
+ end
5
28
 
6
29
  def host(opts = {})
7
- Host.find(opts).first
30
+ Noah::Host.find(opts).first
8
31
  end
9
32
 
10
33
  def hosts(opts = {})
11
- Hosts.all(opts)
34
+ Noah::Hosts.all(opts)
12
35
  end
13
36
 
14
37
  def service(opts = {})
15
- Service.find(options)
38
+ Noah::Service.find(options)
16
39
  end
17
40
 
18
41
  def services(opts = {})
19
- Services.all(opts)
42
+ Noah::Services.all(opts)
20
43
  end
21
44
 
22
45
  def host_service(hostname, servicename)
23
- h = Host.find(:name => hostname).first
46
+ h = Noah::Host.find(:name => hostname).first
24
47
  if h.nil?
25
48
  nil
26
49
  else
27
- Service.find(:host_id => h.id, :name => servicename).first
50
+ Noah::Service.find(:host_id => h.id, :name => servicename).first
28
51
  end
29
52
  end
30
53
 
31
54
  def host_services(hostname)
32
- h = Host.find(:name => hostname).first
55
+ h = Noah::Host.find(:name => hostname).first
33
56
  if h.nil?
34
57
  nil
35
58
  else
36
- Services.all(:host_id => id)
59
+ Noah::Services.all(:host_id => id)
37
60
  end
38
61
  end
39
62
 
40
63
  def application(opts = {})
41
- Application.find(opts).first
64
+ Noah::Application.find(opts).first
42
65
  end
43
66
 
44
67
  def applications(opts = {})
45
- Applications.all(opts)
68
+ Noah::Applications.all(opts)
46
69
  end
47
70
 
48
71
  def configuration(opts = {})
49
- Configuration.find(opts).first
72
+ Noah::Configuration.find(opts).first
50
73
  end
51
74
 
52
75
  def configurations(opts = {})
53
- Configurations.all(opts)
76
+ Noah::Configurations.all(opts)
54
77
  end
55
78
  end
56
79
 
@@ -0,0 +1,69 @@
1
+ class Noah::App
2
+ # Host URIs
3
+
4
+ # GET named {Service} for named {Host}
5
+ get '/h/:hostname/:servicename/?' do |hostname, servicename|
6
+ h = host_service(hostname, servicename)
7
+ if h.nil?
8
+ halt 404
9
+ else
10
+ h.to_json
11
+ end
12
+ end
13
+
14
+ # GET named {Host}
15
+ # @param :hostname name of {Host}
16
+ # @return [JSON] representation of {Host}
17
+ get '/h/:hostname/?' do |hostname|
18
+ h = host(:name => hostname)
19
+ if h.nil?
20
+ halt 404
21
+ else
22
+ h.to_json
23
+ end
24
+ end
25
+
26
+ # GET all {Hosts}
27
+ get '/h/?' do
28
+ hosts.map {|h| h.to_hash}
29
+ if hosts.size == 0
30
+ halt 404
31
+ else
32
+ hosts.to_json
33
+ end
34
+ end
35
+
36
+ put '/h/:hostname/watch' do |hostname|
37
+ required_params = ["endpoint"]
38
+ data = JSON.parse(request.body.read)
39
+ (data.keys.sort == required_params.sort) ? (h = Noah::Host.find(:name => hostname).first) : (raise "Missing Parameters")
40
+ h.nil? ? (halt 404) : (w = h.watch!(:endpoint => data['endpoint']))
41
+ w.to_json
42
+ end
43
+
44
+ put '/h/:hostname/?' do |hostname|
45
+ required_params = ["name", "status"]
46
+ data = JSON.parse(request.body.read)
47
+ (data.keys.sort == required_params.sort && data['name'] == hostname) ? (host = Noah::Host.find_or_create(:name => data['name'], :status => data['status'])) : (raise "Missing Parameters")
48
+ if host.valid?
49
+ r = {"result" => "success","id" => "#{host.id}","status" => "#{host.status}", "name" => "#{host.name}", "new_record" => host.is_new?}
50
+ r.to_json
51
+ else
52
+ raise "#{format_errors(host)}"
53
+ end
54
+ end
55
+
56
+ delete '/h/:hostname/?' do |hostname|
57
+ host = Noah::Host.find(:name => hostname).first
58
+ if host
59
+ services = []
60
+ Noah::Service.find(:host_id => host.id).sort.each {|x| services << x; x.delete} if host.services.size > 0
61
+ host.delete
62
+ r = {"result" => "success", "id" => "#{host.id}", "name" => "#{hostname}", "service_count" => "#{services.size}"}
63
+ r.to_json
64
+ else
65
+ halt 404
66
+ end
67
+ end
68
+
69
+ end
data/lib/noah/models.rb CHANGED
@@ -1,5 +1,86 @@
1
- require File.join(File.dirname(__FILE__),'hosts')
2
- require File.join(File.dirname(__FILE__),'services')
3
- require File.join(File.dirname(__FILE__),'applications')
4
- require File.join(File.dirname(__FILE__),'configurations')
5
- require File.join(File.dirname(__FILE__),'watchers')
1
+ require 'ohm'
2
+ require 'ohm/contrib'
3
+ module Noah
4
+ class Model < Ohm::Model
5
+ def self.inherited(model)
6
+
7
+ model.send :include, Ohm::Timestamping
8
+ model.send :include, Ohm::Typecast
9
+ model.send :include, Ohm::Callbacks
10
+ model.send :include, Ohm::ExtraValidations
11
+
12
+ # removing this as it's simply redundant
13
+ # model.after :save, :notify_via_redis_save
14
+ model.after :create, :notify_via_redis_create
15
+ model.after :update, :notify_via_redis_update
16
+ model.before :delete, :stash_name
17
+ model.after :delete, :notify_via_redis_delete
18
+ model.send :include, ModelClassMethods
19
+ end
20
+ end
21
+
22
+ module ModelClassMethods
23
+
24
+ def is_new?
25
+ self.created_at == self.updated_at
26
+ end
27
+
28
+
29
+ def watch!(opts={:endpoint => nil, :pattern => nil})
30
+ base_pattern = "#{self.patternize_me}"
31
+ opts[:endpoint].nil? ? (raise ArgumentError, "Need an endpoint") : endpoint=opts[:endpoint]
32
+ opts[:pattern].nil? ? pattern=base_pattern : pattern=opts[:pattern]
33
+
34
+ begin
35
+ w = Watcher.new :pattern => pattern, :endpoint => endpoint
36
+ w.valid? ? w.save : (raise "#{w.errors}")
37
+ w.name
38
+ rescue Exception => e
39
+ e.message
40
+ end
41
+ end
42
+
43
+ protected
44
+ def patternize_me
45
+ "//noah/#{self.class_to_lower}/#{name}"
46
+ end
47
+
48
+ def stash_name
49
+ @deleted_name = self.name
50
+ end
51
+
52
+ def class_to_lower
53
+ self.class.to_s.gsub(/(.*)::(\w)/,'\2').downcase
54
+ end
55
+ def dbnum
56
+ o = Ohm.options.first
57
+ return "0" if o.nil?
58
+ return "0" if (o[:db].nil? && o[:url].nil?)
59
+ o[:db].nil? ? "#{o[:url].split('/').last}" : "#{o[:db]}"
60
+ end
61
+
62
+ ["create", "update", "delete"].each do |meth|
63
+ class_eval do
64
+ define_method("notify_via_redis_#{meth}".to_sym) do
65
+ db = self.dbnum
66
+ self.name.nil? ? name=@deleted_name : name=self.name
67
+ # Pulling out dbnum for now. Need to rethink it
68
+ #pub_category = "#{db}:noah.#{self.class.to_s}[#{name}].#{meth}"
69
+ pub_category = "#{self.patternize_me}"
70
+ Ohm.redis.publish(pub_category, self.to_hash.merge({"action" => meth, "pubcategory" => pub_category}).to_json)
71
+
72
+ # The following provides a post post-action hook. It allows a class to provide it's own handling after the fact
73
+ # good example is in [Noah::Ephemeral] where it's used to check for/clean up expired ephemeral nodes entries
74
+ self.send("#{meth}_hook".to_sym) unless self.protected_methods.member?("#{meth}_hook".to_sym) == false
75
+ end
76
+ end
77
+ end
78
+
79
+ end
80
+ end
81
+ require File.join(File.dirname(__FILE__), 'models','hosts')
82
+ require File.join(File.dirname(__FILE__), 'models','services')
83
+ require File.join(File.dirname(__FILE__), 'models','applications')
84
+ require File.join(File.dirname(__FILE__), 'models','configurations')
85
+ require File.join(File.dirname(__FILE__), 'models','watchers')
86
+ require File.join(File.dirname(__FILE__), 'models','ephemerals')
@@ -0,0 +1,41 @@
1
+ require File.join(File.dirname(__FILE__), 'configurations')
2
+ module Noah
3
+ class Application < Model
4
+ attribute :name
5
+ collection :configurations, Configuration
6
+
7
+ index :name
8
+
9
+ def validate
10
+ super
11
+ assert_present :name
12
+ end
13
+
14
+ def to_hash
15
+ arr = []
16
+ configurations.sort.each {|c| arr << c.to_hash}
17
+ super.merge(:name => name, :created_at => created_at, :updated_at => updated_at, :configurations => arr)
18
+ end
19
+
20
+ class << self
21
+ def find_or_create(opts = {})
22
+ begin
23
+ find(opts).first.nil? ? (app = create(opts)) : (app = find(opts).first)
24
+ if app.valid?
25
+ app.save
26
+ end
27
+ app
28
+ rescue Exception => e
29
+ e.message
30
+ end
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+ class Applications
37
+ def self.all(options = {})
38
+ options.empty? ? Application.all.sort : Application.find(options).sort
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,49 @@
1
+ module Noah
2
+ class Configuration < Model
3
+
4
+ attribute :name
5
+ attribute :format
6
+ attribute :body
7
+ attribute :new_record
8
+ reference :application, Application
9
+
10
+ index :name
11
+ index :format
12
+ index :body
13
+
14
+ def validate
15
+ super
16
+ assert_present :name
17
+ assert_present :format
18
+ assert_present :body
19
+ assert_present :application_id
20
+ assert_unique [:name, :application_id]
21
+ end
22
+
23
+ def to_hash
24
+ Application[application_id].nil? ? app_name=nil : app_name=Application[application_id].name
25
+ super.merge(:name => name, :format => format, :body => body, :created_at => created_at, :updated_at => updated_at, :application => app_name)
26
+ end
27
+
28
+ class << self
29
+ def find_or_create(opts={})
30
+ begin
31
+ if find(opts).first.nil?
32
+ conf = create(opts)
33
+ else
34
+ conf = find(opts).first
35
+ end
36
+ rescue Exception => e
37
+ e.message
38
+ end
39
+ end
40
+ end
41
+
42
+ end
43
+
44
+ class Configurations
45
+ def self.all(options = {})
46
+ options.empty? ? Configuration.all.sort : Configuration.find(options).sort
47
+ end
48
+ end
49
+ end