vault-tools 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -12,4 +12,5 @@ end
12
12
 
13
13
  group :test do
14
14
  gem 'vault-test-tools', :path => '../vault-test-tools'
15
+ gem 'minitest'
15
16
  end
data/lib/vault-tools.rb CHANGED
@@ -3,6 +3,7 @@ require "vault-tools/version"
3
3
  require 'sinatra/base'
4
4
  require 'scrolls'
5
5
  require 'rack/ssl-enforcer'
6
+ require 'heroku-api'
6
7
 
7
8
  module Vault
8
9
  #require bundler and the proper gems for the ENV
@@ -2,6 +2,15 @@ module Vault
2
2
  module Config
3
3
  extend self
4
4
 
5
+ def remote_env(app, env)
6
+ heroku = Heroku::API.new
7
+ heroku.get_config_vars(app).body[env]
8
+ end
9
+
10
+ def core_follower_url
11
+ remote_env('vault-core-follower', 'DATABASE_URL')
12
+ end
13
+
5
14
  def env(key)
6
15
  ENV[key]
7
16
  end
@@ -18,8 +27,13 @@ module Vault
18
27
  env('RACK_ENV') == 'test'
19
28
  end
20
29
 
21
- def app_name; env("APP_NAME"); end
22
- def port; env!("PORT").to_i; end
30
+ def app_name
31
+ env("APP_NAME")
32
+ end
33
+
34
+ def port
35
+ env!("PORT").to_i
36
+ end
23
37
 
24
38
  def database_url(kind = '')
25
39
  kind = "#{kind}_".upcase unless kind.empty?
@@ -1,61 +1,54 @@
1
1
  module Vault
2
2
  module Log
3
- extend self
4
-
5
- # Public: logs a counter
6
- #
7
- # name - Name of the counter
8
- #
9
- # Examples
10
- # Log.count('foo')
11
- # => "measure=true at=foo"
3
+ # Log a count metric.
12
4
  #
13
- # Logs via Scrolls
14
- def count(name)
15
- log(measure: true, at: name)
5
+ # @param name [String] The name of the metric.
6
+ def self.count(name)
7
+ name = "#{Config.app_name}.#{name}" if Config.app_name
8
+ log(measure: name)
16
9
  end
17
10
 
18
- # Public: logs an HTTP status code
11
+ # Log an HTTP status code. Two log metrics are written each time this
12
+ # method is invoked:
19
13
  #
20
- # status - HTTP status code
14
+ # - The first one emits a metric called `http_200` for an HTTP 200
15
+ # response.
16
+ # - The second one emits a metric called `http_2xx` for the same code.
21
17
  #
22
- # Examples
23
- # Log.count_status(400)
24
- # => "measure=true at=web-40"
18
+ # This makes it possible to easily measure individual HTTP status codes as
19
+ # well as classes of HTTP status codes.
25
20
  #
26
- # Logs via Scrolls
27
- def count_status(status)
28
- if prefix = status.to_s.match(/\d\d/)[0]
29
- log(measure: true, at: "web-#{prefix}")
21
+ # @param status [Fixnum] The HTTP status code to record.
22
+ def self.count_status(status)
23
+ count("http_#{status}")
24
+ if status_prefix = status.to_s.match(/\d/)[0]
25
+ count("http_#{status_prefix}xx")
30
26
  end
31
27
  end
32
28
 
33
- # Public: logs the time of a web request
29
+ # Log a timing metric.
34
30
  #
35
- # name - a Sinatra-formatted route url
36
- # t - time (integer seconds or milliseconds)
37
- #
38
- # Examples
39
- # Log.time(name, t)
40
- # => "measure=true at=web-40"
41
- #
42
- # Logs via Scrolls
43
- def time(name, t)
31
+ # @param name [String] A Sinatra-formatted route URL.
32
+ # @param duration [Fixnum] The duration to record, in seconds or
33
+ # milliseconds.
34
+ def self.time(name, duration)
44
35
  if name
45
- name.
46
- gsub(/\/:\w+/,''). #remove param names from path
47
- gsub("/","-"). #remove slash from path
48
- gsub(/[^A-Za-z0-9\-\_]/, ''). #only keep subset of chars
49
- slice(1..-1).
50
- tap {|res| log(measure: true, fn: res, elapsed: t)}
36
+ name.gsub(/\/:\w+/, ''). # Remove param names from path.
37
+ gsub("/", "_"). # Replace slash with underscore.
38
+ gsub(/[^A-Za-z0-9\-\_]/, ''). # Only keep subset of chars.
39
+ slice(1..-1).
40
+ tap { |name| log(measure: name, val: duration) }
51
41
  end
