guardrail 2.0.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.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.md +76 -0
- data/lib/guard_rail.rb +114 -0
- data/lib/guard_rail/connection_handler.rb +13 -0
- data/lib/guard_rail/connection_specification.rb +77 -0
- data/lib/guard_rail/helper_methods.rb +38 -0
- data/lib/guard_rail/railtie.rb +9 -0
- data/lib/guard_rail/version.rb +3 -0
- data/lib/guardrail.rb +1 -0
- data/spec/guard_rail_spec.rb +167 -0
- metadata +164 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 737326a16630468262a493961348ecc21dd8b4f8d82c6526bfafe1c0b66c294c
|
4
|
+
data.tar.gz: c90574a6e50c6556e678b08acc766ef74c36be693e6d012989bb6fcb71e93c77
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d9761c6d3817b19a980827c71fceca3051cc8cbaf2b86bd959bf110f954a18c271e433bd433d92352d455e6af38234a1945369bbac118b142e1bcdfe45bbd78d
|
7
|
+
data.tar.gz: 20aab4abcd5ea1743df249d58d7772ac186277d6e8eb25e9772dd7aed46e41fabc1ac1870a4e802c048428f27f2bb7eb38bbbeffc1c951dd22e177c5f92d54ab
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Instructure, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
GuardRail
|
2
|
+
==========
|
3
|
+
|
4
|
+
## About
|
5
|
+
|
6
|
+
GuardRail allows multiple database environments and environment overrides to
|
7
|
+
ActiveRecord, allowing least-privilege best practices.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add `gem 'guardrail'` to your Gemfile (tested with Rails 4.x and 5.x, and also
|
12
|
+
with the release candidate for Rails 6.0)
|
13
|
+
|
14
|
+
## Usage
|
15
|
+
|
16
|
+
There are two major use cases for guardrail. The first is for primary/replica(/deploy) environments.
|
17
|
+
Using a replica is as simple as adding a replica block (underneath your main environment block) in
|
18
|
+
database.yml, then wrapping stuff you want to query the replica in GuardRail.activate(:replica) blocks.
|
19
|
+
You can extend this to a deploy environment so that migrations will run as a deploy user that
|
20
|
+
permission to modify schema, while your normal app runs with lower privileges that cannot modify
|
21
|
+
schema. This is defense-in-depth in practice, so that *if* you happen to have a SQL injection
|
22
|
+
bug, it would be impossible to do something like dropping tables.
|
23
|
+
|
24
|
+
The other major use case is more defense-in-depth. By carefully setting up your environment, you
|
25
|
+
can default to script/console sessions for regular users to use their own database user, and the
|
26
|
+
replica.
|
27
|
+
|
28
|
+
Example database.yml file:
|
29
|
+
|
30
|
+
```yaml
|
31
|
+
production:
|
32
|
+
adapter: postgresql
|
33
|
+
username: myapp
|
34
|
+
database: myapp
|
35
|
+
host: db-primary
|
36
|
+
replica:
|
37
|
+
host: db-replica
|
38
|
+
deploy:
|
39
|
+
username: deploy
|
40
|
+
```
|
41
|
+
|
42
|
+
Using an initializer, you can achieve the default environment settings (in tandem with profile
|
43
|
+
changes):
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
if ENV['RAILS_DATABASE_ENVIRONMENT']
|
47
|
+
GuardRail.activate!(ENV['RAILS_DATABASE_ENVIRONMENT'].to_sym)
|
48
|
+
end
|
49
|
+
if ENV['RAILS_DATABASE_USER']
|
50
|
+
GuardRail.apply_config!(:username => ENV['RAILS_DATABASE_USER'])
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
Additionally **in Ruby 2.0+** you can include GuardRail::HelperMethods and use several helpers
|
55
|
+
to execute methods on specific environments:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
class SomeModel
|
59
|
+
include GuardRail::HelperMethods
|
60
|
+
|
61
|
+
def expensive_read_only
|
62
|
+
...
|
63
|
+
end
|
64
|
+
guard_rail_method :expensive_read_only, environment: :replica
|
65
|
+
|
66
|
+
def self.class_level_expensive_read_only
|
67
|
+
...
|
68
|
+
end
|
69
|
+
gaurd_rail_class_method :class_level_expensive_read_only, environment: :replica
|
70
|
+
|
71
|
+
# helpers for multiple methods are also available
|
72
|
+
|
73
|
+
guard_rail_methods :instance_method_foo, :instance_method_bar, environment: :replica
|
74
|
+
guard_rail_class_methods :class_method_foo, :class_method_bar, environment: :replica
|
75
|
+
end
|
76
|
+
```
|
data/lib/guard_rail.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module GuardRail
|
4
|
+
class << self
|
5
|
+
attr_accessor :primary_environment_name
|
6
|
+
GuardRail.primary_environment_name = :primary
|
7
|
+
|
8
|
+
def environment
|
9
|
+
Thread.current[:guard_rail_environment] ||= primary_environment_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def global_config
|
13
|
+
@global_config ||= {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def activated_environments
|
17
|
+
@activated_environments ||= Set.new()
|
18
|
+
end
|
19
|
+
|
20
|
+
# semi-private
|
21
|
+
def initialize!
|
22
|
+
require 'guard_rail/connection_handler'
|
23
|
+
require 'guard_rail/connection_specification'
|
24
|
+
require 'guard_rail/helper_methods'
|
25
|
+
|
26
|
+
activated_environments << GuardRail.environment
|
27
|
+
|
28
|
+
ActiveRecord::ConnectionAdapters::ConnectionHandler.prepend(ConnectionHandler)
|
29
|
+
ActiveRecord::ConnectionAdapters::ConnectionSpecification.prepend(ConnectionSpecification)
|
30
|
+
end
|
31
|
+
|
32
|
+
def global_config_sequence
|
33
|
+
@global_config_sequence ||= 1
|
34
|
+
end
|
35
|
+
|
36
|
+
def bump_sequence
|
37
|
+
@global_config_sequence ||= 1
|
38
|
+
@global_config_sequence += 1
|
39
|
+
ActiveRecord::Base::connection_handler.clear_all_connections!
|
40
|
+
end
|
41
|
+
|
42
|
+
# for altering other pieces of config (i.e. username)
|
43
|
+
# will force a disconnect
|
44
|
+
def apply_config!(hash)
|
45
|
+
global_config.merge!(hash)
|
46
|
+
bump_sequence
|
47
|
+
end
|
48
|
+
|
49
|
+
def remove_config!(key)
|
50
|
+
global_config.delete(key)
|
51
|
+
bump_sequence
|
52
|
+
end
|
53
|
+
|
54
|
+
def connection_handlers
|
55
|
+
save_handler
|
56
|
+
@connection_handlers
|
57
|
+
end
|
58
|
+
|
59
|
+
# switch environment for the duration of the block
|
60
|
+
# will keep the old connections around
|
61
|
+
def activate(environment)
|
62
|
+
environment ||= primary_environment_name
|
63
|
+
return yield if environment == self.environment
|
64
|
+
begin
|
65
|
+
old_environment = activate!(environment)
|
66
|
+
activated_environments << environment
|
67
|
+
yield
|
68
|
+
ensure
|
69
|
+
Thread.current[:guard_rail_environment] = old_environment
|
70
|
+
ActiveRecord::Base.connection_handler = ensure_handler unless test?
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# for use from script/console ONLY
|
75
|
+
def activate!(environment)
|
76
|
+
environment ||= primary_environment_name
|
77
|
+
save_handler
|
78
|
+
old_environment = self.environment
|
79
|
+
Thread.current[:guard_rail_environment] = environment
|
80
|
+
ActiveRecord::Base.connection_handler = ensure_handler unless test?
|
81
|
+
old_environment
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def test?
|
87
|
+
Rails.env.test?
|
88
|
+
end
|
89
|
+
|
90
|
+
def save_handler
|
91
|
+
@connection_handlers ||= {}
|
92
|
+
@connection_handlers[environment] ||= ActiveRecord::Base.connection_handler
|
93
|
+
end
|
94
|
+
|
95
|
+
def ensure_handler
|
96
|
+
new_handler = @connection_handlers[environment]
|
97
|
+
if !new_handler
|
98
|
+
new_handler = @connection_handlers[environment] = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
|
99
|
+
pools = ActiveRecord::Base.connection_handler.send(:owner_to_pool)
|
100
|
+
pools.each_pair do |model, pool|
|
101
|
+
new_handler.establish_connection(pool.spec.config)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
new_handler
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
if defined?(Rails::Railtie)
|
110
|
+
require "guard_rail/railtie"
|
111
|
+
else
|
112
|
+
# just load everything immediately for Rails 2
|
113
|
+
GuardRail.initialize!
|
114
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module GuardRail
|
2
|
+
module ConnectionHandler
|
3
|
+
%w{clear_active_connections clear_reloadable_connections
|
4
|
+
clear_all_connections verify_active_connections }.each do |method|
|
5
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
6
|
+
def #{method}!(super_method: false)
|
7
|
+
return super() if super_method
|
8
|
+
::GuardRail.connection_handlers.values.each { |handler| handler.#{method}!(super_method: true) }
|
9
|
+
end
|
10
|
+
RUBY
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'i18n/core_ext/hash' unless Hash.method_defined?(:deep_symbolize_keys)
|
2
|
+
|
3
|
+
module GuardRail
|
4
|
+
module ConnectionSpecification
|
5
|
+
class CacheCoherentHash < Hash
|
6
|
+
def initialize(spec)
|
7
|
+
@spec = spec
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def []=(key, value)
|
12
|
+
super
|
13
|
+
@spec.instance_variable_set(:@current_config, nil)
|
14
|
+
@spec.instance_variable_get(:@config)[key] = value
|
15
|
+
end
|
16
|
+
|
17
|
+
def delete(key)
|
18
|
+
super
|
19
|
+
@spec.instance_variable_set(:@current_config, nil)
|
20
|
+
@spec.instance_variable_get(:@config).delete(key)
|
21
|
+
end
|
22
|
+
|
23
|
+
def dup
|
24
|
+
Hash[self]
|
25
|
+
end
|
26
|
+
|
27
|
+
# in rails 4.2, active support tries to create a copy of the original object's class
|
28
|
+
# instead of making a new Hash object, so it fails since initialize expects an argument
|
29
|
+
def transform_keys(&block)
|
30
|
+
dup.transform_keys(&block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(name, config, adapter_method)
|
35
|
+
super(name, config.deep_symbolize_keys, adapter_method)
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize_dup(original)
|
39
|
+
@current_config = nil
|
40
|
+
super
|
41
|
+
end
|
42
|
+
|
43
|
+
def config
|
44
|
+
@current_config = nil if GuardRail.environment != @current_config_environment || GuardRail.global_config_sequence != @current_config_sequence
|
45
|
+
return @current_config if @current_config
|
46
|
+
|
47
|
+
@current_config_environment = GuardRail.environment
|
48
|
+
@current_config_sequence = GuardRail.global_config_sequence
|
49
|
+
config = @config.dup
|
50
|
+
if @config.has_key?(GuardRail.environment)
|
51
|
+
env_config = @config[GuardRail.environment]
|
52
|
+
# an array of databases for this environment; for now, just choose the first non-nil element
|
53
|
+
if env_config.is_a?(Array)
|
54
|
+
env_config = env_config.detect { |individual_config| !individual_config.nil? }
|
55
|
+
end
|
56
|
+
config.merge!(env_config.symbolize_keys)
|
57
|
+
end
|
58
|
+
|
59
|
+
config.keys.each do |key|
|
60
|
+
next unless config[key].is_a?(String)
|
61
|
+
config[key] = config[key] % config
|
62
|
+
end
|
63
|
+
|
64
|
+
config.merge!(GuardRail.global_config)
|
65
|
+
|
66
|
+
@current_config = CacheCoherentHash.new(self)
|
67
|
+
@current_config.replace(config)
|
68
|
+
|
69
|
+
@current_config
|
70
|
+
end
|
71
|
+
|
72
|
+
def config=(value)
|
73
|
+
@config = value
|
74
|
+
@current_config = nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module GuardRail
|
2
|
+
module HelperMethods
|
3
|
+
def self.included(base)
|
4
|
+
base.singleton_class.include(ClassMethods)
|
5
|
+
# call guard_rail_class_method on the class itself, which then calls guard_rail_method on the singleton_class
|
6
|
+
base.singleton_class.singleton_class.include(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
# see readme for example usage
|
10
|
+
module ClassMethods
|
11
|
+
def guard_rail_class_methods(*methods, opts)
|
12
|
+
methods.each { |m| guard_rail_class_method(m, opts) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def guard_rail_class_method(method, opts)
|
16
|
+
self.singleton_class.guard_rail_method(method, opts)
|
17
|
+
end
|
18
|
+
|
19
|
+
def guard_rail_methods(*methods, opts)
|
20
|
+
methods.each { |m| guard_rail_method(m, opts) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def guard_rail_method(method, opts)
|
24
|
+
@guard_rail_module ||= begin
|
25
|
+
m = Module.new
|
26
|
+
self.prepend m
|
27
|
+
m
|
28
|
+
end
|
29
|
+
|
30
|
+
@guard_rail_module.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
31
|
+
def #{method}(*args)
|
32
|
+
GuardRail.activate(#{opts[:environment].inspect}) { super }
|
33
|
+
end
|
34
|
+
RUBY
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/guardrail.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'guard_rail'
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'byebug'
|
3
|
+
require 'rails'
|
4
|
+
require 'guard_rail'
|
5
|
+
|
6
|
+
# we're not actually bringing up ActiveRecord, so we need to initialize our stuff
|
7
|
+
GuardRail.initialize!
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.mock_framework = :mocha
|
11
|
+
end
|
12
|
+
|
13
|
+
describe GuardRail do
|
14
|
+
ConnectionSpecification = ActiveRecord::ConnectionAdapters::ConnectionSpecification
|
15
|
+
|
16
|
+
def spec_args(conf, adapter)
|
17
|
+
['dummy', conf, adapter]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should allow changing environments" do
|
21
|
+
conf = {
|
22
|
+
:adapter => 'postgresql',
|
23
|
+
:database => 'primary',
|
24
|
+
:username => 'canvas',
|
25
|
+
:deploy => {
|
26
|
+
:username => 'deploy'
|
27
|
+
},
|
28
|
+
:replica => {
|
29
|
+
:database => 'replica'
|
30
|
+
}
|
31
|
+
}
|
32
|
+
spec = ConnectionSpecification.new(*spec_args(conf, 'adapter'))
|
33
|
+
expect(spec.config[:username]).to eq('canvas')
|
34
|
+
expect(spec.config[:database]).to eq('primary')
|
35
|
+
GuardRail.activate(:deploy) do
|
36
|
+
expect(spec.config[:username]).to eq('deploy')
|
37
|
+
expect(spec.config[:database]).to eq('primary')
|
38
|
+
end
|
39
|
+
expect(spec.config[:username]).to eq('canvas')
|
40
|
+
expect(spec.config[:database]).to eq('primary')
|
41
|
+
GuardRail.activate(:replica) do
|
42
|
+
expect(spec.config[:username]).to eq('canvas')
|
43
|
+
expect(spec.config[:database]).to eq('replica')
|
44
|
+
end
|
45
|
+
expect(spec.config[:username]).to eq('canvas')
|
46
|
+
expect(spec.config[:database]).to eq('primary')
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should allow using hash insertions" do
|
50
|
+
conf = {
|
51
|
+
:adapter => 'postgresql',
|
52
|
+
:database => 'primary',
|
53
|
+
:username => '%{schema_search_path}',
|
54
|
+
:schema_search_path => 'canvas',
|
55
|
+
:deploy => {
|
56
|
+
:username => 'deploy'
|
57
|
+
}
|
58
|
+
}
|
59
|
+
spec = ConnectionSpecification.new(*spec_args(conf, 'adapter'))
|
60
|
+
expect(spec.config[:username]).to eq('canvas')
|
61
|
+
GuardRail.activate(:deploy) do
|
62
|
+
expect(spec.config[:username]).to eq('deploy')
|
63
|
+
end
|
64
|
+
expect(spec.config[:username]).to eq('canvas')
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should be cache coherent with modifying the config" do
|
68
|
+
conf = {
|
69
|
+
:adapter => 'postgresql',
|
70
|
+
:database => 'primary',
|
71
|
+
:username => '%{schema_search_path}',
|
72
|
+
:schema_search_path => 'canvas',
|
73
|
+
:deploy => {
|
74
|
+
:username => 'deploy'
|
75
|
+
}
|
76
|
+
}
|
77
|
+
spec = ConnectionSpecification.new(*spec_args(conf.dup, 'adapter'))
|
78
|
+
expect(spec.config[:username]).to eq('canvas')
|
79
|
+
spec.config[:schema_search_path] = 'bob'
|
80
|
+
expect(spec.config[:schema_search_path]).to eq('bob')
|
81
|
+
expect(spec.config[:username]).to eq('bob')
|
82
|
+
GuardRail.activate(:deploy) do
|
83
|
+
expect(spec.config[:schema_search_path]).to eq('bob')
|
84
|
+
expect(spec.config[:username]).to eq('deploy')
|
85
|
+
end
|
86
|
+
external_config = spec.config.dup
|
87
|
+
expect(external_config.class).to eq(Hash)
|
88
|
+
expect(external_config).to eq(spec.config)
|
89
|
+
|
90
|
+
spec.config = conf.dup
|
91
|
+
expect(spec.config[:username]).to eq('canvas')
|
92
|
+
end
|
93
|
+
|
94
|
+
it "does not share config objects when dup'ing specs" do
|
95
|
+
conf = {
|
96
|
+
:adapter => 'postgresql',
|
97
|
+
:database => 'primary',
|
98
|
+
:username => '%{schema_search_path}',
|
99
|
+
:schema_search_path => 'canvas',
|
100
|
+
:deploy => {
|
101
|
+
:username => 'deploy'
|
102
|
+
}
|
103
|
+
}
|
104
|
+
spec = ConnectionSpecification.new(*spec_args(conf.dup, 'adapter'))
|
105
|
+
expect(spec.config.object_id).not_to eq spec.dup.config.object_id
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "activate" do
|
109
|
+
before do
|
110
|
+
#!!! trick it in to actually switching envs
|
111
|
+
Rails.env.stubs(:test?).returns(false)
|
112
|
+
|
113
|
+
# be sure to test bugs where the current env isn't yet included in this hash
|
114
|
+
GuardRail.connection_handlers.clear
|
115
|
+
|
116
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should call ensure_handler when switching envs" do
|
120
|
+
old_handler = ActiveRecord::Base.connection_handler
|
121
|
+
GuardRail.expects(:ensure_handler).returns(old_handler).twice
|
122
|
+
GuardRail.activate(:replica) {}
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should not close connections when switching envs" do
|
126
|
+
conn = ActiveRecord::Base.connection
|
127
|
+
replica_conn = GuardRail.activate(:replica) { ActiveRecord::Base.connection }
|
128
|
+
expect(conn).not_to eq(replica_conn)
|
129
|
+
expect(ActiveRecord::Base.connection).to eq(conn)
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should track all activated environments" do
|
133
|
+
GuardRail.activate(:replica) {}
|
134
|
+
GuardRail.activate(:custom) {}
|
135
|
+
expected = Set.new([:primary, :replica, :custom])
|
136
|
+
expect(GuardRail.activated_environments & expected).to eq(expected)
|
137
|
+
end
|
138
|
+
|
139
|
+
context "non-transactional" do
|
140
|
+
it "should really disconnect all envs" do
|
141
|
+
ActiveRecord::Base.connection
|
142
|
+
expect(ActiveRecord::Base.connection_pool).to be_connected
|
143
|
+
|
144
|
+
GuardRail.activate(:replica) do
|
145
|
+
ActiveRecord::Base.connection
|
146
|
+
expect(ActiveRecord::Base.connection_pool).to be_connected
|
147
|
+
end
|
148
|
+
|
149
|
+
ActiveRecord::Base.clear_all_connections!
|
150
|
+
expect(ActiveRecord::Base.connection_pool).not_to be_connected
|
151
|
+
GuardRail.activate(:replica) do
|
152
|
+
expect(ActiveRecord::Base.connection_pool).not_to be_connected
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'is thread safe' do
|
158
|
+
GuardRail.activate(:replica) do
|
159
|
+
Thread.new do
|
160
|
+
GuardRail.activate!(:deploy)
|
161
|
+
expect(GuardRail.environment).to eq :deploy
|
162
|
+
end.join
|
163
|
+
expect(GuardRail.environment).to eq :replica
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
metadata
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: guardrail
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Cody Cutrer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-10-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.1'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '6.1'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '5.1'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '6.1'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: railties
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '5.1'
|
40
|
+
- - "<"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '6.1'
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '5.1'
|
50
|
+
- - "<"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '6.1'
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: appraisal
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
type: :development
|
61
|
+
prerelease: false
|
62
|
+
version_requirements: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: byebug
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
type: :development
|
75
|
+
prerelease: false
|
76
|
+
version_requirements: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: mocha
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: rspec
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - "~>"
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '3.0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - "~>"
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '3.0'
|
109
|
+
- !ruby/object:Gem::Dependency
|
110
|
+
name: sqlite3
|
111
|
+
requirement: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
type: :development
|
117
|
+
prerelease: false
|
118
|
+
version_requirements: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
description: Allows multiple environments in database.yml, and dynamically switching
|
124
|
+
them.
|
125
|
+
email: cody@instructure.com
|
126
|
+
executables: []
|
127
|
+
extensions: []
|
128
|
+
extra_rdoc_files: []
|
129
|
+
files:
|
130
|
+
- LICENSE
|
131
|
+
- README.md
|
132
|
+
- lib/guard_rail.rb
|
133
|
+
- lib/guard_rail/connection_handler.rb
|
134
|
+
- lib/guard_rail/connection_specification.rb
|
135
|
+
- lib/guard_rail/helper_methods.rb
|
136
|
+
- lib/guard_rail/railtie.rb
|
137
|
+
- lib/guard_rail/version.rb
|
138
|
+
- lib/guardrail.rb
|
139
|
+
- spec/guard_rail_spec.rb
|
140
|
+
homepage: http://github.com/instructure/guardrail
|
141
|
+
licenses:
|
142
|
+
- MIT
|
143
|
+
metadata: {}
|
144
|
+
post_install_message:
|
145
|
+
rdoc_options: []
|
146
|
+
require_paths:
|
147
|
+
- lib
|
148
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '2.5'
|
153
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
requirements: []
|
159
|
+
rubygems_version: 3.1.2
|
160
|
+
signing_key:
|
161
|
+
specification_version: 4
|
162
|
+
summary: ActiveRecord database environment switching for secondaries and least-privilege
|
163
|
+
test_files:
|
164
|
+
- spec/guard_rail_spec.rb
|