vault-tools 0.0.10 → 0.2.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.
@@ -0,0 +1 @@
1
+ -m markdown
data/Gemfile CHANGED
@@ -3,6 +3,13 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in vault-tools.gemspec
4
4
  gemspec
5
5
 
6
+ group :development do
7
+ gem 'rake'
8
+ gem 'ruby-debug19'
9
+ gem 'shotgun'
10
+ gem 'yard-sinatra'
11
+ end
12
+
6
13
  group :test do
7
14
  gem 'vault-test-tools', :path => '../vault-test-tools'
8
15
  end
data/Rakefile CHANGED
@@ -1 +1,11 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'yard'
3
+
4
+ desc "Test the things"
5
+ require 'vault-test-tools/rake_task'
6
+
7
+ desc "Doc the things"
8
+ YARD::Rake::YardocTask.new
9
+
10
+ desc "Create test databases and run the test suite"
11
+ task :default => [:test]
@@ -2,6 +2,7 @@ require "vault-tools/version"
2
2
 
3
3
  require 'sinatra/base'
4
4
  require 'scrolls'
5
+ require 'rack/ssl-enforcer'
5
6
 
6
7
  module Vault
7
8
  def self.require
@@ -2,15 +2,15 @@ require 'uuidtools'
2
2
 
3
3
  module Vault
4
4
  module App
5
- # Convert an app ID into a Heroku app ID.
5
+ # Convert a core app ID into a Heroku app ID.
6
6
  #
7
- # @param app_id [Fixnum] An app ID.
7
+ # @param app_id [Fixnum] A core app ID.
8
8
  # @return [String] A Heroku ID that uniquely represents the app.
9
9
  def self.id_to_hid(app_id)
10
10
  "app#{app_id}@heroku.com"
11
11
  end
12
12
 
13
- # Convert an app ID into a v5 UUID.
13
+ # Convert a core app ID into a v5 UUID.
14
14
  #
15
15
  # @param app_id [Fixnum] An app ID.
16
16
  # @return [String] A v5 UUID that uniquely represents the app.
@@ -19,6 +19,19 @@ module Vault
19
19
  UUIDTools::UUID.sha1_create(UUIDTools::UUID_URL_NAMESPACE, url).to_s
20
20
  end
21
21
 
22
+ # Convert a Heroku app ID into a core app ID.
23
+ #
24
+ # @param heroku_id [String] A Heroku app ID, such as `app1234@heroku.com`.
25
+ # @raise [ArgumentError] Raised if a malformed Heroku ID is provided.
26
+ # @return [Fixnum] The core app ID that uniquely represents the app.
27
+ def self.hid_to_id(heroku_id)
28
+ if app_id = heroku_id.slice(/^app(\d+)\@heroku\.com$/, 1)
29
+ app_id.to_i
30
+ else
31
+ raise ArgumentError,"#{heroku_id} is not a valid Heroku app ID."
32
+ end
33
+ end
34
+
22
35
  # Convert a Heroku app ID into a v5 UUID.
23
36
  #
24
37
  # @param heroku_id [String] A Heroku app ID, such as `app1234@heroku.com`.
@@ -10,6 +10,14 @@ module Vault
10
10
  env(key) || raise("missing #{key}")
11
11
  end
12
12
 
13
+ def production?
14
+ env('RACK_ENV') == 'production'
15
+ end
16
+
17
+ def test?
18
+ env('RACK_ENV') == 'test'
19
+ end
20
+
13
21
  def app_name; env("APP_NAME"); end
14
22
  def port; env!("PORT").to_i; end
15
23
 
@@ -17,5 +25,9 @@ module Vault
17
25
  kind = "#{kind}_".upcase unless kind.empty?
18
26
  env!("#{kind}DATABASE_URL")
19
27
  end
28
+
29
+ def enable_ssl?
30
+ !env('VAULT_TOOLS_DISABLE_SSL')
31
+ end
20
32
  end
21
33
  end
