noah 0.0.5 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. data/.gemtest +0 -0
  2. data/.gitignore +9 -0
  3. data/LICENSE +201 -0
  4. data/README.md +68 -212
  5. data/Rakefile +70 -41
  6. data/TODO.md +59 -0
  7. data/bin/noah +2 -1
  8. data/bin/noah-watcher.rb +93 -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 +32 -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 +5 -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/ephemeral_routes.rb +19 -0
  33. data/lib/noah/helpers.rb +12 -14
  34. data/lib/noah/host_routes.rb +69 -0
  35. data/lib/noah/models.rb +86 -5
  36. data/lib/noah/models/applications.rb +41 -0
  37. data/lib/noah/models/configurations.rb +49 -0
  38. data/lib/noah/models/ephemerals.rb +33 -0
  39. data/lib/noah/models/hosts.rb +56 -0
  40. data/lib/noah/models/services.rb +54 -0
  41. data/lib/noah/models/watchers.rb +54 -0
  42. data/lib/noah/passthrough.rb +11 -0
  43. data/lib/noah/service_routes.rb +71 -0
  44. data/lib/noah/validations.rb +1 -0
  45. data/lib/noah/validations/watcher_validations.rb +48 -0
  46. data/lib/noah/version.rb +1 -1
  47. data/lib/noah/watcher.rb +75 -0
  48. data/lib/noah/watcher_routes.rb +12 -0
  49. data/lib/vendor/em-hiredis/Gemfile +4 -0
  50. data/lib/vendor/em-hiredis/README.md +61 -0
  51. data/lib/vendor/em-hiredis/Rakefile +2 -0
  52. data/lib/vendor/em-hiredis/em-hiredis-0.0.1.gem +0 -0
  53. data/lib/vendor/em-hiredis/em-hiredis.gemspec +23 -0
  54. data/lib/vendor/em-hiredis/lib/em-hiredis.rb +22 -0
  55. data/lib/vendor/em-hiredis/lib/em-hiredis/client.rb +131 -0
  56. data/lib/vendor/em-hiredis/lib/em-hiredis/connection.rb +61 -0
  57. data/lib/vendor/em-hiredis/lib/em-hiredis/event_emitter.rb +29 -0
  58. data/lib/vendor/em-hiredis/lib/em-hiredis/version.rb +5 -0
  59. data/noah.gemspec +21 -17
  60. data/spec/application_spec.rb +30 -30
  61. data/spec/configuration_spec.rb +81 -14
  62. data/spec/ephemeral_spec.rb +52 -0
  63. data/spec/host_spec.rb +21 -21
  64. data/spec/noahapp_application_spec.rb +6 -6
  65. data/spec/noahapp_configuration_spec.rb +3 -3
  66. data/spec/noahapp_host_spec.rb +2 -2
  67. data/spec/noahapp_service_spec.rb +9 -9
  68. data/spec/noahapp_watcher_spec.rb +34 -0
  69. data/spec/service_spec.rb +27 -27
  70. data/spec/spec_helper.rb +13 -22
  71. data/spec/support/db/.keep +0 -0
  72. data/spec/support/test-redis.conf +8 -0
  73. data/spec/watcher_spec.rb +62 -0
  74. data/views/index.haml +21 -15
  75. metadata +124 -148
  76. data/Gemfile.lock +0 -85
  77. data/doc/coverage/index.html +0 -138
  78. data/doc/coverage/jquery-1.3.2.min.js +0 -19
  79. data/doc/coverage/jquery.tablesorter.min.js +0 -15
  80. data/doc/coverage/lib-helpers_rb.html +0 -393
  81. data/doc/coverage/lib-models_rb.html +0 -1449
  82. data/doc/coverage/noah_rb.html +0 -2019
  83. data/doc/coverage/print.css +0 -12
  84. data/doc/coverage/rcov.js +0 -42
  85. data/doc/coverage/screen.css +0 -270
  86. data/lib/noah/applications.rb +0 -46
  87. data/lib/noah/configurations.rb +0 -49
  88. data/lib/noah/hosts.rb +0 -54
  89. data/lib/noah/services.rb +0 -57
  90. data/lib/noah/watchers.rb +0 -18
@@ -0,0 +1,19 @@
1
+ class Noah::App
2
+ # Stubbing Ephemeral endpoints
3
+ get '/e/*' do
4
+ # Some logic to handle splats for ephemerals
5
+ # Eventually I'll move to root path
6
+ end
7
+
8
+ put '/e/*/watch' do
9
+ # Logic for adding watches to ephemerals
10
+ end
11
+
12
+ put '/e/*' do
13
+ # Some logic for creating ephemerals
14
+ end
15
+
16
+ delete '/e/*' do
17
+ # See previous two entries
18
+ end
19
+ end
data/lib/noah/helpers.rb CHANGED
@@ -1,56 +1,54 @@
1
- require File.join(File.dirname(__FILE__), 'models')
2
1
  module Noah
3
2
  module SinatraHelpers
4
- extend(Ohm)
5
3
 
6
4
  def host(opts = {})
7
- Host.find(opts).first
5
+ Noah::Host.find(opts).first
8
6
  end
9
7
 
10
8
  def hosts(opts = {})
11
- Hosts.all(opts)
9
+ Noah::Hosts.all(opts)
12
10
  end
13
11
 
14
12
  def service(opts = {})
15
- Service.find(options)
13
+ Noah::Service.find(options)
16
14
  end
