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