@@ -0,0 +1,29 @@
1
+ # Rake tasks for Core DB
2
+ #
3
+ # include in Rakefile via:
4
+ #
5
+ # require 'vault-tools/core_db_tasks'
6
+
7
+ desc "Pull db/structure.sql from core HEAD"
8
+ task :pull_core do
9
+ steps = []
10
+ steps << 'cd contrib/'
11
+ if File.exists?('contrib/core')
12
+ steps << 'rm -rf core'
13
+ end
14
+ steps << 'git clone -n git@github.com:heroku/core --depth 1'
15
+ steps << 'cd core'
16
+ steps << 'git checkout HEAD db/structure.sql'
17
+ sh steps.join(' && ')
18
+ end
19
+
20
+ desc "Drop and create vault-usage-test, core-test and shushu-test databases"
21
+ task :create_core_db => [:drop_core_db, :pull_core] do
22
+ sh 'createdb core-test'
23
+ sh 'psql core-test -f contrib/core/db/structure.sql'
24
+ end
25
+
26
+ desc "Drop the vault-usage-test, core-test and shushu-test databases"
27
+ task :drop_core_db do
28
+ sh 'dropdb core-test || true'
29
+ end
@@ -4,7 +4,7 @@ module Vault
4
4
  module HID
5
5
  # Convert a Heroku app ID or user ID into a v5 UUID.
6
6
  #
7
- # @param heroku_id [String] A Heroku app ID over user ID
7
+ # @param heroku_id [String] A Heroku app ID or user ID.
8
8
  # @raise [ArgumentError] Raised if a malformed Heroku ID is provided.
9
9
  # @return [String] A v5 UUID that uniquely represents the app.
10
10
  def self.hid_to_uuid(heroku_id)
@@ -19,6 +19,20 @@ module Vault
19
19
  UUIDTools::UUID.sha1_create(UUIDTools::UUID_URL_NAMESPACE, url).to_s
20
20
  end
21
21
 
22
+ # Convert a Heroku user ID into a core user ID.
23
+ #
24
+ # @param heroku_id [String] A Heroku user ID, such as
25
+ # `user1234@heroku.com`.
26
+ # @raise [ArgumentError] Raised if a malformed Heroku ID is provided.
27
+ # @return [Fixnum] The core user ID that uniquely represents the user.
28
+ def self.hid_to_id(heroku_id)
29
+ if user_id = heroku_id.slice(/^user(\d+)\@heroku\.com$/, 1)
30
+ user_id.to_i
31
+ else
32
+ raise ArgumentError,"#{heroku_id} is not a valid Heroku user ID."
33
+ end
34
+ end
35
+
22
36
  # Convert a Heroku user ID into a v5 UUID.
23
37
  #
24
38
  # @param heroku_id [String] A Heroku user ID, such as
@@ -1,5 +1,5 @@
1
1
  module Vault
2
2
  module Tools
3
- VERSION = '0.0.10'
3
+ VERSION = '0.2.0'
4
4
  end
5
5
  end
@@ -1,28 +1,71 @@
1
1
  require 'vault-tools/log'
2
2
 
3
3
  module Vault
4
+ # Base class for HTTP API services.
4
5
  class Web < Sinatra::Base
6
+ # Store the action for logging purposes.
5
7
  def self.route(verb, action, *)
6
8
  condition { @action = action }
7
9
  super
8
10
  end
9
11
 
12
+ # Start timing the request.
10
13
  before do
11
14
  @start_request = Time.now
12
15
  end
13
16
 
17
+ # Log details about the request including how long it took.
14
18
  after do
15
19
  Log.count_status(response.status)
16
20
  Log.time(@action, Time.now - @start_request)
17
21
  end
18
22
 
19
- # Health check on HEAD
20
- head('/') { status(200) }
23
+ # Make sure error handler blocks are invoked in tests.
24
+ set :show_exceptions, false
25
+ set :raise_errors, false
21
26
 
22
- # Trigger a 500 to test live monitoring and paging
23
- head('/boom') { status(500) }
27
+ # Require HTTPS connections when production mode is enabled.
28
+ use Rack::SslEnforcer if (Config.enable_ssl? && Config.production?)
24
29
 
