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 +1 -0
- data/lib/vault-tools.rb +1 -0
- data/lib/vault-tools/config.rb +16 -2
- data/lib/vault-tools/log.rb +35 -42
- data/lib/vault-tools/version.rb +1 -1
- data/test/config_test.rb +14 -1
- data/test/helper.rb +2 -0
- data/test/log_test.rb +84 -0
- data/test/web_test.rb +21 -17
- data/vault-tools.gemspec +1 -0
- metadata +21 -4
data/Gemfile
CHANGED
data/lib/vault-tools.rb
CHANGED
data/lib/vault-tools/config.rb
CHANGED
@@ -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
|
22
|
-
|
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?
|
data/lib/vault-tools/log.rb
CHANGED
@@ -1,61 +1,54 @@
|
|
1
1
|
module Vault
|
2
2
|
module Log
|
3
|
-
|
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
|
-
#
|
14
|
-
def count(name)
|
15
|
-
|
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
|
-
#
|
11
|
+
# Log an HTTP status code. Two log metrics are written each time this
|
12
|
+
# method is invoked:
|
19
13
|
#
|
20
|
-
#
|
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
|
-
#
|
23
|
-
#
|
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
|
-
#
|
27
|
-
def count_status(status)
|
28
|
-
|
29
|
-
|
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
|
-
#
|
29
|
+
# Log a timing metric.
|
34
30
|
#
|
35
|
-
# name
|
36
|
-
#
|
37
|
-
#
|
38
|
-
|
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
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
|
58
|
-
|
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
|
data/lib/vault-tools/version.rb
CHANGED
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
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
|
-
#
|
18
|
-
#
|
19
|
-
#
|
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
|
-
#
|
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=
|
30
|
-
assert_match(/
|
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
|
35
|
-
#
|
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=
|
39
|
-
assert_match(/
|
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
|
-
#
|
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=
|
48
|
-
assert_match(/
|
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=
|
57
|
-
assert_match(/
|
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', '
|
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
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.
|
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-
|
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:
|
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:
|
157
|
+
hash: -4204391023637480040
|
141
158
|
requirements: []
|
142
159
|
rubyforge_project:
|
143
160
|
rubygems_version: 1.8.23
|