ops_routes 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +21 -0
- data/README.rdoc +62 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/app/controllers/ops_controller.rb +12 -0
- data/config/routes.rb +5 -0
- data/init.rb +1 -0
- data/install.rb +1 -0
- data/lib/ops_routes/middleware.rb +27 -0
- data/lib/ops_routes.rb +115 -0
- data/lib/tasks/ops_routes.rake +4 -0
- data/lib/views/version.html.haml +45 -0
- data/spec/app/controllers/ops_controller_spec.rb +35 -0
- data/spec/ops_routes/middleware_spec.rb +74 -0
- data/spec/ops_routes_spec.rb +220 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +24 -0
- data/uninstall.rb +1 -0
- metadata +147 -0
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2010 PRIMEDIA Inc.
|
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.
|
21
|
+
|
data/README.rdoc
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
= Operations Routes
|
2
|
+
|
3
|
+
This library provides a Rails plugin and a piece of Rack middleware for monitoring application status. When using with a Rails application, the plugin is preferred, but the middleware is available for use in non-Rails Rack applications.
|
4
|
+
|
5
|
+
*Note:* Previous versions of this library were available as the gem <tt>operations_middleware</tt> and included only the middleware portion.
|
6
|
+
|
7
|
+
== Installation
|
8
|
+
|
9
|
+
To include it in a Rails application, install the plugin:
|
10
|
+
|
11
|
+
script/plugin install https://duien@github.com/primedia/ops_routes.git
|
12
|
+
|
13
|
+
Then add the following minimum configuration to <tt>config/initializers/ops_routes.rb</tt>:
|
14
|
+
|
15
|
+
OpsRoutes.config do |ops|
|
16
|
+
ops.file_root = RAILS_ROOT
|
17
|
+
end
|
18
|
+
|
19
|
+
To include it in a Sinatra application, install the gem <tt>ops_routes</tt> then add the following to your app:
|
20
|
+
|
21
|
+
require 'ops_routes/middleware'
|
22
|
+
use OpsRoutes::Middleware do |ops|
|
23
|
+
ops.file_root = File.dirname(__FILE__)
|
24
|
+
end
|
25
|
+
|
26
|
+
== Version Page
|
27
|
+
|
28
|
+
This will provide a page at <tt>/ops/version</tt> which shows, in development mode, the current git branch and last commit SHA. In all other modes, it shows the same information, but taken from the contents of VERSION and REVISION files in the <tt>file_root</tt> directory. If you are deploying with Capistrano, the REVISION file will be created for you. If you want the VERSION file, you'll need to add that to your project or your Capistrano scripts.
|
29
|
+
|
30
|
+
== Heartbeat Page
|
31
|
+
|
32
|
+
It also provides a simple page at <tt>/ops/heartbeat</tt> which returns a 200 'OK' as long as the application is running.
|
33
|
+
|
34
|
+
The application name is taken from the <tt>file_root</tt> option. If that folder is a Capistrano timestamped release, it goes up 2 additional levels for the name, stripping <tt>.com</tt> from the end of the directory if necessary. If this logic does not get the correct application name, it can be set manually in the configuration block:
|
35
|
+
|
36
|
+
ops.app_name = 'Custom Application Name'
|
37
|
+
|
38
|
+
=== Adding Custom Heartbeats
|
39
|
+
|
40
|
+
Additionally, you can specify custom heartbeat monitoring pages as follows:
|
41
|
+
|
42
|
+
ops.add_heartbeat :mysql do
|
43
|
+
conn = ActiveRecord::Base.connection
|
44
|
+
migrations = conn.select_all("SELECT COUNT(1) FROM schema_migrations;")
|
45
|
+
conn.disconnect!
|
46
|
+
end
|
47
|
+
|
48
|
+
The mysql example shown above would be accessed at <tt>ops/heartbeat/mysql</tt>. The heartbeat page will return a 200 'OK' as long as the provided block does not raise an error. If an error is raised or the heartbeat does not exist, a 500 will be returned instead.
|
49
|
+
|
50
|
+
== Note on Patches/Pull Requests
|
51
|
+
|
52
|
+
* Fork the project.
|
53
|
+
* Make your feature addition or bug fix.
|
54
|
+
* Add tests for it. This is important so I don't break it in a
|
55
|
+
future version unintentionally.
|
56
|
+
* Commit, do not mess with rakefile, version, or history.
|
57
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
58
|
+
* Send me a pull request. Bonus points for topic branches.
|
59
|
+
|
60
|
+
== Copyright
|
61
|
+
|
62
|
+
Copyright (c) 2010 PRIMEDIA Inc. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "ops_routes"
|
8
|
+
gem.summary = %Q{Plugin or middleware that provides version and heartbeat pages for app monitoring}
|
9
|
+
gem.description = %Q{Installs as Rails plugin or Rack middleware for non-Rails apps}
|
10
|
+
gem.email = ""
|
11
|
+
gem.homepage = "http://github.com/primedia/ops_routes"
|
12
|
+
gem.authors = ["Primedia Team"]
|
13
|
+
gem.add_dependency "rack", "~> 1.0"
|
14
|
+
gem.add_dependency "haml", "~> 3.0.0"
|
15
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
16
|
+
gem.add_development_dependency "rack-test"
|
17
|
+
gem.add_development_dependency 'rspec_tag_matchers'
|
18
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
19
|
+
end
|
20
|
+
Jeweler::GemcutterTasks.new
|
21
|
+
rescue LoadError
|
22
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'spec/rake/spectask'
|
26
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
27
|
+
spec.libs << 'lib' << 'spec'
|
28
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
29
|
+
end
|
30
|
+
|
31
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
32
|
+
spec.libs << 'lib' << 'spec'
|
33
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
34
|
+
spec.rcov = true
|
35
|
+
end
|
36
|
+
|
37
|
+
task :spec => :check_dependencies
|
38
|
+
|
39
|
+
task :default => :spec
|
40
|
+
|
41
|
+
require 'rake/rdoctask'
|
42
|
+
Rake::RDocTask.new do |rdoc|
|
43
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
44
|
+
|
45
|
+
rdoc.rdoc_dir = 'rdoc'
|
46
|
+
rdoc.title = "ops_routes #{version}"
|
47
|
+
rdoc.rdoc_files.include('README*')
|
48
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
49
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.4.0
|
data/config/routes.rb
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
ActionController::Routing::Routes.draw do |map|
|
2
|
+
map.connect '/ops/version', :controller => 'ops', :action => 'version'
|
3
|
+
map.connect '/ops/heartbeat', :controller => 'ops', :action => 'heartbeat'
|
4
|
+
map.connect '/ops/heartbeat/:name', :controller => 'ops', :action => 'heartbeat'
|
5
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Include hook code here
|
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Install hook code here
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.join(File.dirname(File.dirname(__FILE__)), 'ops_routes')
|
2
|
+
|
3
|
+
module OpsRoutes
|
4
|
+
class Middleware
|
5
|
+
def initialize(app)
|
6
|
+
@app = app
|
7
|
+
yield OpsRoutes if block_given?
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
return @app.call(env) unless env['PATH_INFO'] =~ %r{^/ops/(heartbeat|version)(?:/(\w+))?/?$}
|
12
|
+
route, heartbeat_name = $1, $2
|
13
|
+
case route
|
14
|
+
when 'heartbeat'
|
15
|
+
heartbeat_result = OpsRoutes.check_heartbeat(heartbeat_name)
|
16
|
+
headers = { 'Content-Type' => 'text/plain',
|
17
|
+
'Content-Length' => heartbeat_result[:text].length.to_s }
|
18
|
+
[ heartbeat_result[:status], headers, [heartbeat_result[:text]] ]
|
19
|
+
when 'version'
|
20
|
+
version_result = OpsRoutes.check_version(env)
|
21
|
+
headers = { 'Content-Type' => 'text/html',
|
22
|
+
'Content-Length' => version_result.length.to_s }
|
23
|
+
[ 200, headers, [version_result] ]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/ops_routes.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'haml'
|
2
|
+
|
3
|
+
module OpsRoutes
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
attr_writer :app_name
|
8
|
+
attr_accessor :file_root
|
9
|
+
|
10
|
+
def config
|
11
|
+
yield OpsRoutes if block_given?
|
12
|
+
end
|
13
|
+
# Heartbeats
|
14
|
+
|
15
|
+
def heartbeats
|
16
|
+
@heartbeats ||= {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_heartbeat(name, &block)
|
20
|
+
heartbeats[name] = block
|
21
|
+
end
|
22
|
+
|
23
|
+
def check_heartbeat(name = nil)
|
24
|
+
if name
|
25
|
+
begin
|
26
|
+
heartbeats[name.to_sym].call
|
27
|
+
return { :status => 200, :text => "#{name} is OK" }
|
28
|
+
rescue
|
29
|
+
return { :status => 500, :text => "#{name} does not have a heartbeat" }
|
30
|
+
end
|
31
|
+
else
|
32
|
+
return { :status => 200, :text => 'OK' }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Version page
|
37
|
+
|
38
|
+
def check_version(headers={})
|
39
|
+
@headers = headers
|
40
|
+
version_template = File.join(File.dirname(__FILE__), 'views', 'version.html.haml')
|
41
|
+
Haml::Engine.new(File.read(version_template)).render(self)
|
42
|
+
end
|
43
|
+
|
44
|
+
def version_or_branch
|
45
|
+
@version ||= if File.exists?(version_file)
|
46
|
+
File.read(version_file).chomp.gsub('^{}', '')
|
47
|
+
elsif environment == 'development' && `git branch` =~ /^\* (.*)$/
|
48
|
+
$1
|
49
|
+
else
|
50
|
+
'Unknown (VERSION file is missing)'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def version_file
|
55
|
+
@version_file ||= File.join(file_root, 'VERSION')
|
56
|
+
end
|
57
|
+
|
58
|
+
def environment
|
59
|
+
ENV['RAILS_ENV']
|
60
|
+
end
|
61
|
+
|
62
|
+
def deploy_date
|
63
|
+
@deploy_date ||= if File.exists?(version_file)
|
64
|
+
File.stat(version_file).mtime
|
65
|
+
elsif environment == 'development'
|
66
|
+
'Live'
|
67
|
+
else
|
68
|
+
'Unknown (VERSION file is missing)'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def last_commit
|
73
|
+
@last_commit ||= if File.exists?(revision_file)
|
74
|
+
File.read(revision_file).chomp
|
75
|
+
elsif environment == 'development' && `git show` =~ /^commit (.*)$/
|
76
|
+
$1
|
77
|
+
else
|
78
|
+
'Unknown (REVISION file is missing)'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def revision_file
|
83
|
+
@revision_file ||= File.join(file_root, 'REVISION')
|
84
|
+
end
|
85
|
+
|
86
|
+
def hostname
|
87
|
+
@hostname ||= `/bin/hostname` || 'Unknown'
|
88
|
+
end
|
89
|
+
|
90
|
+
def app_name
|
91
|
+
@app_name ||= begin
|
92
|
+
dirs = Dir.pwd.split('/')
|
93
|
+
if dirs.last =~ /^\d+$/
|
94
|
+
dirs[-3]
|
95
|
+
else
|
96
|
+
dirs.last
|
97
|
+
end.sub(/\.com$/, '')
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def headers
|
102
|
+
@headers.select{|k,v| k.match(/^[-A-Z_].*$/) }
|
103
|
+
end
|
104
|
+
|
105
|
+
def version_link
|
106
|
+
"https://github.com/primedia/#{app_name}/tree/#{version_or_branch}" unless version_or_branch =~ /^Unknown/
|
107
|
+
end
|
108
|
+
|
109
|
+
def last_commit_link
|
110
|
+
"https://github.com/primedia/#{app_name}/commit/#{last_commit}" unless version_or_branch =~ /^Unknown/
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
!!!
|
2
|
+
%head
|
3
|
+
%title= app_name
|
4
|
+
:css
|
5
|
+
.container {font-size: 20px; width: 1000px; padding-bottom: 100px;}
|
6
|
+
.label {float: left; width: 400px; font-weight: bold}
|
7
|
+
.spacer {clear: both; padding: 10px;}
|
8
|
+
.value {float: left; width: 500px; text-align: right;}
|
9
|
+
#headers .value {clear: both; width: 1000px; text-align: left;}
|
10
|
+
#headers .value tr.even {background: #dddddd;}
|
11
|
+
.clear {clear: both;}
|
12
|
+
%body
|
13
|
+
.container
|
14
|
+
.spacer
|
15
|
+
#version
|
16
|
+
.label= "#{app_name} Version"
|
17
|
+
.value
|
18
|
+
%a{:href => version_link}= version_or_branch
|
19
|
+
.spacer
|
20
|
+
#date
|
21
|
+
.label Date Deployed
|
22
|
+
.value= deploy_date
|
23
|
+
.spacer
|
24
|
+
#commit
|
25
|
+
.label Last Commit
|
26
|
+
.value
|
27
|
+
%a{:href => last_commit_link}= last_commit
|
28
|
+
.spacer
|
29
|
+
#host
|
30
|
+
.label Host
|
31
|
+
.value= hostname
|
32
|
+
.spacer
|
33
|
+
#environment
|
34
|
+
.label Environment
|
35
|
+
.value= environment
|
36
|
+
.spacer
|
37
|
+
#headers
|
38
|
+
.label Headers
|
39
|
+
.value
|
40
|
+
%table
|
41
|
+
- headers.sort_by{ |h| h.first }.each_with_index do |(header, value), i|
|
42
|
+
%tr{ :class => i%2==0 ? 'even' : nil }
|
43
|
+
%td= header
|
44
|
+
%td= value
|
45
|
+
.clear
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# Weird hacky controller specs since spec/rails explodes outside a Rails app
|
4
|
+
describe OpsController do
|
5
|
+
let(:controller) do
|
6
|
+
c = OpsController.new
|
7
|
+
c.stub(:request).and_return(mock('request', :headers => {}))
|
8
|
+
c.stub(:response).and_return(mock('response', :content_type= => true))
|
9
|
+
c
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should respond to version" do
|
13
|
+
version_page = mock('version page')
|
14
|
+
OpsRoutes.should_receive(:check_version).and_return(version_page)
|
15
|
+
controller.should_receive(:render).with(:text => version_page)
|
16
|
+
controller.version
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should respond to heartbeat for unnamed heartbeat" do
|
20
|
+
controller.stub(:params).and_return({})
|
21
|
+
heartbeat_page = mock('heartbeat page')
|
22
|
+
OpsRoutes.should_receive(:check_heartbeat).with(nil).and_return(heartbeat_page)
|
23
|
+
controller.should_receive(:render).with(heartbeat_page)
|
24
|
+
controller.heartbeat
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should respond to heartbeat for named heartbeat" do
|
28
|
+
controller.stub(:params).and_return({:name => 'foo'})
|
29
|
+
heartbeat_page = mock('heartbeat page')
|
30
|
+
OpsRoutes.should_receive(:check_heartbeat).with('foo').and_return(heartbeat_page)
|
31
|
+
controller.should_receive(:render).with(heartbeat_page)
|
32
|
+
controller.heartbeat
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OpsRoutes::Middleware do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@parent_app = mock("Rails App")
|
7
|
+
@parent_app.stub(:call).and_return([200, {}, ['Parent App']])
|
8
|
+
@parent_app.stub(:env).and_return({})
|
9
|
+
end
|
10
|
+
|
11
|
+
def app
|
12
|
+
@app ||= OpsRoutes::Middleware.new(@parent_app) do |ops|
|
13
|
+
ops.file_root = File.join(File.dirname(__FILE__), '..')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "passing routes" do
|
18
|
+
|
19
|
+
it "should pass to parent app for /" do
|
20
|
+
@parent_app.should_receive(:call)
|
21
|
+
get '/'
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should pass to parent app for /Georgia/Atlanta" do
|
25
|
+
@parent_app.should_receive(:call)
|
26
|
+
get '/Georgia/Atlanta'
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
context "captured routes" do
|
32
|
+
|
33
|
+
context "version" do
|
34
|
+
|
35
|
+
it "should not pass to parent app for '/ops/version'" do
|
36
|
+
@parent_app.should_not_receive(:call)
|
37
|
+
get '/ops/version'
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should call check_version" do
|
41
|
+
OpsRoutes.should_receive(:check_version).and_return("The version page")
|
42
|
+
get '/ops/version'
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
context "heartbeat" do
|
48
|
+
|
49
|
+
it "should not pass to parent app for '/ops/heartbeat'" do
|
50
|
+
@parent_app.should_not_receive(:call)
|
51
|
+
get '/ops/version'
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should call check_heartbeat' do
|
55
|
+
OpsRoutes.should_receive(:check_heartbeat).with(nil).and_return(
|
56
|
+
:status => 200,
|
57
|
+
:text => 'OK')
|
58
|
+
get '/ops/heartbeat'
|
59
|
+
end
|
60
|
+
|
61
|
+
context "named heartbeat" do
|
62
|
+
it 'should call check_heartbeat with heartbeat name' do
|
63
|
+
OpsRoutes.should_receive(:check_heartbeat).with('foo').and_return(
|
64
|
+
:status => 200,
|
65
|
+
:text => 'OK')
|
66
|
+
get '/ops/heartbeat/foo'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,220 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OpsRoutes do
|
4
|
+
|
5
|
+
before do
|
6
|
+
OpsRoutes.instance_variables.each do |var|
|
7
|
+
OpsRoutes.method(:remove_instance_variable).call(var)
|
8
|
+
end
|
9
|
+
OpsRoutes.stub(:file_root).and_return(File.dirname(__FILE__))
|
10
|
+
end
|
11
|
+
|
12
|
+
describe ".check_version" do
|
13
|
+
it "should have version link" do
|
14
|
+
OpsRoutes.stub(:app_name).and_return('App')
|
15
|
+
OpsRoutes.stub(:version_or_branch).and_return('branch')
|
16
|
+
version_page = OpsRoutes.check_version({})
|
17
|
+
version_page.should have_tag('#version .label', 'App Version')
|
18
|
+
version_page.should have_tag('#version .value a', 'branch')
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should have date deployed" do
|
22
|
+
OpsRoutes.stub(:deploy_date).and_return('now')
|
23
|
+
version_page = OpsRoutes.check_version({})
|
24
|
+
version_page.should have_tag('#date .label', 'Date Deployed')
|
25
|
+
version_page.should have_tag('#date .value', 'now')
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should have last commit" do
|
29
|
+
OpsRoutes.stub(:last_commit).and_return('mock_sha')
|
30
|
+
version_page = OpsRoutes.check_version({})
|
31
|
+
version_page.should have_tag('#commit .label', 'Last Commit')
|
32
|
+
version_page.should have_tag('#commit .value a', 'mock_sha')
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should have host" do
|
36
|
+
OpsRoutes.stub(:hostname).and_return('host')
|
37
|
+
version_page = OpsRoutes.check_version({})
|
38
|
+
version_page.should have_tag('#host .label', 'Host')
|
39
|
+
version_page.should have_tag('#host .value', 'host')
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should have headers" do
|
43
|
+
OpsRoutes.stub(:headers).and_return({'header' => 'value'})
|
44
|
+
version_page = OpsRoutes.check_version({})
|
45
|
+
version_page.should have_tag('#headers .label', 'Headers')
|
46
|
+
version_page.should have_tag('#headers .value td', 'header')
|
47
|
+
version_page.should have_tag('#headers .value td', 'value')
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should have environment" do
|
51
|
+
OpsRoutes.stub(:environment).and_return('env')
|
52
|
+
version_page = OpsRoutes.check_version({})
|
53
|
+
version_page.should have_tag('#environment .label', 'Environment')
|
54
|
+
version_page.should have_tag('#environment .value', 'env')
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should render a valid html page" do
|
58
|
+
version_page = OpsRoutes.check_version({})
|
59
|
+
version_page.should have_tag('html')
|
60
|
+
version_page.should have_tag('head')
|
61
|
+
version_page.should have_tag('body')
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
describe ".check_heartbeat" do
|
67
|
+
|
68
|
+
it "should return 200 'OK' with no params" do
|
69
|
+
heartbeat = OpsRoutes.check_heartbeat
|
70
|
+
heartbeat[:status].should == 200
|
71
|
+
heartbeat[:text].should == 'OK'
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should return 200 '<name> is OK' with named heartbeat" do
|
75
|
+
OpsRoutes.add_heartbeat(:foo) do
|
76
|
+
"All is well"
|
77
|
+
end
|
78
|
+
heartbeat = OpsRoutes.check_heartbeat('foo')
|
79
|
+
heartbeat[:status].should == 200
|
80
|
+
heartbeat[:text].should == 'foo is OK'
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should return 500 '<name> does not have a heartbeat' with invalid named heartbeat" do
|
84
|
+
heartbeat = OpsRoutes.check_heartbeat('foo')
|
85
|
+
heartbeat[:status].should == 500
|
86
|
+
heartbeat[:text].should == 'foo does not have a heartbeat'
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should return 500 '<name> does not have a heartbeat' if heartbeat fails" do
|
90
|
+
OpsRoutes.add_heartbeat(:foo) do
|
91
|
+
raise
|
92
|
+
end
|
93
|
+
heartbeat = OpsRoutes.check_heartbeat('foo')
|
94
|
+
heartbeat[:status].should == 500
|
95
|
+
heartbeat[:text].should == 'foo does not have a heartbeat'
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
describe '.app_name' do
|
101
|
+
|
102
|
+
it "should return config option if given" do
|
103
|
+
OpsRoutes.app_name = 'Config App'
|
104
|
+
OpsRoutes.app_name.should == 'Config App'
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should return parent directory if not a timestamp" do
|
108
|
+
Dir.stub(:pwd).and_return('/foo/bar')
|
109
|
+
OpsRoutes.app_name.should == 'bar'
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should return 2 levels up from parent if parent is timestamp (cap deploy structure)" do
|
113
|
+
Dir.stub(:pwd).and_return('/app/releases/12345')
|
114
|
+
OpsRoutes.app_name.should == 'app'
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should strip '.com' off of parent name" do
|
118
|
+
Dir.stub(:pwd).and_return('/app.com/releases/12345')
|
119
|
+
OpsRoutes.app_name.should == 'app'
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
describe '.version_or_branch' do
|
125
|
+
it "should use contents of VERSION file if available" do
|
126
|
+
File.stub(:exists?).with(File.join(OpsRoutes.file_root, 'VERSION')).and_return(true)
|
127
|
+
File.stub(:read).with(File.join(OpsRoutes.file_root, 'VERSION')).and_return('version')
|
128
|
+
|
129
|
+
OpsRoutes.version_or_branch.should == 'version'
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should use current git branch if in git repo and development mode" do
|
133
|
+
File.stub(:exists?).with(File.join(OpsRoutes.file_root, 'VERSION')).and_return(false)
|
134
|
+
# stub on app instead of Kernel since apparently you can't stub on a Module
|
135
|
+
OpsRoutes.stub(:`).with('git branch').and_return("* foo\n bar")
|
136
|
+
OpsRoutes.stub(:environment).and_return('development')
|
137
|
+
|
138
|
+
OpsRoutes.version_or_branch.should == 'foo'
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should be unknown if not in git repo" do
|
142
|
+
File.stub(:exists?).with(File.join(OpsRoutes.file_root, 'VERSION')).and_return(false)
|
143
|
+
# stub on OpsRoutes instead of Kernel since apparently you can't stub on a Module
|
144
|
+
OpsRoutes.stub(:`).with('git branch').and_return("fatal: Not a git repository (or any of the parent directories): .git")
|
145
|
+
OpsRoutes.stub(:environment).and_return('development')
|
146
|
+
|
147
|
+
OpsRoutes.version_or_branch.should match('^Unknown')
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should be unknown if not in development mode" do
|
151
|
+
File.stub(:exists?).with(File.join(OpsRoutes.file_root, 'VERSION')).and_return(false)
|
152
|
+
# stub on OpsRoutes instead of Kernel since apparently you can't stub on a Module
|
153
|
+
OpsRoutes.stub(:`).with('git branch').and_return("* foo\n bar")
|
154
|
+
OpsRoutes.stub(:environment).and_return('production')
|
155
|
+
|
156
|
+
OpsRoutes.version_or_branch.should match('^Unknown')
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
describe '.deploy_date' do
|
162
|
+
it "should use mtime of VERSION file if available" do
|
163
|
+
time = mock('time')
|
164
|
+
time.stub(:mtime).and_return('timestamp')
|
165
|
+
File.stub(:exists?).with(File.join(OpsRoutes.file_root, 'VERSION')).and_return(true)
|
166
|
+
File.stub(:stat).with(File.join(OpsRoutes.file_root, 'VERSION')).and_return(time)
|
167
|
+
|
168
|
+
OpsRoutes.deploy_date.should == 'timestamp'
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should use 'Live' if in development" do
|
172
|
+
File.stub(:exists?).with(File.join(OpsRoutes.file_root, 'VERSION')).and_return(false)
|
173
|
+
OpsRoutes.stub(:environment).and_return('development')
|
174
|
+
|
175
|
+
OpsRoutes.deploy_date.should == 'Live'
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should use unknown if not development" do
|
179
|
+
File.stub(:exists?).with(File.join(OpsRoutes.file_root, 'VERSION')).and_return(false)
|
180
|
+
OpsRoutes.stub(:environment).and_return('production')
|
181
|
+
|
182
|
+
OpsRoutes.deploy_date.should match('^Unknown')
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe '.last_commit' do
|
187
|
+
it "should use contents of REVISION file if available" do
|
188
|
+
File.stub(:exists?).with(File.join(OpsRoutes.file_root, 'REVISION')).and_return(true)
|
189
|
+
File.stub(:read).with(File.join(OpsRoutes.file_root, 'REVISION')).and_return('some_sha')
|
190
|
+
|
191
|
+
OpsRoutes.last_commit.should == 'some_sha'
|
192
|
+
end
|
193
|
+
|
194
|
+
it "should use current git HEAD if in git repo and development mode" do
|
195
|
+
File.stub(:exists?).with(File.join(OpsRoutes.file_root, 'REVISION')).and_return(false)
|
196
|
+
OpsRoutes.stub(:`).with('git show').and_return("commit some_sha")
|
197
|
+
OpsRoutes.stub(:environment).and_return('development')
|
198
|
+
|
199
|
+
OpsRoutes.last_commit.should == 'some_sha'
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should be unknown if not in git repo" do
|
203
|
+
File.stub(:exists?).with(File.join(OpsRoutes.file_root, 'REVISION')).and_return(false)
|
204
|
+
OpsRoutes.stub(:`).with('git show').and_return("fatal: Not a git repository (or any of the parent directories): .git")
|
205
|
+
OpsRoutes.stub(:environment).and_return('development')
|
206
|
+
|
207
|
+
OpsRoutes.last_commit.should match('^Unknown')
|
208
|
+
end
|
209
|
+
|
210
|
+
it "should be unknown if not in development mode" do
|
211
|
+
File.stub(:exists?).with(File.join(OpsRoutes.file_root, 'REVISION')).and_return(false)
|
212
|
+
OpsRoutes.stub(:`).with('git show').and_return("commit some_sha")
|
213
|
+
OpsRoutes.stub(:environment).and_return('production')
|
214
|
+
|
215
|
+
OpsRoutes.last_commit.should match('^Unknown')
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'app', 'controllers'))
|
4
|
+
require 'ops_routes'
|
5
|
+
require 'ops_routes/middleware'
|
6
|
+
|
7
|
+
# Define app controller since we're not in a rails app
|
8
|
+
class ApplicationController
|
9
|
+
end
|
10
|
+
require 'ops_controller'
|
11
|
+
|
12
|
+
require 'rubygems'
|
13
|
+
gem 'rack', '~>1.0'
|
14
|
+
require 'rack'
|
15
|
+
require 'spec'
|
16
|
+
# require 'spec/rails'
|
17
|
+
require 'spec/autorun'
|
18
|
+
require 'rack/test'
|
19
|
+
require 'rspec_tag_matchers'
|
20
|
+
|
21
|
+
Spec::Runner.configure do |config|
|
22
|
+
include Rack::Test::Methods
|
23
|
+
config.include(RspecTagMatchers)
|
24
|
+
end
|
data/uninstall.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Uninstall hook code here
|
metadata
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ops_routes
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 4
|
8
|
+
- 0
|
9
|
+
version: 0.4.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Primedia Team
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-08-09 00:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rack
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ~>
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 1
|
29
|
+
- 0
|
30
|
+
version: "1.0"
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: haml
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
segments:
|
41
|
+
- 3
|
42
|
+
- 0
|
43
|
+
- 0
|
44
|
+
version: 3.0.0
|
45
|
+
type: :runtime
|
46
|
+
version_requirements: *id002
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rspec
|
49
|
+
prerelease: false
|
50
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
segments:
|
55
|
+
- 1
|
56
|
+
- 2
|
57
|
+
- 9
|
58
|
+
version: 1.2.9
|
59
|
+
type: :development
|
60
|
+
version_requirements: *id003
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: rack-test
|
63
|
+
prerelease: false
|
64
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
segments:
|
69
|
+
- 0
|
70
|
+
version: "0"
|
71
|
+
type: :development
|
72
|
+
version_requirements: *id004
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: rspec_tag_matchers
|
75
|
+
prerelease: false
|
76
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
segments:
|
81
|
+
- 0
|
82
|
+
version: "0"
|
83
|
+
type: :development
|
84
|
+
version_requirements: *id005
|
85
|
+
description: Installs as Rails plugin or Rack middleware for non-Rails apps
|
86
|
+
email: ""
|
87
|
+
executables: []
|
88
|
+
|
89
|
+
extensions: []
|
90
|
+
|
91
|
+
extra_rdoc_files:
|
92
|
+
- LICENSE
|
93
|
+
- README.rdoc
|
94
|
+
files:
|
95
|
+
- LICENSE
|
96
|
+
- README.rdoc
|
97
|
+
- Rakefile
|
98
|
+
- VERSION
|
99
|
+
- app/controllers/ops_controller.rb
|
100
|
+
- config/routes.rb
|
101
|
+
- init.rb
|
102
|
+
- install.rb
|
103
|
+
- lib/ops_routes.rb
|
104
|
+
- lib/ops_routes/middleware.rb
|
105
|
+
- lib/tasks/ops_routes.rake
|
106
|
+
- lib/views/version.html.haml
|
107
|
+
- spec/app/controllers/ops_controller_spec.rb
|
108
|
+
- spec/ops_routes/middleware_spec.rb
|
109
|
+
- spec/ops_routes_spec.rb
|
110
|
+
- spec/spec.opts
|
111
|
+
- spec/spec_helper.rb
|
112
|
+
- uninstall.rb
|
113
|
+
has_rdoc: true
|
114
|
+
homepage: http://github.com/primedia/ops_routes
|
115
|
+
licenses: []
|
116
|
+
|
117
|
+
post_install_message:
|
118
|
+
rdoc_options:
|
119
|
+
- --charset=UTF-8
|
120
|
+
require_paths:
|
121
|
+
- lib
|
122
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
segments:
|
127
|
+
- 0
|
128
|
+
version: "0"
|
129
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
segments:
|
134
|
+
- 0
|
135
|
+
version: "0"
|
136
|
+
requirements: []
|
137
|
+
|
138
|
+
rubyforge_project:
|
139
|
+
rubygems_version: 1.3.6
|
140
|
+
signing_key:
|
141
|
+
specification_version: 3
|
142
|
+
summary: Plugin or middleware that provides version and heartbeat pages for app monitoring
|
143
|
+
test_files:
|
144
|
+
- spec/app/controllers/ops_controller_spec.rb
|
145
|
+
- spec/ops_routes/middleware_spec.rb
|
146
|
+
- spec/ops_routes_spec.rb
|
147
|
+
- spec/spec_helper.rb
|