17
15
 
18
16
  def services(opts = {})
19
- Services.all(opts)
17
+ Noah::Services.all(opts)
20
18
  end
21
19
 
22
20
  def host_service(hostname, servicename)
23
- h = Host.find(:name => hostname).first
21
+ h = Noah::Host.find(:name => hostname).first
24
22
  if h.nil?
25
23
  nil
26
24
  else
27
- Service.find(:host_id => h.id, :name => servicename).first
25
+ Noah::Service.find(:host_id => h.id, :name => servicename).first
28
26
  end
29
27
  end
30
28
 
31
29
  def host_services(hostname)
32
- h = Host.find(:name => hostname).first
30
+ h = Noah::Host.find(:name => hostname).first
33
31
  if h.nil?
34
32
  nil
35
33
  else
36
- Services.all(:host_id => id)
34
+ Noah::Services.all(:host_id => id)
37
35
  end
38
36
  end
39
37
 
40
38
  def application(opts = {})
41
- Application.find(opts).first
39
+ Noah::Application.find(opts).first
42
40
  end
43
41
 
44
42
  def applications(opts = {})
45
- Applications.all(opts)
43
+ Noah::Applications.all(opts)
46
44
  end
47
45
 
48
46
  def configuration(opts = {})
49
- Configuration.find(opts).first
47
+ Noah::Configuration.find(opts).first
50
48
  end
51
49
 
52
50
  def configurations(opts = {})
53
- Configurations.all(opts)
51
+ Noah::Configurations.all(opts)
54
52
  end
55
53
  end
56
54
 
@@ -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 "#{host.errors}"
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
@@ -0,0 +1,33 @@
1
+ require 'digest/sha1'
2
+ module Noah
3
+ class Ephemeral < Model #NYI
4
+
5
+ attribute :path
6
+ attribute :data
7
+
8
+ index :path
9
+
10
+ def validate
11
+ super
12
+ assert_present :path
13
+ assert_unique :path
14
+ end
15
+
16
+ def name
17
+ @name = path
18
+ end
19
+
20
+ protected
21
+ def save_hook
22
+ # called after any create,update,delete
23
+ # logic needed to expire any orphaned ephemerals
24
+ end
25
+
26
+ private
27
+ def path_protected?(path_part)
28
+ # Check for protected paths in ephemeral nodes
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,56 @@
1
+ require File.join(File.dirname(__FILE__), 'services')
2
+ module Noah
3
+ class Host < Model
4
+ # Host model
5
+ # @return {Host} a {Host} object
6
+
7
+ attribute :name
8
+ attribute :status
9
+ collection :services, Service
10
+
11
+ index :name
12
+ index :status
13
+
14
+ def validate
15
+ super
16
+ assert_present :name
17
+ assert_present :status
18
+ assert_unique :name
19
+ assert_member :status, ["up","down","pending"]
20
+ end
21
+
22
+ # @return [Hash] A hash representation of a {Host}
23
+ def to_hash
24
+ arr = []
25
+ services.sort.each {|s| arr << s.to_hash}
26
+ h = {:name => name, :status => status, :created_at => created_at, :updated_at => updated_at, :services => arr}
27
+ super.merge(h)
28
+ end
29
+
30
+ class << self
31
+ def find_or_create(opts = {})
32
+ begin
33
+ # exclude requested status from lookup
34
+ h = find(opts.reject{|key,value| key == :status}).first
35
+ host = h.nil? ? create(opts) : h
36
+ host.status = opts[:status]
37
+ if host.valid?
38
+ host.save
39
+ end
40
+ host
41
+ rescue Exception => e
42
+ e.message
43
+ end
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+ class Hosts
50
+ # @param [Hash] optional filters for results
51
+ # @return [Array] Array of {Host} objects
52
+ def self.all(options = {})
53
+ options.empty? ? Noah::Host.all.sort : Noah::Host.find(options).sort
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,54 @@
1
+ module Noah
2
+
3
+ class Service < Model
4
+
5
+ attribute :name
6
+ attribute :status
7
+ reference :host, Host
8
+
9
+ index :name
10
+ index :status
11
+
12
+ def validate
13
+ super
14
+ assert_present :name
15
+ assert_present :status
16
+ assert_present :host_id
17
+ assert_unique [:name, :host_id]
18
+ assert_member :status, ["up", "down", "pending"]
19
+ end
20
+
21
+ def to_hash
22
+ Host[host_id].nil? ? host_name=nil : host_name=Host[host_id].name
23
+ super.merge(:name => name, :status => status, :updated_at => updated_at, :host => host_name)
24
+ end
25
+
26
+ class << self
27
+ def find_or_create(opts = {})
28
+ begin
29
+ # convert passed host object to host_id if passed
30
+ if opts.has_key?(:host)
31
+ opts.merge!({:host_id => opts[:host].id})
32
+ opts.reject!{|key, value| key == :host}
33
+ end
34
+ # exclude requested status from lookup
35
+ s = find(opts.reject{|key,value| key == :status}).first
36
+ service = s.nil? ? create(opts) : s
37
+ service.status = opts[:status]
38
+ if service.valid?
39
+ service.save
40
+ end
41
+ service
42
+ rescue Exception => e
43
+ e.message
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ class Services
50
+ def self.all(options = {})
51
+ options.empty? ? Service.all.sort : Service.find(options).sort
52
+ end
53
+ end
54
+ end