25
- # Can do more than the head request
26
- get('/health') { [200, 'OK'] }
30
+ # Return an *HTTP 500 Internal Server Error* with a traceback in the
31
+ # body for easy debugging of errors.
32
+ error do
33
+ e = env['sinatra.error']
34
+ content = "#{e.class}: #{e.message}\n\n"
35
+ content << e.backtrace.join("\n")
36
+ [500, content]
37
+ end
38
+
39
+ # Determine if the service is running and responding to requests.
40
+ #
41
+ # @method status-check
42
+ # @return The following responses may be returned by this method:
43
+ #
44
+ # - *HTTP 200 OK*: Returned if the request was successful.
45
+ head '/' do
46
+ status(200)
47
+ end
48
+
49
+ # Determine if the service is running and responding to requests.
50
+ #
51
+ # @method health-check
52
+ # @return The following responses may be returned by this method:
53
+ #
54
+ # - *HTTP 200 OK*: Returned if the request was successful with `OK` in
55
+ # the body.
56
+ get '/health' do
57
+ [200, 'OK']
58
+ end
59
+
60
+ # Trigger an internal server error (to test monitoring and paging tools).
61
+ #
62
+ # @method boom
63
+ # @return The following responses may be returned by this method:
64
+ #
65
+ # - *HTTP 500 Internal Server Error*: Returned with a traceback in the
66
+ # body.
67
+ get '/boom' do
68
+ raise "An expected error occurred."
69
+ end
27
70
  end
28
71
  end
@@ -14,6 +14,21 @@ class AppTest < Vault::TestCase
14
14
  assert_equal(uuid, Vault::App.id_to_uuid(1234))
15
15
  end
16
16
 
17
+ # App.hid_to_id converts a Heroku app ID into a core integer app ID.
18
+ def test_hid_to_id
19
+ assert_equal(1234, Vault::App.hid_to_id('app1234@heroku.com'))
20
+ end
21
+
22
+ # App.hid_to_id raises an ArgumentError if the specified ID doesn't match
23
+ # the expected format.
24
+ def test_hid_to_id_with_invalid_heroku_id
25
+ error = assert_raises(ArgumentError) do
26
+ Vault::App.hid_to_id('invalid1234@heroku.com')
27
+ end
28
+ assert_equal('invalid1234@heroku.com is not a valid Heroku app ID.',
29
+ error.message)
30
+ end
31
+
17
32
  # App.hid_to_uuid converts a Heroku app ID to a v5 UUID based on a URL
18
33
  # scheme.
19
34
  def test_hid_to_uuid
@@ -22,7 +37,7 @@ class AppTest < Vault::TestCase
22
37
  assert_equal(uuid, Vault::App.hid_to_uuid('app1234@heroku.com'))
23
38
  end
24
39
 
25
- # App.hid_to_uuid raises a ArgumentError if the specified ID doesn't match
40
+ # App.hid_to_uuid raises an ArgumentError if the specified ID doesn't match
26
41
  # the expected format.
27
42
  def test_hid_to_uuid_with_invalid_heroku_id
28
43
  error = assert_raises(ArgumentError) do
@@ -2,6 +2,22 @@ require 'helper'
2
2
 
3
3
  class ConfigTest < Vault::TestCase
4
4
 
5
+ # Config.production? is true when RACK_ENV=production
6
+ def test_production_mode
7
+ set_env 'RACK_ENV', nil
8
+ refute Vault::Config.production?
9
+ set_env 'RACK_ENV', 'production'
10
+ assert Vault::Config.production?
11
+ end
12
+
13
+ # Config.test? is true when RACK_ENV=test
14
+ def test_test_mode
15
+ set_env 'RACK_ENV', nil
16
+ refute Vault::Config.test?
17
+ set_env 'RACK_ENV', 'test'
18
+ assert Vault::Config.test?
19
+ end
20
+
5
21
  # Returns DATABASE_URL with no params
6
22
  def test_database_url
7
23
  set_env 'DATABASE_URL', "postgres:///foo"
@@ -36,4 +52,15 @@ class ConfigTest < Vault::TestCase
36
52
  set_env 'PORT', "3000"
37
53
  Vault::Config.port.must_equal 3000
38
54
  end
