ops 1.0.0.pre → 1.1.0

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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +27 -7
  3. data/lib/ops.rb +2 -3
  4. data/lib/ops/config.rb +8 -8
  5. data/lib/ops/heartbeat.rb +4 -7
  6. data/lib/ops/revision.rb +89 -31
  7. data/lib/ops/server.rb +22 -12
  8. data/lib/ops/server/helpers.rb +6 -14
  9. data/lib/ops/server/views/{env.html.erb → env.erb} +0 -0
  10. data/lib/ops/server/views/{layout.html.erb → layout.erb} +0 -0
  11. data/lib/ops/server/views/version.erb +76 -0
  12. data/lib/ops/version.rb +1 -1
  13. metadata +25 -73
  14. data/.gitignore +0 -18
  15. data/.rspec +0 -2
  16. data/Gemfile +0 -16
  17. data/Gemfile.lock +0 -129
  18. data/LICENSE +0 -7
  19. data/Rakefile +0 -28
  20. data/examples/rails3/.gitignore +0 -15
  21. data/examples/rails3/.rspec +0 -3
  22. data/examples/rails3/Gemfile +0 -5
  23. data/examples/rails3/Gemfile.lock +0 -130
  24. data/examples/rails3/Rakefile +0 -13
  25. data/examples/rails3/config.ru +0 -4
  26. data/examples/rails3/config/application.rb +0 -37
  27. data/examples/rails3/config/boot.rb +0 -6
  28. data/examples/rails3/config/environment.rb +0 -5
  29. data/examples/rails3/config/initializers/secret_token.rb +0 -1
  30. data/examples/rails3/config/locales/en.yml +0 -5
  31. data/examples/rails3/config/routes.rb +0 -3
  32. data/examples/rails3/public/404.html +0 -26
  33. data/examples/rails3/public/422.html +0 -26
  34. data/examples/rails3/public/500.html +0 -25
  35. data/examples/rails3/public/favicon.ico +0 -0
  36. data/examples/rails3/public/index.html +0 -14
  37. data/examples/rails3/public/robots.txt +0 -5
  38. data/examples/rails3/script/rails +0 -6
  39. data/examples/rails3/spec/functional/ops_routes_spec.rb +0 -69
  40. data/examples/rails3/spec/spec_helper.rb +0 -5
  41. data/examples/sample_deploys/1234/REVISION +0 -1
  42. data/examples/sample_deploys/1234/VERSION +0 -1
  43. data/examples/sample_deploys/2341/REVISION +0 -1
  44. data/examples/sample_deploys/2341/VERSION +0 -1
  45. data/examples/sample_deploys/3412/REVISION +0 -1
  46. data/examples/sample_deploys/3412/VERSION +0 -1
  47. data/examples/sample_deploys/4123/REVISION +0 -1
  48. data/examples/sample_deploys/4123/VERSION +0 -1
  49. data/examples/sinatra/.rspec +0 -3
  50. data/examples/sinatra/Rakefile +0 -15
  51. data/examples/sinatra/app.rb +0 -14
  52. data/examples/sinatra/config.ru +0 -20
  53. data/examples/sinatra/spec/functional/ops_routes_spec.rb +0 -69
  54. data/examples/sinatra/spec/spec_helper.rb +0 -3
  55. data/lib/ops/server/views/version.html.erb +0 -53
  56. data/ops.gemspec +0 -22
  57. data/spec/functional/ops_routes_spec.rb +0 -69
  58. data/spec/ops/config_spec.rb +0 -0
  59. data/spec/ops/heartbeat_spec.rb +0 -49
  60. data/spec/ops/revision_spec.rb +0 -42
  61. data/spec/ops/server_spec.rb +0 -66
  62. data/spec/ops/version_spec.rb +0 -7
  63. data/spec/ops_spec.rb +0 -9
  64. data/spec/spec_helper.rb +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5a2c4cc3db903a62379145de252980257d0f9116