52
42
  end
53
43
 
54
- # Internal: log something
55
- #
56
- # Logs via Scrolls
57
- def log(data, &blk)
58
- Scrolls.log(data, &blk)
44
+ # Write a message with key/value pairs to the log stream.
45
+ #
46
+ # @param data [Hash] A mapping of key/value pairs to include in the
47
+ # output.
48
+ # @param block [Proc] Optionally, a block of code to run before writing
49
+ # the log message.
50
+ def self.log(data, &block)
51
+ Scrolls.log(data, &block)
59
52
  end
60
53
  end
61
54
  end
@@ -1,5 +1,5 @@
1
1
  module Vault
2
2
  module Tools
3
- VERSION = '0.3.1'
3
+ VERSION = '0.3.2'
4
4
  end
5
5
  end
data/test/config_test.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'helper'
2
+ require 'minitest/mock'
2
3
 
3
4
  class ConfigTest < Vault::TestCase
4
-
5
5
  # Config.production? is true when RACK_ENV=production
6
6
  def test_production_mode
7
7
  set_env 'RACK_ENV', nil
@@ -36,6 +36,8 @@ class ConfigTest < Vault::TestCase
36
36
  end
37
37
  end
38
38
 
39
+ # Vault::Config.app_name returns the value of the APP_NAME environment
40
+ # variable.
39
41
  def test_app_name
40
42
  Vault::Config.app_name.must_equal nil
41
43
  set_env 'APP_NAME', "my-app"
@@ -63,4 +65,15 @@ class ConfigTest < Vault::TestCase
63
65
  set_env 'VAULT_TOOLS_DISABLE_SSL', 'foo'
64
66
  refute Vault::Config.enable_ssl?
65
67
  end
68
+
69
+ # Config.remote_env uses Heroku API to read config
70
+ # vars from apps
71
+ def test_remote_env
72
+ api_mock = MiniTest::Mock.new
73
+ api_response = OpenStruct.new(body: {'DATABASE_URL' => 'postgres:///foo'})
74
+ Heroku::API.stub(:new, api_mock) do
75
+ api_mock.expect(:get_config_vars, api_response, ['app'])
76
+ assert_equal('postgres:///foo', Vault::Config.remote_env('app', 'DATABASE_URL'))
77
+ end
78
+ end
66
79
  end
data/test/helper.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  require 'vault-test-tools'
2
2
  require 'vault-tools'
3
+ ENV['RACK_ENV'] = 'test'
4
+ Vault.setup
3
5
 
4
6
  class Vault::TestCase
5
7
  include Vault::Test::EnvironmentHelpers