55
+
56
+ # Config.enable_ssl? is true unless VAULT_TOOLS_DISABLE_SSL
57
+ # is set
58
+ def test_enable_ssl
59
+ set_env 'VAULT_TOOLS_DISABLE_SSL', nil
60
+ assert Vault::Config.enable_ssl?
61
+ set_env 'VAULT_TOOLS_DISABLE_SSL', '1'
62
+ refute Vault::Config.enable_ssl?
63
+ set_env 'VAULT_TOOLS_DISABLE_SSL', 'foo'
64
+ refute Vault::Config.enable_ssl?
65
+ end
39
66
  end
@@ -1,6 +1,6 @@
1
1
  require 'vault-test-tools'
2
2
  require 'vault-tools'
3
3
 
4
- module Vault::Test
5
- include_in_all EnvironmentHelpers
4
+ class Vault::TestCase
5
+ include Vault::Test::EnvironmentHelpers
6
6
  end
@@ -14,6 +14,21 @@ class UserTest < Vault::TestCase
14
14
  assert_equal(uuid, Vault::User.id_to_uuid(1234))
15
15
  end
16
16
 
17
+ # User.hid_to_id converts a Heroku user ID into a core integer user ID.
18
+ def test_hid_to_id
19
+ assert_equal(1234, Vault::User.hid_to_id('user1234@heroku.com'))
20
+ end
21
+
22
+ # User.hid_to_id raises an ArgumentError if the specified ID doesn't match
23
+ # the expected format.
24
+ def test_hid_to_id_with_invalid_heroku_id
25
+ error = assert_raises(ArgumentError) do
26
+ Vault::User.hid_to_id('invalid1234@heroku.com')
27
+ end
28
+ assert_equal('invalid1234@heroku.com is not a valid Heroku user ID.',
29
+ error.message)
30
+ end
31
+
17
32
  # User.hid_to_uuid converts a Heroku user ID to a v5 UUID based on a URL
18
33
  # scheme.
19
34
  def test_hid_to_uuid
@@ -22,7 +37,7 @@ class UserTest < Vault::TestCase
22
37
  assert_equal(uuid, Vault::User.hid_to_uuid('user1234@heroku.com'))
23
38
  end
24
39
 
25
- # User.hid_to_uuid raises a ArgumentError if the specified ID doesn't match
40
+ # User.hid_to_uuid raises an ArgumentError if the specified ID doesn't match
26
41
  # the expected format.
27
42
  def test_hid_to_uuid_with_invalid_heroku_id
28
43
  error = assert_raises(ArgumentError) do
@@ -0,0 +1,80 @@
1
+ require 'helper'
2
+
3
+ class WebTest < Vault::TestCase
4
+ include Rack::Test::Methods
5
+
6
+ # Anonymous Web Frontend
7
+ def app
8
+ Class.new(Vault::Web)
9
+ end
10
+
11
+ # Always reload the web class to eliminate test leakage
12
+ def setup
13
+ super
14
+ reload_web!
15
+ end
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
20
+ def reload_web!
21
+ # remove the constant to force a clean reload
22
+ Vault.send(:remove_const, 'Web')
23
+ load 'lib/vault-tools/web.rb'
24
+ end
25
+
26
+ # A successful request causes a `web-20` log entry to be written.
27
+ def test_head_status_check
28
+ head '/'
29
+ assert_match(/measure=true/, Scrolls.stream.string)
30
+ assert_match(/at=web-20/, Scrolls.stream.string)
31
+ assert_equal(200, last_response.status)
32
+ end
33
+
34
+ # A successful request causes `web-20` log entry to be written and `OK`
35
+ # content is returned in the response body.
36
+ def test_get_health_check
37
+ get '/health'
38
+ assert_match(/measure=true/, Scrolls.stream.string)
39
+ assert_match(/at=web-20/, Scrolls.stream.string)
40
+ assert_equal(200, last_response.status)
41
+ assert_equal('OK', last_response.body)
42
+ end
43
+
44
+ # A validation error causes a `web-40` log entry to be written.
45
+ def test_head_with_unknown_endpoint
46
+ head '/unknown'
47
+ assert_match(/measure=true/, Scrolls.stream.string)
48
+ assert_match(/at=web-40/, Scrolls.stream.string)
49
+ assert_equal(404, last_response.status)
50
+ end
51
+
52
+ # An internal server error causes a `web-50` log entry to be written. A
53
+ # traceback is also written to the response body to ease debugging.
54
+ def test_error_logs_500
55
+ get '/boom'
56
+ assert_match(/measure=true/, Scrolls.stream.string)
57
+ assert_match(/at=web-50/, Scrolls.stream.string)
58
+ assert_match(/^RuntimeError: An expected error occurred.$/m,
59
+ last_response.body)
60
+ assert_equal(500, last_response.status)
61
+ end
62
+
63
+
64
+ # SSL is enforced when we are in production mode
65
+ def test_ssl_enforced_in_production_mode
66
+ set_env 'RACK_ENV', 'production'
67
+ reload_web!
68
+ get '/health'
69
+ assert_equal(301, last_response.status)
70
+ assert_match(/^https/, last_response.headers['Location'])
71
+ end
72
+
73
+ def test_ssl_can_be_disabled
74
+ set_env 'RACK_ENV', 'production'
75
+ set_env 'VAULT_TOOLS_DISABLE_SSL', 'anything'
76
+ reload_web!
77
+ get '/health'
78
+ assert_equal(200, last_response.status)
79
+ end
80
+ end
@@ -20,4 +20,5 @@ Gem::Specification.new do |gem|
20
20
  gem.add_dependency 'scrolls'
