vault-tools 0.3.1 → 0.3.2

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