vault-tools 0.0.10 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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