21
21
  gem.add_dependency 'sinatra'
22
22
  gem.add_dependency 'uuidtools'
23
+ gem.add_dependency 'rack-ssl-enforcer'
23
24
  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.0.10
4
+ version: 0.2.0
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-03 00:00:00.000000000 Z
13
+ date: 2013-02-22 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: scrolls
@@ -60,6 +60,22 @@ dependencies:
60
60
  - - ! '>='
61
61
  - !ruby/object:Gem::Version
62
62
  version: '0'
63
+ - !ruby/object:Gem::Dependency
64
+ name: rack-ssl-enforcer
65
+ requirement: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ type: :runtime
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
63
79
  description: Basic tools for Heroku Vault's Ruby projects
64
80
  email:
65
81
  - christopher.continanza@gmail.com
@@ -71,22 +87,15 @@ extensions: []
71
87
  extra_rdoc_files: []
72
88
  files:
73
89
  - .gitignore
90
+ - .yardopts
74
91
  - Gemfile
75
92
  - LICENSE.txt
76
93
  - README.md
77
94
  - Rakefile
78
- - bin/d
79
- - bin/nokogiri
80
- - bin/pprof.rb
81
- - bin/rackup
82
- - bin/ri
83
- - bin/t
84
- - bin/tilt
85
- - bin/turn
86
- - bin/vault-tools
87
95
  - lib/vault-tools.rb
88
96
  - lib/vault-tools/app.rb
89
97
  - lib/vault-tools/config.rb
98
+ - lib/vault-tools/core_db_tasks.rb
90
99
  - lib/vault-tools/hid.rb
91
100
  - lib/vault-tools/log.rb
92
101
  - lib/vault-tools/pipeline.rb
@@ -103,6 +112,7 @@ files:
103
112
  - test/product_test.rb
104
113
  - test/test_spec.rb
105
114
  - test/user_test.rb
115
+ - test/web_test.rb
106
116
  - vault-tools.gemspec
107
117
  homepage: ''
108
118
  licenses: []
@@ -118,7 +128,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
118
128
  version: '0'
119
129
  segments:
120
130
  - 0
121
- hash: -2764314088517499753
131
+ hash: 29653798075093968
122
132
  required_rubygems_version: !ruby/object:Gem::Requirement
123
133
  none: false
124
134
  requirements:
@@ -127,7 +137,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
127
137
  version: '0'
128
138
  segments:
129
139
  - 0
130
- hash: -2764314088517499753
140
+ hash: 29653798075093968
131
141
  requirements: []
132
142
  rubyforge_project:
133
143
  rubygems_version: 1.8.23