4
- data.tar.gz: 097364bd5ae78bbc864132377d3ccd27a5d0575d
3
+ metadata.gz: c5858a2e0dde21d89b70cb34b9e96ca51829443c
4
+ data.tar.gz: d502e55582d6d4ff4b027c7f5dc507c2f1816da4
5
5
  SHA512:
6
- metadata.gz: 6f07d97daec0091594c703442587814e703fb7c8f9f9562cb03e521cb2ba21df64757150c02ebd86e388677afe530a757d2f80f0f1978ad828cca3fe4aed2d02
7
- data.tar.gz: 1432a8c961924d408d6269819967fc86b82d78485689192a8c90c684c3d1b6576bd709f37ad7135980d6a2f66b59288ca3d992b09561f49ce1c119c7763bee80
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 3 apps:
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
- NewHomeGuide,
83
- ListingSearch::App,
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. The heartbeat page will return a `200 ‘OK’` as long as the provided block returns true. If an error is raised, the heartbeat does not exist, or the block returns a falsey value, a `500` will be returned instead.
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
@@ -11,10 +11,9 @@ module Ops
11
11
  end
12
12
 
13
13
  def rack_app(path)
14
- Rack::Builder.new {
14
+ Rack::Builder.new do
15
15
  map(path) { run Ops.new }
16
- }.to_app
16
+ end.to_app
17
17
  end
18
18
  end
19
19
  end
20
-
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
- @data[key.to_sym] = Config.new(value)
21
- else
22
- @data[key.to_sym] = value
23
- end
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[$1] = args.first
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
- begin
27
- return heartbeats[name.to_sym].call
28
- rescue Exception => e
29
- # print stacktrace for error raised by executing block
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
- attr_reader :file_root
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 environment
14
- @environment
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 headers
18
- @headers.select{|k,v| k.match(/^[-A-Z_].*$/) }
21
+ def previous_versions
22
+ @previous_versions ||= get_previous_by_time
19
23
  end
20
24
 
21
- def info
22
- @info ||= build_info.merge(deploy_info)
25
+ def get_previous_by_time
26
+ get_previous_versions.sort_by { |a| a[:time] }
23
27
  end
24
28
 
25
- def previous_info
26
- @previous_info ||= previous_build_info.merge(previous_deploy_info)
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
- private
37
+ def path
38
+ File.absolute_path file_root
39
+ end
30
40
 
31
- def build_info
32
- info_from_file('BUILD-INFO')
41
+ def current_dir
42
+ file_root.split('/').last
33
43
  end
34
44
 
35
- def previous_build_info
36
- info_from_file('PREVIOUS-BUILD-INFO')
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 deploy_info
40
- info_from_file('DEPLOY-INFO')
51
+ def get_version(file)
52
+ chomp(file).gsub('^{}', '')
41
53
  end
42
54
 
43
- def previous_deploy_info
44
- info_from_file('PREVIOUS-DEPLOY-INFO')
55
+ def get_revision(file)
56
+ chomp file
45
57
  end
46
58
 
47
- def info_from_file(name)
48
- if file_exists?(name)
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 parse_info_file(filename)
56
- YAML.load(File.read(File.join(file_root, filename))) if file_exists?(filename)
63
+ def get_time(file)
64
+ File.stat(file).mtime
57
65
  end
58
66
 
59
- def file_exists?(file_name)
60
- File.exist?(File.join(file_root, file_name))
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
- Server.register Sinatra::RespondTo
9
- dir = File.dirname(File.expand_path(__FILE__))
10
- set :views, "#{dir}/server/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({info: @version.info, previous_info: @version.previous_info, headers: @headers})
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/?' do
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
- respond_to do |wants|
32
- wants.html { erb :version }
33
- wants.json { jsonified_version(@version, @headers) }
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
-
@@ -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 github_link(resource, subresource)
29
- "<a href='#{GITHUB_ORG_LINK}/#{app_name}/#{resource}/#{subresource}'>#{subresource}</a>" unless subresource =~ /^Unknown/
28
+ def repo_name
29
+ Ops.config[:repo_name] || app_name
30
30
  end
31
31
 
32
- def print_detail(object, indent = 0)
33
- output = ""
34
- if object.kind_of? Hash
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
@@ -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>