data/test/log_test.rb ADDED
@@ -0,0 +1,84 @@
1
+ require 'helper'
2
+
3
+ class LogTest < Vault::TestCase
4
+ # Vault::Log.count emits a metric, using the specified name, that represents
5
+ # a countable event.
6
+ def test_count
7
+ set_env('APP_NAME', nil)
8
+ Vault::Log.count('countable')
9
+ assert_match(/measure=countable/, Scrolls.stream.string)
10
+ end
11
+
12
+ # Vault::Log.count emits a metric that represents a countable event. If an
13
+ # APP_NAME environment variable is available it will be prepended to the
14
+ # measurement name.
15
+ def test_count_with_app_name
16
+ set_env('APP_NAME', 'vault_app')
17
+ Vault::Log.count('countable')
18
+ assert_match(/measure=vault_app.countable/, Scrolls.stream.string)
19
+ end
20
+
21
+ # Vault::Log.count_status emits metrics to measure HTTP responses. The
22
+ # exact HTTP status, and the status family, are recorded.
23
+ def test_count_status
24
+ set_env('APP_NAME', nil)
25
+ Vault::Log.count_status(201)
26
+ assert_match(/measure=http_201/, Scrolls.stream.string)
27
+ assert_match(/measure=http_2xx/, Scrolls.stream.string)
28
+ end
29
+
30
+ # Vault::Log.count_status emits metrics to measure HTTP responses. If an
31
+ # APP_NAME environment variable is available it will be prepended to the
32
+ # measurement name.
33
+ def test_count_status_with_app_name
34
+ set_env('APP_NAME', 'vault_app')
35
+ Vault::Log.count_status(201)
36
+ assert_match(/measure=vault_app.http_201/, Scrolls.stream.string)
37
+ assert_match(/measure=vault_app.http_2xx/, Scrolls.stream.string)
38
+ end
39
+
40
+ # Vault::Log.time emits a metric to measure the duration of an HTTP request.
41
+ # It converts slashes to underscores.
42
+ def test_time_replaces_slash_with_underscore
43
+ Vault::Log.time('/some/web/path', 123.4)
44
+ assert_match(/measure=some_web_path val=123.400/, Scrolls.stream.string)
45
+ assert_match(/val=123.4/, Scrolls.stream.string)
46
+ end
47
+
48
+ # Vault::Log.time removes parameters.
49
+ def test_time_removes_parameters
50
+ Vault::Log.time('/some/:web/path', 123.4)
51
+ assert_match(/measure=some_path val=123.400/, Scrolls.stream.string)
52
+ assert_match(/val=123.4/, Scrolls.stream.string)
53
+ end
54
+
55
+ # Vault::Log.time removes non-alphanumeric characters.
56
+ def test_time_removes_non_alphanumeric_characters
57
+ Vault::Log.time('/some/web+path', 123.4)
58
+ assert_match(/measure=some_webpath val=123.400/, Scrolls.stream.string)
59
+ assert_match(/val=123.4/, Scrolls.stream.string)
60
+ end
61
+
62
+ # Vault::Log.time is a no-op if a nil name is provided.
63
+ def test_time_without_name
64
+ Vault::Log.time(nil, 123.4)
65
+ assert_equal('', Scrolls.stream.string)
66
+ end
67
+
68
+ # Vault::Log.log emits a set of key/value metrics using data from the
69
+ # specified hash.
70
+ def test_log
71
+ Vault::Log.log(integer: 123, float: 123.4, string: 'string', bool: false)
72
+ assert_match(/integer=123 float=123.400 string=string bool=false/,
73
+ Scrolls.stream.string)
74
+ end
75
+
76
+ # Vault::Log.log can be used to measure the time spent in a block.
77
+ def test_log_with_block
78
+ Vault::Log.log(A: true) do
79
+ Vault::Log.log(B: true)
80
+ end
81
+ assert_match(/A=true at=start\nB=true\nA=true at=finish elapsed=0/,
82
+ Scrolls.stream.string)
83
+ end
84
+ end
data/test/web_test.rb CHANGED
@@ -14,38 +14,40 @@ class WebTest < Vault::TestCase
14
14
  reload_web!
15
15
  end
16
16
 
17
- # middleware is attached at load time, so we have to
18
- # delete the web class and reload it to simulate being
19
- # loaded with a given ENV
17
+ # Middleware is attached at load time, so we have to delete the Vault::Web
18
+ # class and reload it to simulate being loaded with different environment
19
+ # variables.
20
20
  def reload_web!
21
21
  # remove the constant to force a clean reload
22
22
  Vault.send(:remove_const, 'Web')
23
23
  load 'lib/vault-tools/web.rb'
24
24
  end
25
25
 
26
- # A successful request causes a `web-20` log entry to be written.
26
+ # An `http_200` and an `http_2xx` log metric is written for successful
27
+ # requests.
27
28
  def test_head_status_check
28
29
  head '/'
29
- assert_match(/measure=true/, Scrolls.stream.string)
30
- assert_match(/at=web-20/, Scrolls.stream.string)
30
+ assert_match(/measure=http_200/, Scrolls.stream.string)
31
+ assert_match(/measure=http_2xx/, Scrolls.stream.string)
31
32
  assert_equal(200, last_response.status)
32
33
  end
33
34
 
34
- # A successful request causes `web-20` log entry to be written and `OK`
35
- # content is returned in the response body.
35
+ # A GET /health request logs success metrics and returns 'OK' in the
36
+ # response body.
36
37
  def test_get_health_check
37
38
  get '/health'
38
- assert_match(/measure=true/, Scrolls.stream.string)
39
- assert_match(/at=web-20/, Scrolls.stream.string)
39
+ assert_match(/measure=http_200/, Scrolls.stream.string)
40
+ assert_match(/measure=http_2xx/, Scrolls.stream.string)
40
41
  assert_equal(200, last_response.status)
