ops 1.0.0.pre → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +27 -7
- data/lib/ops.rb +2 -3
- data/lib/ops/config.rb +8 -8
- data/lib/ops/heartbeat.rb +4 -7
- data/lib/ops/revision.rb +89 -31
- data/lib/ops/server.rb +22 -12
- data/lib/ops/server/helpers.rb +6 -14
- data/lib/ops/server/views/{env.html.erb → env.erb} +0 -0
- data/lib/ops/server/views/{layout.html.erb → layout.erb} +0 -0
- data/lib/ops/server/views/version.erb +76 -0
- data/lib/ops/version.rb +1 -1
- metadata +25 -73
- data/.gitignore +0 -18
- data/.rspec +0 -2
- data/Gemfile +0 -16
- data/Gemfile.lock +0 -129
- data/LICENSE +0 -7
- data/Rakefile +0 -28
- data/examples/rails3/.gitignore +0 -15
- data/examples/rails3/.rspec +0 -3
- data/examples/rails3/Gemfile +0 -5
- data/examples/rails3/Gemfile.lock +0 -130
- data/examples/rails3/Rakefile +0 -13
- data/examples/rails3/config.ru +0 -4
- data/examples/rails3/config/application.rb +0 -37
- data/examples/rails3/config/boot.rb +0 -6
- data/examples/rails3/config/environment.rb +0 -5
- data/examples/rails3/config/initializers/secret_token.rb +0 -1
- data/examples/rails3/config/locales/en.yml +0 -5
- data/examples/rails3/config/routes.rb +0 -3
- data/examples/rails3/public/404.html +0 -26
- data/examples/rails3/public/422.html +0 -26
- data/examples/rails3/public/500.html +0 -25
- data/examples/rails3/public/favicon.ico +0 -0
- data/examples/rails3/public/index.html +0 -14
- data/examples/rails3/public/robots.txt +0 -5
- data/examples/rails3/script/rails +0 -6
- data/examples/rails3/spec/functional/ops_routes_spec.rb +0 -69
- data/examples/rails3/spec/spec_helper.rb +0 -5
- data/examples/sample_deploys/1234/REVISION +0 -1
- data/examples/sample_deploys/1234/VERSION +0 -1
- data/examples/sample_deploys/2341/REVISION +0 -1
- data/examples/sample_deploys/2341/VERSION +0 -1
- data/examples/sample_deploys/3412/REVISION +0 -1
- data/examples/sample_deploys/3412/VERSION +0 -1
- data/examples/sample_deploys/4123/REVISION +0 -1
- data/examples/sample_deploys/4123/VERSION +0 -1
- data/examples/sinatra/.rspec +0 -3
- data/examples/sinatra/Rakefile +0 -15
- data/examples/sinatra/app.rb +0 -14
- data/examples/sinatra/config.ru +0 -20
- data/examples/sinatra/spec/functional/ops_routes_spec.rb +0 -69
- data/examples/sinatra/spec/spec_helper.rb +0 -3
- data/lib/ops/server/views/version.html.erb +0 -53
- data/ops.gemspec +0 -22
- data/spec/functional/ops_routes_spec.rb +0 -69
- data/spec/ops/config_spec.rb +0 -0
- data/spec/ops/heartbeat_spec.rb +0 -49
- data/spec/ops/revision_spec.rb +0 -42
- data/spec/ops/server_spec.rb +0 -66
- data/spec/ops/version_spec.rb +0 -7
- data/spec/ops_spec.rb +0 -9
- data/spec/spec_helper.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c5858a2e0dde21d89b70cb34b9e96ca51829443c
|
4
|
+
data.tar.gz: d502e55582d6d4ff4b027c7f5dc507c2f1816da4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eb1b3cafc156a9da0719726ca846d84c74d1ab2da4fd0689573a640515878413538d33688f113a1d25fa2a9d8360fb4d4b9933ce640281ad8178ff4639fabff4
|
7
|
+
data.tar.gz: f70c5bcdb1be85cf20d36064a72035301f75a2f1bef054838924c710915e27691dc25200c5bd816d370543fea98c88a1138217f8b3fdf32e2fae877c617071d4
|
data/README.md
CHANGED
@@ -1,9 +1,22 @@
|
|
1
|
-
Ops
|
2
|
-
|
1
|
+
# Ops
|
2
|
+
This gem provides standardized support for obtaining environment, version, and heartbeat information from Sinatra or Rails-based web applications.
|
3
3
|
|
4
4
|
[![Code Climate](https://codeclimate.com/github/primedia/ops.png)](https://codeclimate.com/github/primedia/ops)
|
5
|
+
[![Build Status](https://travis-ci.org/rentpath/ops.svg?branch=master)](https://travis-ci.org/rentpath/ops)
|
6
|
+
|
7
|
+
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
8
|
+
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
9
|
+
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
|
10
|
+
|
11
|
+
- [Installation](#installation)
|
12
|
+
- [For Rails apps:](#for-rails-apps)
|
13
|
+
- [For Sinatra apps:](#for-sinatra-apps)
|
14
|
+
- [Adding Custom Heartbeats](#adding-custom-heartbeats)
|
15
|
+
- [The Configuration Service Adapter (Optional)](#the-configuration-service-adapter-optional)
|
16
|
+
- [Running tests](#running-tests)
|
17
|
+
|
18
|
+
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
5
19
|
|
6
|
-
This gem provides standardized support for obtaining environment, version, and heartbeat information from Sinatra or Rails-based web applications.
|
7
20
|
|
8
21
|
**You will likely want to block or restrict access to the following routes:**
|
9
22
|
|
@@ -27,7 +40,7 @@ This gem replaces the now-deprecated [ops_routes](https://github.com/rentpath/op
|
|
27
40
|
Installation
|
28
41
|
------------
|
29
42
|
|
30
|
-
### For Rails
|
43
|
+
### For Rails apps:
|
31
44
|
|
32
45
|
1. Add the gem to your project's Gemfile:
|
33
46
|
```ruby
|
@@ -40,6 +53,7 @@ Installation
|
|
40
53
|
Ops.setup do |config|
|
41
54
|
config.file_root = Rails.root
|
42
55
|
config.environment = Rails.env
|
56
|
+
config.repo_name = 'my_repository_name'
|
43
57
|
config.config_service_adapter = something_that_responds_to_call # optional
|
44
58
|
end
|
45
59
|
```
|
@@ -79,8 +93,8 @@ Installation
|
|
79
93
|
```ruby
|
80
94
|
# Implementation within rack cascade:
|
81
95
|
run Rack::Cascade.new([
|
82
|
-
|
83
|
-
|
96
|
+
MyApp,
|
97
|
+
MySearch::App,
|
84
98
|
Ops.rack_app('/ops')
|
85
99
|
])
|
86
100
|
```
|
@@ -98,7 +112,10 @@ Ops.add_heartbeat :mysql do
|
|
98
112
|
end
|
99
113
|
```
|
100
114
|
|
101
|
-
The mysql example shown above would be accessed at ops/heartbeat/mysql.
|
115
|
+
The mysql example shown above would be accessed at ops/heartbeat/mysql.
|
116
|
+
The heartbeat page will return a `200 ‘OK’` as long as the provided block returns true.
|
117
|
+
If an error is raised, the heartbeat does not exist, or the block returns a falsey value,
|
118
|
+
a `500` will be returned instead.
|
102
119
|
|
103
120
|
|
104
121
|
## The Configuration Service Adapter (Optional)
|
@@ -124,3 +141,6 @@ Proc.new { |_| { key: 'value' } }
|
|
124
141
|
|
125
142
|
Then just provide your "callable" per the installation instructions above.
|
126
143
|
|
144
|
+
### Running tests
|
145
|
+
|
146
|
+
script/test
|
data/lib/ops.rb
CHANGED
data/lib/ops/config.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Ops
|
2
2
|
class Config
|
3
|
-
def initialize(data={})
|
3
|
+
def initialize(data = {})
|
4
4
|
@data = {}
|
5
5
|
update!(data)
|
6
6
|
end
|
@@ -16,16 +16,16 @@ module Ops
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def []=(key, value)
|
19
|
-
if value.class == Hash
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
@data[key.to_sym] = if value.class == Hash
|
20
|
+
Config.new(value)
|
21
|
+
else
|
22
|
+
value
|
23
|
+
end
|
24
24
|
end
|
25
25
|
|
26
|
-
def method_missing(sym, *args)
|
26
|
+
def method_missing(sym, *args) # rubocop:disable Style/MethodMissing
|
27
27
|
if sym.to_s =~ /(.+)=$/
|
28
|
-
self[
|
28
|
+
self[Regexp.last_match(1)] = args.first
|
29
29
|
else
|
30
30
|
self[sym]
|
31
31
|
end
|
data/lib/ops/heartbeat.rb
CHANGED
@@ -23,13 +23,10 @@ module Ops
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def check(name)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
puts "Exception: #{e}\n#{e.backtrace[2..-1].join("\n")}" unless heartbeats[name.to_sym].nil?
|
31
|
-
return false
|
32
|
-
end
|
26
|
+
return heartbeats[name.to_sym].call
|
27
|
+
rescue StandardError => e
|
28
|
+
puts "Error: #{e}\n#{e.backtrace[2..-1].join("\n")}" unless heartbeats[name.to_sym].nil?
|
29
|
+
return false
|
33
30
|
end
|
34
31
|
end
|
35
32
|
|
data/lib/ops/revision.rb
CHANGED
@@ -1,63 +1,121 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
|
3
1
|
module Ops
|
4
2
|
class Revision
|
5
|
-
|
3
|
+
attr_writer :branch_source
|
6
4
|
|
7
|
-
def initialize(new_headers={}, opts = Ops.config)
|
5
|
+
def initialize(new_headers = {}, opts = Ops.config)
|
8
6
|
@file_root = opts.file_root.to_s # convert to string in case they pass us a Pathname
|
9
7
|
@environment = opts.environment
|
10
8
|
@headers = new_headers
|
11
9
|
end
|
12
10
|
|
13
|
-
def
|
14
|
-
@
|
11
|
+
def version_or_branch
|
12
|
+
@version ||= if version_file?
|
13
|
+
chomp(version_file).gsub('^{}', '')
|
14
|
+
elsif development? && branch_source.call =~ /^\* (.*)$/
|
15
|
+
Regexp.last_match(1)
|
16
|
+
else
|
17
|
+
'Unknown (VERSION file is missing)'
|
18
|
+
end
|
15
19
|
end
|
16
20
|
|
17
|
-
def
|
18
|
-
@
|
21
|
+
def previous_versions
|
22
|
+
@previous_versions ||= get_previous_by_time
|
19
23
|
end
|
20
24
|
|
21
|
-
def
|
22
|
-
|
25
|
+
def get_previous_by_time
|
26
|
+
get_previous_versions.sort_by { |a| a[:time] }
|
23
27
|
end
|
24
28
|
|
25
|
-
def
|
26
|
-
|
29
|
+
def get_previous_versions
|
30
|
+
Dir["#{path}/../*"].each_with_object([]) do |dir, array|
|
31
|
+
next if dir =~ /#{current_dir}$/
|
32
|
+
version, revision = File.join(dir, 'VERSION'), File.join(dir, 'REVISION')
|
33
|
+
array << stats_hash(version: version, revision: revision) if File.exist?(version) && File.exist?(revision)
|
34
|
+
end
|
27
35
|
end
|
28
36
|
|
29
|
-
|
37
|
+
def path
|
38
|
+
File.absolute_path file_root
|
39
|
+
end
|
30
40
|
|
31
|
-
def
|
32
|
-
|
41
|
+
def current_dir
|
42
|
+
file_root.split('/').last
|
33
43
|
end
|
34
44
|
|
35
|
-
def
|
36
|
-
|
45
|
+
def stats_hash(files)
|
46
|
+
{ version: get_version(files[:version]),
|
47
|
+
revision: get_revision(files[:revision]),
|
48
|
+
time: get_time(files[:revision]) }
|
37
49
|
end
|
38
50
|
|
39
|
-
def
|
40
|
-
|
51
|
+
def get_version(file)
|
52
|
+
chomp(file).gsub('^{}', '')
|
41
53
|
end
|
42
54
|
|
43
|
-
def
|
44
|
-
|
55
|
+
def get_revision(file)
|
56
|
+
chomp file
|
45
57
|
end
|
46
58
|
|
47
|
-
def
|
48
|
-
|
49
|
-
parse_info_file(name)
|
50
|
-
else
|
51
|
-
{name.downcase.gsub('-', '_') => "No #{name} file found"}
|
52
|
-
end
|
59
|
+
def chomp(file)
|
60
|
+
File.read(file).chomp
|
53
61
|
end
|
54
62
|
|
55
|
-
def
|
56
|
-
|
63
|
+
def get_time(file)
|
64
|
+
File.stat(file).mtime
|
57
65
|
end
|
58
66
|
|
59
|
-
|
60
|
-
|
67
|
+
attr_reader :file_root
|
68
|
+
|
69
|
+
attr_reader :environment
|
70
|
+
|
71
|
+
def development?
|
72
|
+
environment == 'development'
|
73
|
+
end
|
74
|
+
|
75
|
+
def version_file
|
76
|
+
@version_file ||= File.join(file_root, 'VERSION')
|
77
|
+
end
|
78
|
+
|
79
|
+
def version_file?
|
80
|
+
File.exist? version_file
|
81
|
+
end
|
82
|
+
|
83
|
+
def revision_file
|
84
|
+
@revision_file ||= File.join(file_root, 'REVISION')
|
85
|
+
end
|
86
|
+
|
87
|
+
def revision_file?
|
88
|
+
File.exist? revision_file
|
89
|
+
end
|
90
|
+
|
91
|
+
def deploy_date
|
92
|
+
@deploy_date ||= if version_file?
|
93
|
+
get_time version_file
|
94
|
+
elsif development?
|
95
|
+
'Live'
|
96
|
+
else
|
97
|
+
'Unknown (VERSION file is missing)'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def last_commit
|
102
|
+
@last_commit ||= if revision_file?
|
103
|
+
chomp revision_file
|
104
|
+
elsif development? && `git show` =~ /^commit (.*)$/
|
105
|
+
Regexp.last_match(1)
|
106
|
+
else
|
107
|
+
'Unknown (REVISION file is missing)'
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def headers
|
112
|
+
@headers.select { |k, v| k.match(/^[-A-Z_].*$/) }
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def branch_source
|
118
|
+
@branch_source ||= -> { `git branch` }
|
61
119
|
end
|
62
120
|
end
|
63
121
|
end
|
data/lib/ops/server.rb
CHANGED
@@ -1,22 +1,30 @@
|
|
1
1
|
require 'sinatra/base'
|
2
|
-
require 'sinatra/respond_to'
|
3
2
|
require 'ops/server/helpers'
|
4
3
|
require 'json'
|
5
4
|
|
6
5
|
module Ops
|
7
6
|
class Server < Sinatra::Base
|
8
|
-
|
9
|
-
|
10
|
-
set :views,
|
7
|
+
dir = File.dirname(File.expand_path('', __FILE__))
|
8
|
+
set :views, "#{dir}/server/views"
|
9
|
+
# set :views, File.dirname(File.expand_path('/../server/views', __FILE__))
|
11
10
|
|
12
11
|
helpers Ops::Helpers
|
13
12
|
|
14
13
|
def request_headers
|
15
|
-
env.each_with_object({}) { |(k,v), headers| headers[k] = v }
|
14
|
+
env.each_with_object({}) { |(k, v), headers| headers[k] = v }
|
16
15
|
end
|
17
16
|
|
18
|
-
def jsonified_version(version, headers)
|
19
|
-
JSON.generate(
|
17
|
+
def jsonified_version(version, previous_versions, headers)
|
18
|
+
JSON.generate(
|
19
|
+
version: version.version_or_branch,
|
20
|
+
revision: version.last_commit,
|
21
|
+
previous_versions: previous_versions,
|
22
|
+
headers: headers
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def json_request?
|
27
|
+
!!Array(params['format'] || request.accept).detect { |f| f.to_s =~ /json/ }
|
20
28
|
end
|
21
29
|
|
22
30
|
get '/env/?' do
|
@@ -24,14 +32,17 @@ module Ops
|
|
24
32
|
erb :env
|
25
33
|
end
|
26
34
|
|
27
|
-
get '/version
|
35
|
+
get '/version/?:format?', provides: %i(html json) do
|
28
36
|
@version = Revision.new(request_headers)
|
37
|
+
@previous_versions = @version.previous_versions
|
29
38
|
@headers = @version.headers
|
30
39
|
|
31
|
-
|
32
|
-
|
33
|
-
|
40
|
+
if json_request?
|
41
|
+
content_type 'application/json'
|
42
|
+
return jsonified_version(@version, @previous_versions, @headers)
|
34
43
|
end
|
44
|
+
|
45
|
+
erb :version
|
35
46
|
end
|
36
47
|
|
37
48
|
get '/heartbeat/?' do
|
@@ -62,4 +73,3 @@ module Ops
|
|
62
73
|
end
|
63
74
|
end
|
64
75
|
end
|
65
|
-
|
data/lib/ops/server/helpers.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Ops
|
2
2
|
module Helpers
|
3
|
-
GITHUB_ORG_LINK = 'https://github.com/rentpath'
|
3
|
+
GITHUB_ORG_LINK = 'https://github.com/rentpath'.freeze
|
4
4
|
|
5
5
|
def hostname
|
6
6
|
@hostname ||= `/bin/hostname` || 'Unknown'
|
@@ -25,22 +25,14 @@ module Ops
|
|
25
25
|
github_link 'commit', commit
|
26
26
|
end
|
27
27
|
|
28
|
-
def
|
29
|
-
|
28
|
+
def repo_name
|
29
|
+
Ops.config[:repo_name] || app_name
|
30
30
|
end
|
31
31
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
output << "{\n"
|
36
|
-
output << object.collect { |key, value|
|
37
|
-
" " * indent + " #{print_detail key} => #{print_detail value, indent+1}"
|
38
|
-
}.join(",\n") << "\n"
|
39
|
-
output << " " * indent + "}"
|
40
|
-
else
|
41
|
-
output << object.inspect
|
32
|
+
def github_link(resource, subresource)
|
33
|
+
unless subresource =~ /^Unknown/
|
34
|
+
"<a href='#{GITHUB_ORG_LINK}/#{repo_name}/#{resource}/#{subresource}'>#{subresource}</a>"
|
42
35
|
end
|
43
|
-
output
|
44
36
|
end
|
45
37
|
end
|
46
38
|
end
|
File without changes
|
File without changes
|
@@ -0,0 +1,76 @@
|
|
1
|
+
<div class="container">
|
2
|
+
<div class="spacer"></div>
|
3
|
+
<div id="version">
|
4
|
+
<div class="label">
|
5
|
+
<%= "#{app_name} Version" %>
|
6
|
+
</div>
|
7
|
+
<div class="value">
|
8
|
+
<%= version_link @version.version_or_branch %>
|
9
|
+
</div>
|
10
|
+
</div>
|
11
|
+
<div class="spacer"></div>
|
12
|
+
<div id="date">
|
13
|
+
<div class="label">Date Deployed</div>
|
14
|
+
<div class="value"><%= @version.deploy_date %></div>
|
15
|
+
</div>
|
16
|
+
<div class="spacer"></div>
|
17
|
+
<div id="commit">
|
18
|
+
<div class="label">Last Commit</div>
|
19
|
+
<div class="value">
|
20
|
+
<%= commit_link(@version.last_commit)%>
|
21
|
+
</div>
|
22
|
+
</div>
|
23
|
+
<div class="spacer"></div>
|
24
|
+
<div id="host">
|
25
|
+
<div class="label">Host</div>
|
26
|
+
<div class="value"><%= hostname %></div>
|
27
|
+
</div>
|
28
|
+
<div class="spacer"></div>
|
29
|
+
<div id="environment">
|
30
|
+
<div class="label">Environment</div>
|
31
|
+
<div class="value"><%= @version.environment %></div>
|
32
|
+
</div>
|
33
|
+
<div class="spacer"></div>
|
34
|
+
<div id="previous_versions">
|
35
|
+
<div class="label">Previous Versions</div>
|
36
|
+
<div class="value">
|
37
|
+
<% unless @previous_versions.empty? %>
|
38
|
+
<table>
|
39
|
+
<tr class="header">
|
40
|
+
<td>Time</td>
|
41
|
+
<td>Version</td>
|
42
|
+
<td>Commit</td>
|
43
|
+
</tr>
|
44
|
+
<% @previous_versions.each_with_index do |version, i| %>
|
45
|
+
<tr class="<%= i%2==0 ? 'even' : nil %>">
|
46
|
+
<td>
|
47
|
+
<%= version[:time].strftime('%x %X') %>
|
48
|
+
</td>
|
49
|
+
<td>
|
50
|
+
<%= version_link(version[:version]) %>
|
51
|
+
</td>
|
52
|
+
<td>
|
53
|
+
<%= commit_link(version[:revision]) %>
|
54
|
+
</td>
|
55
|
+
</tr>
|
56
|
+
<% end %>
|
57
|
+
</table>
|
58
|
+
<% end %>
|
59
|
+
</div>
|
60
|
+
</div>
|
61
|
+
<div class="spacer"></div>
|
62
|
+
<div id="headers">
|
63
|
+
<div class="label">Headers</div>
|
64
|
+
<div class="value">
|
65
|
+
<table>
|
66
|
+
<% @headers.sort_by{ |h| h.first }.each_with_index do |(header, value), i| %>
|
67
|
+
<tr class="<%= i%2==0 ? 'even' : nil %>">
|
68
|
+
<td><%= header %></td>
|
69
|
+
<td><%= value %></td>
|
70
|
+
</tr>
|
71
|
+
<% end %>
|
72
|
+
</table>
|
73
|
+
</div>
|
74
|
+
</div>
|
75
|
+
<div class="clear"></div>
|
76
|
+
</div>
|