noah 0.0.5 → 0.0.9

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 (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