41
42
  assert_equal('OK', last_response.body)
42
43
  end
43
44
 
44
- # A validation error causes a `web-40` log entry to be written.
45
+ # An `http_404` and an `http_4xx` log metric is written when a path doesn't
46
+ # match a known resource.
45
47
  def test_head_with_unknown_endpoint
46
48
  head '/unknown'
47
- assert_match(/measure=true/, Scrolls.stream.string)
48
- assert_match(/at=web-40/, Scrolls.stream.string)
49
+ assert_match(/measure=http_404/, Scrolls.stream.string)
50
+ assert_match(/measure=http_4xx/, Scrolls.stream.string)
49
51
  assert_equal(404, last_response.status)
50
52
  end
51
53
 
@@ -53,26 +55,28 @@ class WebTest < Vault::TestCase
53
55
  # traceback is also written to the response body to ease debugging.
54
56
  def test_error_logs_500
55
57
  get '/boom'
56
- assert_match(/measure=true/, Scrolls.stream.string)
57
- assert_match(/at=web-50/, Scrolls.stream.string)
58
+ assert_match(/measure=http_500/, Scrolls.stream.string)
59
+ assert_match(/measure=http_5xx/, Scrolls.stream.string)
58
60
  assert_match(/^RuntimeError: An expected error occurred.$/m,
59
61
  last_response.body)
60
62
  assert_equal(500, last_response.status)
61
63
  end
62
64
 
63
-
64
65
  # SSL is enforced when we are in production mode
65
66
  def test_ssl_enforced_in_production_mode
66
67
  set_env 'RACK_ENV', 'production'
68
+ set_env 'VAULT_TOOLS_DISABLE_SSL', nil
67
69
  reload_web!
68
70
  get '/health'
69
71
  assert_equal(301, last_response.status)
70
72
  assert_match(/^https/, last_response.headers['Location'])
71
73
  end
72
74
 
75
+ # SSL isn't enforced when the VAULT_TOOLS_DISABLE_SSL environment variable
76
+ # has a true value.
73
77
  def test_ssl_can_be_disabled
74
78
  set_env 'RACK_ENV', 'production'
75
- set_env 'VAULT_TOOLS_DISABLE_SSL', 'anything'
79
+ set_env 'VAULT_TOOLS_DISABLE_SSL', '1'
76
80
  reload_web!
77
81
  get '/health'
78
82
  assert_equal(200, last_response.status)
data/vault-tools.gemspec CHANGED
@@ -21,4 +21,5 @@ Gem::Specification.new do |gem|
21
21
  gem.add_dependency 'sinatra'
22
22
  gem.add_dependency 'uuidtools'
23
23
  gem.add_dependency 'rack-ssl-enforcer'
24
+ gem.add_dependency 'heroku-api'
24
25
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vault-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-02-27 00:00:00.000000000 Z
13
+ date: 2013-03-06 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: scrolls
@@ -76,6 +76,22 @@ dependencies:
76
76
  - - ! '>='
77
77
  - !ruby/object:Gem::Version
78
78
  version: '0'
79
+ - !ruby/object:Gem::Dependency
80
+ name: heroku-api
81
+ requirement: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ type: :runtime
88
+ prerelease: false
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
79
95
  description: Basic tools for Heroku Vault's Ruby projects
80
96
  email:
81
97
  - christopher.continanza@gmail.com
@@ -108,6 +124,7 @@ files:
108
124
  - test/config_test.rb
109
125
  - test/helper.rb
110
126
  - test/hid_test.rb
127
+ - test/log_test.rb
111
128
  - test/pipeline_test.rb
112
129
  - test/product_test.rb
113
130
  - test/test_spec.rb
@@ -128,7 +145,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
128
145
  version: '0'
129
146
  segments:
130
147
  - 0
131
- hash: 3619778048086262088
148
+ hash: -4204391023637480040
132
149
  required_rubygems_version: !ruby/object:Gem::Requirement
133
150
  none: false
134
151
  requirements:
@@ -137,7 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
137
154
  version: '0'
138
155
  segments:
139
156
  - 0
140
- hash: 3619778048086262088
157
+ hash: -4204391023637480040
141
158
  requirements: []
142
159
  rubyforge_project:
143
160
  rubygems_version: 1.8.23