healthy 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ gem 'sinatra'
9
+
10
+ group :development, :test do
11
+ gem "rspec", "~> 2.3.0"
12
+ gem "bundler", "~> 1.0.0"
13
+ gem "jeweler", "~> 1.5.2"
14
+ gem "rcov", ">= 0"
15
+ gem 'mocha'
16
+ end
@@ -0,0 +1,37 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.2)
5
+ git (1.2.5)
6
+ jeweler (1.5.2)
7
+ bundler (~> 1.0.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ mocha (0.9.9)
11
+ rake
12
+ rack (1.2.1)
13
+ rake (0.8.7)
14
+ rcov (0.9.9)
15
+ rspec (2.3.0)
16
+ rspec-core (~> 2.3.0)
17
+ rspec-expectations (~> 2.3.0)
18
+ rspec-mocks (~> 2.3.0)
19
+ rspec-core (2.3.1)
20
+ rspec-expectations (2.3.0)
21
+ diff-lcs (~> 1.1.2)
22
+ rspec-mocks (2.3.0)
23
+ sinatra (1.1.2)
24
+ rack (~> 1.1)
25
+ tilt (~> 1.2)
26
+ tilt (1.2.2)
27
+
28
+ PLATFORMS
29
+ ruby
30
+
31
+ DEPENDENCIES
32
+ bundler (~> 1.0.0)
33
+ jeweler (~> 1.5.2)
34
+ mocha
35
+ rcov
36
+ rspec (~> 2.3.0)
37
+ sinatra
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Matthew Deiters
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,46 @@
1
+ = healthy
2
+
3
+ Coming Soon
4
+
5
+ == Mounting the application in rails 2.3.*
6
+
7
+ Create a new folder in app called metal. Add the file healthy_web.rb with:
8
+
9
+ require 'healthy'
10
+ class HealthyWeb
11
+ @app = Rack::Builder.new {
12
+ map "/status" do
13
+ run Healthy::Server.new
14
+ end
15
+ }.to_app
16
+
17
+ def self.call(env)
18
+ @app.call(env)
19
+ end
20
+ end
21
+
22
+ This will route all requests to /status to the healthy rack app
23
+
24
+ == Adding custom diagnostics
25
+
26
+ Add each diagnostics check to the to
27
+
28
+ config.to_prepare do
29
+ Healthy::Diagnostic.monitor(YourSuperDuperDiagnosticsCheck)
30
+ end
31
+
32
+ == Contributing to healthy
33
+
34
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
35
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
36
+ * Fork the project
37
+ * Start a feature/bugfix branch
38
+ * Commit and push until you are happy with your contribution
39
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
40
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
41
+
42
+ == Copyright
43
+
44
+ Copyright (c) 2011 Matthew Deiters. See LICENSE.txt for
45
+ further details.
46
+
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "healthy"
16
+ gem.homepage = "http://github.com/mdeiters/healthy"
17
+ gem.license = "MIT"
18
+ gem.summary = %Q{A rack app for monitoring application health that can be attached to your rails application}
19
+ gem.description = %Q{A rack app for monitoring application health that can be attached to your rails application}
20
+ gem.email = "mdeiters@gmail.com"
21
+ gem.authors = ["Matthew Deiters"]
22
+ gem.add_runtime_dependency 'sinatra'
23
+ gem.add_development_dependency 'rspec', '> 1.2.3'
24
+ end
25
+ Jeweler::RubygemsDotOrgTasks.new
26
+
27
+ require 'rspec/core'
28
+ require 'rspec/core/rake_task'
29
+ RSpec::Core::RakeTask.new(:spec) do |spec|
30
+ spec.pattern = FileList['spec/**/*_spec.rb']
31
+ end
32
+
33
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
34
+ spec.pattern = 'spec/**/*_spec.rb'
35
+ spec.rcov = true
36
+ end
37
+
38
+ task :default => :spec
39
+
40
+ require 'rake/rdoctask'
41
+ Rake::RDocTask.new do |rdoc|
42
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
43
+
44
+ rdoc.rdoc_dir = 'rdoc'
45
+ rdoc.title = "healthy #{version}"
46
+ rdoc.rdoc_files.include('README*')
47
+ rdoc.rdoc_files.include('lib/**/*.rb')
48
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ begin
5
+ require 'vegas'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'vegas'
9
+ end
10
+ require 'activesupport'
11
+ require 'healthy'
12
+
13
+ Vegas::Runner.new(Healthy::Server, 'healthy-web', {}) do |runner, opts, app|
14
+
15
+ end
@@ -0,0 +1,98 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{healthy}
8
+ s.version = "0.1.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Matthew Deiters"]
12
+ s.date = %q{2011-09-01}
13
+ s.default_executable = %q{healthy-web}
14
+ s.description = %q{A rack app for monitoring application health that can be attached to your rails application}
15
+ s.email = %q{mdeiters@gmail.com}
16
+ s.executables = ["healthy-web"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE.txt",
19
+ "README.rdoc"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ ".rspec",
24
+ "Gemfile",
25
+ "Gemfile.lock",
26
+ "LICENSE.txt",
27
+ "README.rdoc",
28
+ "Rakefile",
29
+ "VERSION",
30
+ "bin/healthy-web",
31
+ "healthy.gemspec",
32
+ "lib/healthy.rb",
33
+ "lib/healthy/base.rb",
34
+ "lib/healthy/diagnostic.rb",
35
+ "lib/healthy/disk_space.rb",
36
+ "lib/healthy/env_check.rb",
37
+ "lib/healthy/gem_list.rb",
38
+ "lib/healthy/public/bg_button_a.gif",
39
+ "lib/healthy/public/bg_button_span.gif",
40
+ "lib/healthy/public/screen.css",
41
+ "lib/healthy/revision_deployed.rb",
42
+ "lib/healthy/router.rb",
43
+ "lib/healthy/server.rb",
44
+ "lib/healthy/server_identity.rb",
45
+ "lib/healthy/views/show.erb",
46
+ "spec/base_spec.rb",
47
+ "spec/diagnostic_spec.rb",
48
+ "spec/router_spec.rb",
49
+ "spec/server_identity_spec.rb",
50
+ "spec/spec_helper.rb"
51
+ ]
52
+ s.homepage = %q{http://github.com/mdeiters/healthy}
53
+ s.licenses = ["MIT"]
54
+ s.require_paths = ["lib"]
55
+ s.rubygems_version = %q{1.6.2}
56
+ s.summary = %q{A rack app for monitoring application health that can be attached to your rails application}
57
+ s.test_files = [
58
+ "spec/base_spec.rb",
59
+ "spec/diagnostic_spec.rb",
60
+ "spec/router_spec.rb",
61
+ "spec/server_identity_spec.rb",
62
+ "spec/spec_helper.rb"
63
+ ]
64
+
65
+ if s.respond_to? :specification_version then
66
+ s.specification_version = 3
67
+
68
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
69
+ s.add_runtime_dependency(%q<sinatra>, [">= 0"])
70
+ s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
71
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
72
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
73
+ s.add_development_dependency(%q<rcov>, [">= 0"])
74
+ s.add_development_dependency(%q<mocha>, [">= 0"])
75
+ s.add_runtime_dependency(%q<sinatra>, [">= 0"])
76
+ s.add_development_dependency(%q<rspec>, ["> 1.2.3"])
77
+ else
78
+ s.add_dependency(%q<sinatra>, [">= 0"])
79
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
80
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
81
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
82
+ s.add_dependency(%q<rcov>, [">= 0"])
83
+ s.add_dependency(%q<mocha>, [">= 0"])
84
+ s.add_dependency(%q<sinatra>, [">= 0"])
85
+ s.add_dependency(%q<rspec>, ["> 1.2.3"])
86
+ end
87
+ else
88
+ s.add_dependency(%q<sinatra>, [">= 0"])
89
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
90
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
91
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
92
+ s.add_dependency(%q<rcov>, [">= 0"])
93
+ s.add_dependency(%q<mocha>, [">= 0"])
94
+ s.add_dependency(%q<sinatra>, [">= 0"])
95
+ s.add_dependency(%q<rspec>, ["> 1.2.3"])
96
+ end
97
+ end
98
+
@@ -0,0 +1,12 @@
1
+ DIAGNOSTIC_LOGGER = defined?(Rails) ? Rails.logger : require('logger') && Logger.new($stdout)
2
+
3
+ require 'healthy/diagnostic'
4
+ require 'healthy/base'
5
+ require 'sinatra'
6
+ require 'healthy/server'
7
+ require 'healthy/server_identity'
8
+ require 'healthy/router'
9
+ require('healthy/gem_list') && Healthy::Diagnostic.monitor(Healthy::GemList)
10
+ require('healthy/env_check') && Healthy::Diagnostic.monitor(Healthy::EnvCheck)
11
+ require('healthy/disk_space') && Healthy::Diagnostic.monitor(Healthy::DiskSpace)
12
+ require('healthy/revision_deployed') && Healthy::Diagnostic.monitor(Healthy::RevisionDeployed)
@@ -0,0 +1,18 @@
1
+ module Healthy
2
+ class Diagnostic
3
+ # all checks are expected to implement methods passed?, warning?, info
4
+ # all tools are expected to implement methods info
5
+ module Base
6
+ def status
7
+ raise "status is not supported on a tool" unless respond_to?(:passed?)
8
+ return :warn if warning? && passed?
9
+ return :fail unless passed?
10
+ return :pass
11
+ end
12
+
13
+ def name
14
+ self.class.name
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,73 @@
1
+ module Healthy
2
+ class Diagnostic
3
+ class << self
4
+ def info_for(normalized_name)
5
+ route = Router.routes[normalized_name]
6
+ if ServerIdentity.matches?(route[:servers])
7
+ route[:klass].new.info
8
+ else
9
+ `curl -H 'Host: #{Diagnostic.site_host}' http://#{route[:servers].first}/status/#{normalized_name}`
10
+ end
11
+ end
12
+
13
+ def status
14
+ status = :pass
15
+ checks.each do |check|
16
+ case check.status
17
+ when :warn
18
+ status = warn
19
+ when :fail
20
+ return :fail
21
+ end
22
+ end
23
+
24
+ status
25
+ end
26
+
27
+ def all_good?
28
+ status == :pass
29
+ end
30
+
31
+ def site_host
32
+ @@site_host
33
+ end
34
+
35
+ def site_host=(value)
36
+ @@site_host=value
37
+ end
38
+
39
+ def monitor(diagnostic)
40
+ @diagnostics ||= []
41
+ exsisting = @diagnostics.detect{|exsisting| exsisting.name == diagnostic.name }
42
+ @diagnostics.delete(exsisting) if exsisting
43
+ @diagnostics << diagnostic
44
+ Router.add_route(diagnostic)
45
+ end
46
+
47
+ def diagnostics
48
+ @diagnostics.uniq.collect(&:new)
49
+ end
50
+
51
+ def flush_diagnostics!
52
+ @diagnostics.uniq.clear
53
+ Router.routes = nil
54
+ end
55
+
56
+ def checks
57
+ diagnostics.select{|diagnostic| diagnostic.respond_to?(:passed?) }.sort{|x,y| extract_name(x) <=> extract_name(y)}
58
+ end
59
+
60
+ def tools
61
+ diagnostics.select{|diagnostic| !diagnostic.respond_to?(:passed?) }.sort{|x,y| extract_name(x) <=> extract_name(y)}
62
+ end
63
+
64
+ def extract_name(obj)
65
+ if obj.respond_to?(:name)
66
+ obj.name
67
+ else
68
+ obj.class.name
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,11 @@
1
+ module Healthy
2
+ class DiskSpace
3
+ def name
4
+ 'Disk Space'
5
+ end
6
+
7
+ def info
8
+ `df`
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Healthy
2
+ class EnvCheck
3
+ def name
4
+ 'Environment'
5
+ end
6
+
7
+ def info
8
+ ENV.keys.sort.map { |key| "#{key}=#{ENV[key]}" }.join("\n")
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ module Healthy
2
+ class GemList
3
+ def name
4
+ 'Installed Ruby And Gems'
5
+ end
6
+
7
+ def info
8
+ [
9
+ "RUBY_VERSION=#{RUBY_VERSION}",
10
+ "RUBY_PATCHLEVEL=#{RUBY_PATCHLEVEL}",
11
+ "RUBY_PLATFORM=#{RUBY_PLATFORM}",
12
+ "RUBY_RELEASE_DATE=#{RUBY_RELEASE_DATE}",
13
+ `gem list`
14
+ ].join("\n")
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,155 @@
1
+ body {
2
+ font-family: 'HelveticaNeue-Light', 'Helvetica Neue Light', 'Helvetica Neue', Arial, Helvetica, sans-serif;
3
+ }
4
+
5
+ #main {
6
+ width: 800px;
7
+ margin: auto;
8
+ background: #fff;
9
+ -webkit-border-radius: 15px;
10
+ -moz-border-radius: 15px;
11
+ border-radius: 15px;
12
+ }
13
+
14
+ p{
15
+ margin: 0;
16
+ padding: 0;
17
+ text-align: center;
18
+ color: #85813B;
19
+ }
20
+
21
+ h1 {
22
+ margin: 0px;
23
+ margin-bottom: 5px;
24
+ font-size: 24px;
25
+ text-align: center;
26
+ font-weight: bold;
27
+ color: #fff;
28
+ }
29
+ h4 {
30
+ margin: 5px 0px 0px 0px;
31
+ text-align: center;
32
+ font-weight: bold;
33
+ color: #fff;
34
+ font-size: 12px;
35
+ }
36
+
37
+ h2 {
38
+ margin: 0px;
39
+ padding: 0px;
40
+ font-size: 18px;
41
+ font-weight: bold;
42
+ text-align: left;
43
+ display: inline;
44
+ }
45
+
46
+ h3 {
47
+ margin: 0px;
48
+ padding: 0px;
49
+ font-size: 14px;
50
+ font-weight: bold;
51
+ text-align: left;
52
+ }
53
+
54
+ ul {
55
+ list-style: none;
56
+ margin: 0;
57
+ padding: 0;
58
+ padding-bottom: 20px;
59
+ }
60
+
61
+ li {
62
+ padding: 20px 20px 0px 20px;
63
+ }
64
+
65
+ li a {
66
+ float: right;
67
+ }
68
+
69
+ .status{
70
+ width: 100px;
71
+ height: 20px;
72
+ text-align: center;
73
+ font-size: 60%;
74
+ line-height: 20px;
75
+ float: left;
76
+ margin-right:20px;
77
+ }
78
+
79
+ .pass{
80
+ background:#6DAF2D;
81
+ }
82
+
83
+ .fail{
84
+ background:#C52D08;
85
+ }
86
+
87
+ .warn{
88
+ background:#EBC809;
89
+ }
90
+
91
+ .info{
92
+ clear:both;
93
+ display:none;
94
+ border-bottom: 1px solid;
95
+ margin-top: 5px;
96
+ margin-bottom: 5px;
97
+ padding: 5px;
98
+ font-size: 12px;
99
+ white-space:pre;
100
+ }
101
+
102
+ .close{
103
+ display:none;
104
+ padding-right: 10px;
105
+ }
106
+
107
+ #headers{
108
+ display: none;
109
+ }
110
+
111
+
112
+ .clear { /* generic container (i.e. div) for floating buttons */
113
+ overflow: hidden;
114
+ width: 100%;
115
+ }
116
+
117
+ a.button {
118
+ background: transparent url('bg_button_a.gif') no-repeat scroll top right;
119
+ color: #444;
120
+ font: normal 12px arial, sans-serif;
121
+ height: 24px;
122
+ margin-right: 6px;
123
+ padding-right: 18px; /* sliding doors padding */
124
+ text-decoration: none;
125
+ }
126
+
127
+ a.button span {
128
+ background: transparent url('bg_button_span.gif') no-repeat;
129
+ display: block;
130
+ line-height: 14px;
131
+ padding: 5px 0 5px 18px;
132
+ }
133
+
134
+ a.button:active {
135
+ background-position: bottom right;
136
+ color: #000;
137
+ outline: none; /* hide dotted outline in Firefox */
138
+ }
139
+
140
+ a.button:active span {
141
+ background-position: bottom left;
142
+ padding: 6px 0 4px 18px; /* push text down 1px */
143
+ }
144
+
145
+ .hide {
146
+ display: none;
147
+ }
148
+
149
+ .left{
150
+ float: left;
151
+ }
152
+
153
+ .right{
154
+ float: right;
155
+ }
@@ -0,0 +1,13 @@
1
+ module Healthy
2
+ class RevisionDeployed
3
+ def name
4
+ 'Subversion Revision'
5
+ end
6
+
7
+ def info
8
+ File.read("#{RAILS_ROOT}/REVISION")
9
+ rescue
10
+ 'Unkown'
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ require 'singleton'
2
+ module Healthy
3
+ class Router
4
+ class << self
5
+ attr_accessor :routes
6
+
7
+ # @param Hash r key should be class, value should be array of servers to run on
8
+ def add(r)
9
+ r.each { |klass, servers| add_route(klass, servers) }
10
+ end
11
+
12
+ def add_route(*route)
13
+ @routes ||= Hash.new{|h, k| h[k] = {}}
14
+ klass = route.first
15
+ # this can be nil
16
+ servers = route[1]
17
+ @routes[normalized_name(klass)][:klass] = klass
18
+ @routes[normalized_name(klass)][:servers] = servers
19
+ end
20
+
21
+ def normalized_name(klass)
22
+ name = klass.respond_to?(:display_name) ? klass.display_name : klass.name
23
+ name.downcase.gsub(" ", "")
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,40 @@
1
+ module Healthy
2
+ class Server < Sinatra::Base
3
+ dir = File.dirname(File.expand_path(__FILE__))
4
+
5
+ set :views, "#{dir}/views"
6
+ set :public, "#{dir}/public"
7
+ set :static, true
8
+
9
+ get '/', :provides => 'html' do
10
+ @checks = Diagnostic.checks
11
+ @tools = Diagnostic.tools
12
+ erb :show
13
+ end
14
+
15
+ get '/up.txt' do
16
+ content_type 'text/plain'
17
+ if Healthy::Diagnostic.all_good?
18
+ return halt(200, 'PASS')
19
+ else
20
+ return halt(500, 'FAIL')
21
+ end
22
+ end
23
+
24
+ get '/:info' do
25
+ Healthy::Diagnostic.info_for(params[:info])
26
+ end
27
+
28
+ helpers do
29
+ def url_for(path)
30
+ "#{request.env['SCRIPT_NAME']}/#{path}"
31
+ end
32
+
33
+ def display_name(check)
34
+ klass = check.class
35
+ klass.respond_to?(:display_name) ? klass.display_name : klass.name
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,35 @@
1
+ require 'singleton'
2
+ module Healthy
3
+ class ServerIdentity
4
+ class << self
5
+ attr_accessor :identity
6
+ def establish(string_or_method_name = nil, &block)
7
+ self.identity = if block
8
+ yield
9
+ elsif string_or_method_name.is_a? Symbol
10
+ self.send(string_or_method_name)
11
+ else
12
+ string_or_method_name
13
+ end
14
+ if identity.nil? || identity.empty?
15
+ raise ArgumentError, "server identity cannot be nil or empty"
16
+ end
17
+ end
18
+
19
+ def matches?(test)
20
+ test.nil? || test.empty? || test == identity || test.is_a?(Array) && test.include?(identity)
21
+ end
22
+
23
+ def fqdn
24
+ fqdn = `hostname --fqdn`.strip
25
+ fqdn = `hostname`.strip if fqdn == ''
26
+ fqdn
27
+ end
28
+
29
+ # TODO
30
+ def ip_and_port
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,90 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
5
+ <link rel='stylesheet' href='<%= url_for("screen.css") %>' type='text/css' media='screen' />
6
+ <title>Status : <%= Healthy::Diagnostic.status.to_s.upcase %></title>
7
+ </head>
8
+ <body class="<%= Healthy::Diagnostic.status %>">
9
+ <h1><%= Healthy::Diagnostic.status.to_s.upcase %></h1>
10
+ <div id="main">
11
+ <ul>
12
+ <% @checks.each do |check| %>
13
+ <% normalized_name = Healthy::Router.normalized_name(check.class) %>
14
+ <li id="<%= normalized_name %>">
15
+ <h2>
16
+ <div class="status <%= check.status %>"><%= check.status.to_s.upcase %></div>
17
+ <%= display_name(check) %>
18
+ </h2>
19
+ <% if check.respond_to?(:info) %>
20
+ <a href="<%= url_for(normalized_name) %>" class='remote button'><span>run</span></a>
21
+ <% end %>
22
+ <a href="#" class='close button hide' onclick="$(this).hide().siblings('.info').slideUp(); return false;"><span>close</span></a>
23
+ <div class='info'></div>
24
+ </li>
25
+ <% end %>
26
+ </ul>
27
+ <ul id='tools'>
28
+ <% @tools.each do |check| %>
29
+ <% normalized_name = Healthy::Router.normalized_name(check.class) %>
30
+ <li id="<%=normalized_name %>">
31
+ <h2>
32
+ <div class="status"></div>
33
+ <%= display_name(check) %>
34
+ </h2>
35
+ <a href="<%= url_for(normalized_name) %>" class='remote button'><span>run</span></a>
36
+ <a href="#" class='close button hide' onclick="$(this).hide().siblings('.info').slideUp(); return false;"><span>close</span></a>
37
+ <div class='info'></div>
38
+ </li>
39
+ <% end %>
40
+ </ul>
41
+
42
+ <h4 id="refresh-msg" class='left'></h4>
43
+ <h4 class='right'>
44
+ <%= Healthy::ServerIdentity.identity %>
45
+ <a href='#headers' onclick="$('#headers').toggle(); return false;">(show headers)</a>
46
+ </h4>
47
+ <div id='headers'><h6><%= request.env.keys.sort.collect{|n| "#{n} = #{request.env[n]}" }.join('<br>') %></h6></div>
48
+ </div>
49
+ </body>
50
+ <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
51
+ <script type="text/JavaScript">
52
+ var pause = false;
53
+
54
+ function reload(){
55
+ if(!pause) location.reload(true);
56
+ }
57
+
58
+ function updateRefreshMessage(){
59
+ var element = document.getElementById('refresh-msg');
60
+ if(pause){
61
+ element.innerHTML = 'page refresh disabled'
62
+ return true;
63
+ }
64
+
65
+ var seconds = parseInt(element.innerHTML.replace(/\D/g,''));
66
+ if(isNaN(seconds))
67
+ element.innerHTML = "The page will refresh in 30 seconds";
68
+ else
69
+ element.innerHTML = "The page will refresh in " + (seconds - 1) + " seconds";
70
+ setTimeout('updateRefreshMessage();', 1000);
71
+ }
72
+
73
+ $(document).ready(function(){
74
+ $('a.remote').bind('click', function(){
75
+ pause = true;
76
+ var close = $(this).siblings('.close');
77
+ var info = $(this).siblings('.info');
78
+ $.get($(this).attr('href'), function(data){
79
+ info.html(data).slideDown();
80
+ close.children('span').text('hide');
81
+ });
82
+ close.show().children('span').text('(processing)');
83
+ return false;
84
+ });
85
+
86
+ updateRefreshMessage();
87
+ setTimeout("reload();", 30 * 1000);
88
+ });
89
+ </script>
90
+ </html>
@@ -0,0 +1,36 @@
1
+ require 'spec/spec_helper'
2
+
3
+ module Healthy
4
+
5
+ describe Diagnostic::Base do
6
+ before(:all) do
7
+ class Check
8
+ include Healthy::Diagnostic::Base
9
+ end
10
+ end
11
+ it 'returns a status of :pass if a diagnostic indicates passed? and there are no warnings' do
12
+ test_diagnotic = Class.new(Check) do
13
+ def warning?; false; end
14
+ def passed?; true; end
15
+ end
16
+ test_diagnotic.new.status.should == :pass
17
+ end
18
+
19
+ it 'returns a status of :warn if a diagnostic indicates passed? but there are warnings' do
20
+ test_diagnotic = Class.new(Check) do
21
+ def warning?; true; end
22
+ def passed?; true; end
23
+ end
24
+ test_diagnotic.new.status.should == :warn
25
+ end
26
+
27
+ it 'returns a status of :fail when it did not pass' do
28
+ test_diagnotic = Class.new(Check) do
29
+ def warning?; true; end
30
+ def passed?; false; end
31
+ end
32
+ test_diagnotic.new.status.should == :fail
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec/spec_helper'
2
+
3
+ module Healthy
4
+ describe Diagnostic do
5
+ before :each do
6
+ Diagnostic.flush_diagnostics!
7
+ end
8
+
9
+ it 'considers tools as anything in diagnostic array that does not have the passed? method but has info' do
10
+ test_tool_class = Class.new do
11
+ def info; end
12
+ end
13
+ Diagnostic.monitor(test_tool_class)
14
+ Diagnostic.tools.first.should be_instance_of(test_tool_class)
15
+ end
16
+
17
+ it 'considers checks as anything in diagnostic array that has the passed? method' do
18
+ test_check_class = Class.new do
19
+ def passed?; end
20
+ def info; end
21
+ end
22
+ Diagnostic.monitor(test_check_class)
23
+ Diagnostic.checks.first.should be_instance_of(test_check_class)
24
+ end
25
+
26
+
27
+ it 'should only have one instance of diagnostic even if multiple exsist so development autoreloading works' do
28
+ test_check_class = Class.new(Object)
29
+ test_check_class.class_eval do
30
+ include Diagnostic::Base
31
+ end
32
+ Diagnostic.monitor(test_check_class)
33
+ Diagnostic.monitor(test_check_class)
34
+ loaded_diagnostics = Diagnostic.instance_variable_get("@diagnostics")
35
+ loaded_diagnostics.uniq.should == loaded_diagnostics
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec/spec_helper'
2
+
3
+ class WarholCheck
4
+ end
5
+
6
+ class DuchampCheck
7
+ def self.display_name
8
+ "Moustache Check"
9
+ end
10
+ end
11
+
12
+ module Healthy
13
+ describe Router do
14
+ before(:all) do
15
+ Diagnostic.flush_diagnostics!
16
+ end
17
+
18
+ describe ".add" do
19
+ it "should convert they given key/value pair" do
20
+ Router.add({
21
+ WarholCheck => ["localhost:3000"],
22
+ DuchampCheck => ["localhost:3001"]
23
+ })
24
+
25
+ Router.routes.should == {
26
+ "warholcheck" => {
27
+ :servers => ["localhost:3000"],
28
+ :klass => WarholCheck
29
+ },
30
+ "moustachecheck" => {
31
+ :servers => ["localhost:3001"],
32
+ :klass => DuchampCheck
33
+ }
34
+ }
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec/spec_helper'
2
+
3
+ module Healthy
4
+
5
+ describe ServerIdentity do
6
+ before { ServerIdentity.identity = nil }
7
+ describe ".establish" do
8
+ it "should set the identity when given a string" do
9
+ ServerIdentity.establish("test")
10
+ ServerIdentity.identity.should == "test"
11
+ end
12
+
13
+ it "should set the identity when given a block" do
14
+ ServerIdentity.establish { "block" }
15
+ ServerIdentity.identity.should == "block"
16
+ end
17
+
18
+ it "should set the identity when given a symbol of a method name" do
19
+ class Healthy::ServerIdentity
20
+ def self.test_method
21
+ "test_method"
22
+ end
23
+ end
24
+
25
+ ServerIdentity.establish(:test_method)
26
+ ServerIdentity.identity.should == "test_method"
27
+ end
28
+ end
29
+
30
+ describe ".matches?" do
31
+ it "should match if the argument given is nil or empty" do
32
+ ServerIdentity.matches?(nil).should == true
33
+ ServerIdentity.matches?([]).should == true
34
+ end
35
+
36
+ it "should match if the argument is the same as the identity" do
37
+ ServerIdentity.establish("match")
38
+ ServerIdentity.matches?("match").should == true
39
+ end
40
+
41
+ it "should match if the argument is an array containing the identity" do
42
+ ServerIdentity.establish("match")
43
+ ServerIdentity.matches?(["does not match", "match"]).should == true
44
+ end
45
+
46
+ it "should not match if none of the above are true" do
47
+ ServerIdentity.establish("match")
48
+ ServerIdentity.matches?("does not match").should == false
49
+ ServerIdentity.matches?(["does not match"]).should == false
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'healthy'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+ config.mock_with :mocha
12
+ end
metadata ADDED
@@ -0,0 +1,219 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: healthy
3
+ version: !ruby/object:Gem::Version
4
+ hash: 25
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 1
10
+ version: 0.1.1
11
+ platform: ruby
12
+ authors:
13
+ - Matthew Deiters
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-09-01 00:00:00 -10:00
19
+ default_executable: healthy-web
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ version_requirements: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ hash: 3
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ requirement: *id001
32
+ prerelease: false
33
+ name: sinatra
34
+ type: :runtime
35
+ - !ruby/object:Gem::Dependency
36
+ version_requirements: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ hash: 3
42
+ segments:
43
+ - 2
44
+ - 3
45
+ - 0
46
+ version: 2.3.0
47
+ requirement: *id002
48
+ prerelease: false
49
+ name: rspec
50
+ type: :development
51
+ - !ruby/object:Gem::Dependency
52
+ version_requirements: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ~>
56
+ - !ruby/object:Gem::Version
57
+ hash: 23
58
+ segments:
59
+ - 1
60
+ - 0
61
+ - 0
62
+ version: 1.0.0
63
+ requirement: *id003
64
+ prerelease: false
65
+ name: bundler
66
+ type: :development
67
+ - !ruby/object:Gem::Dependency
68
+ version_requirements: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ~>
72
+ - !ruby/object:Gem::Version
73
+ hash: 7
74
+ segments:
75
+ - 1
76
+ - 5
77
+ - 2
78
+ version: 1.5.2
79
+ requirement: *id004
80
+ prerelease: false
81
+ name: jeweler
82
+ type: :development
83
+ - !ruby/object:Gem::Dependency
84
+ version_requirements: &id005 !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ hash: 3
90
+ segments:
91
+ - 0
92
+ version: "0"
93
+ requirement: *id005
94
+ prerelease: false
95
+ name: rcov
96
+ type: :development
97
+ - !ruby/object:Gem::Dependency
98
+ version_requirements: &id006 !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ hash: 3
104
+ segments:
105
+ - 0
106
+ version: "0"
107
+ requirement: *id006
108
+ prerelease: false
109
+ name: mocha
110
+ type: :development
111
+ - !ruby/object:Gem::Dependency
112
+ version_requirements: &id007 !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ hash: 3
118
+ segments:
119
+ - 0
120
+ version: "0"
121
+ requirement: *id007
122
+ prerelease: false
123
+ name: sinatra
124
+ type: :runtime
125
+ - !ruby/object:Gem::Dependency
126
+ version_requirements: &id008 !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ">"
130
+ - !ruby/object:Gem::Version
131
+ hash: 25
132
+ segments:
133
+ - 1
134
+ - 2
135
+ - 3
136
+ version: 1.2.3
137
+ requirement: *id008
138
+ prerelease: false
139
+ name: rspec
140
+ type: :development
141
+ description: A rack app for monitoring application health that can be attached to your rails application
142
+ email: mdeiters@gmail.com
143
+ executables:
144
+ - healthy-web
145
+ extensions: []
146
+
147
+ extra_rdoc_files:
148
+ - LICENSE.txt
149
+ - README.rdoc
150
+ files:
151
+ - .document
152
+ - .rspec
153
+ - Gemfile
154
+ - Gemfile.lock
155
+ - LICENSE.txt
156
+ - README.rdoc
157
+ - Rakefile
158
+ - VERSION
159
+ - bin/healthy-web
160
+ - healthy.gemspec
161
+ - lib/healthy.rb
162
+ - lib/healthy/base.rb
163
+ - lib/healthy/diagnostic.rb
164
+ - lib/healthy/disk_space.rb
165
+ - lib/healthy/env_check.rb
166
+ - lib/healthy/gem_list.rb
167
+ - lib/healthy/public/bg_button_a.gif
168
+ - lib/healthy/public/bg_button_span.gif
169
+ - lib/healthy/public/screen.css
170
+ - lib/healthy/revision_deployed.rb
171
+ - lib/healthy/router.rb
172
+ - lib/healthy/server.rb
173
+ - lib/healthy/server_identity.rb
174
+ - lib/healthy/views/show.erb
175
+ - spec/base_spec.rb
176
+ - spec/diagnostic_spec.rb
177
+ - spec/router_spec.rb
178
+ - spec/server_identity_spec.rb
179
+ - spec/spec_helper.rb
180
+ has_rdoc: true
181
+ homepage: http://github.com/mdeiters/healthy
182
+ licenses:
183
+ - MIT
184
+ post_install_message:
185
+ rdoc_options: []
186
+
187
+ require_paths:
188
+ - lib
189
+ required_ruby_version: !ruby/object:Gem::Requirement
190
+ none: false
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ hash: 3
195
+ segments:
196
+ - 0
197
+ version: "0"
198
+ required_rubygems_version: !ruby/object:Gem::Requirement
199
+ none: false
200
+ requirements:
201
+ - - ">="
202
+ - !ruby/object:Gem::Version
203
+ hash: 3
204
+ segments:
205
+ - 0
206
+ version: "0"
207
+ requirements: []
208
+
209
+ rubyforge_project:
210
+ rubygems_version: 1.6.2
211
+ signing_key:
212
+ specification_version: 3
213
+ summary: A rack app for monitoring application health that can be attached to your rails application
214
+ test_files:
215
+ - spec/base_spec.rb
216
+ - spec/diagnostic_spec.rb
217
+ - spec/router_spec.rb
218
+ - spec/server_identity_spec.rb
219
+ - spec/spec_helper.rb