guardrail 2.0.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +9 -37
- data/lib/guard_rail.rb +8 -103
- data/lib/guard_rail/version.rb +1 -1
- metadata +11 -58
- data/lib/guard_rail/connection_handler.rb +0 -15
- data/lib/guard_rail/connection_specification.rb +0 -79
- data/lib/guard_rail/railtie.rb +0 -11
- data/spec/guard_rail_spec.rb +0 -169
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b9e58f5329f5eda67ce7018576fc6c2d970df8325c3f60646caf25c2a185fc9
|
4
|
+
data.tar.gz: 5450d44f587e98fe84d12a5572f7aa2cde259fc75aebc9d1f28f4c535510af16
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b1c38cf3c156354856914ebba409fb4e5cb1287109ad66e07da1b0bd831e4dd522487bec8ba1b8bd056fafac7710747ffe691dbdf59a29b14f03999938ec687a
|
7
|
+
data.tar.gz: 5a8e7b29e2006a618045c1d17950b17bbaa13fa479589fc93b061055ea6f4ba80a6ccdfa532e75bb9cadc4cdaebd36a2c5b07261d7223b5f235491871e5913bf
|
data/README.md
CHANGED
@@ -3,55 +3,27 @@ GuardRail
|
|
3
3
|
|
4
4
|
## About
|
5
5
|
|
6
|
-
GuardRail
|
7
|
-
ActiveRecord, allowing least-privilege best practices.
|
6
|
+
GuardRail is a thin wrapper around Rail 6.1's native role switching.
|
8
7
|
|
9
8
|
## Installation
|
10
9
|
|
11
|
-
Add `gem 'guardrail'` to your Gemfile
|
12
|
-
with the release candidate for Rails 6.0)
|
10
|
+
Add `gem 'guardrail'` to your Gemfile.
|
13
11
|
|
14
12
|
## Usage
|
15
13
|
|
16
|
-
|
17
|
-
|
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.
|
14
|
+
See https://guides.rubyonrails.org/active_record_multiple_databases.html. GuardRail simply adds
|
15
|
+
syntactic sugar to easily switch to different roles:
|
23
16
|
|
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
17
|
|
45
18
|
```ruby
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
GuardRail.apply_config!(:username => ENV['RAILS_DATABASE_USER'])
|
19
|
+
def some_method
|
20
|
+
GuardRails.activate(:secondary) do
|
21
|
+
MyModel.some_really_long_query
|
22
|
+
end
|
51
23
|
end
|
52
24
|
```
|
53
25
|
|
54
|
-
Additionally
|
26
|
+
Additionally you can include GuardRail::HelperMethods and use several helpers
|
55
27
|
to execute methods on specific environments:
|
56
28
|
|
57
29
|
```ruby
|
data/lib/guard_rail.rb
CHANGED
@@ -1,114 +1,19 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module GuardRail
|
4
4
|
class << self
|
5
|
-
attr_accessor :primary_environment_name
|
6
|
-
GuardRail.primary_environment_name = :primary
|
7
|
-
|
8
5
|
def environment
|
9
|
-
|
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
|
6
|
+
ActiveRecord::Base.current_role
|
72
7
|
end
|
73
8
|
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
9
|
+
def activate(role)
|
10
|
+
return yield if environment == role
|
11
|
+
ActiveRecord::Base.connected_to(role: role) { yield }
|
82
12
|
end
|
83
13
|
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
14
|
+
def activate!(role)
|
15
|
+
return if environment == role
|
16
|
+
ActiveRecord::Base.connecting_to(role: role)
|
105
17
|
end
|
106
18
|
end
|
107
19
|
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
|
data/lib/guard_rail/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: guardrail
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cody Cutrer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,40 +16,40 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '6.1'
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '6.
|
22
|
+
version: '6.2'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
27
|
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: '
|
29
|
+
version: '6.1'
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: '6.
|
32
|
+
version: '6.2'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: railties
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
37
|
- - ">="
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: '
|
39
|
+
version: '6.1'
|
40
40
|
- - "<"
|
41
41
|
- !ruby/object:Gem::Version
|
42
|
-
version: '6.
|
42
|
+
version: '6.2'
|
43
43
|
type: :runtime
|
44
44
|
prerelease: false
|
45
45
|
version_requirements: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
47
|
- - ">="
|
48
48
|
- !ruby/object:Gem::Version
|
49
|
-
version: '
|
49
|
+
version: '6.1'
|
50
50
|
- - "<"
|
51
51
|
- !ruby/object:Gem::Version
|
52
|
-
version: '6.
|
52
|
+
version: '6.2'
|
53
53
|
- !ruby/object:Gem::Dependency
|
54
54
|
name: appraisal
|
55
55
|
requirement: !ruby/object:Gem::Requirement
|
@@ -78,48 +78,6 @@ dependencies:
|
|
78
78
|
- - ">="
|
79
79
|
- !ruby/object:Gem::Version
|
80
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
81
|
description: Allows multiple environments in database.yml, and dynamically switching
|
124
82
|
them.
|
125
83
|
email: cody@instructure.com
|
@@ -130,13 +88,9 @@ files:
|
|
130
88
|
- LICENSE
|
131
89
|
- README.md
|
132
90
|
- lib/guard_rail.rb
|
133
|
-
- lib/guard_rail/connection_handler.rb
|
134
|
-
- lib/guard_rail/connection_specification.rb
|
135
91
|
- lib/guard_rail/helper_methods.rb
|
136
|
-
- lib/guard_rail/railtie.rb
|
137
92
|
- lib/guard_rail/version.rb
|
138
93
|
- lib/guardrail.rb
|
139
|
-
- spec/guard_rail_spec.rb
|
140
94
|
homepage: http://github.com/instructure/guardrail
|
141
95
|
licenses:
|
142
96
|
- MIT
|
@@ -160,5 +114,4 @@ rubygems_version: 3.1.4
|
|
160
114
|
signing_key:
|
161
115
|
specification_version: 4
|
162
116
|
summary: ActiveRecord database environment switching for secondaries and least-privilege
|
163
|
-
test_files:
|
164
|
-
- spec/guard_rail_spec.rb
|
117
|
+
test_files: []
|
@@ -1,15 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module GuardRail
|
4
|
-
module ConnectionHandler
|
5
|
-
%w{clear_active_connections clear_reloadable_connections
|
6
|
-
clear_all_connections verify_active_connections }.each do |method|
|
7
|
-
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
8
|
-
def #{method}!(super_method: false)
|
9
|
-
return super() if super_method
|
10
|
-
::GuardRail.connection_handlers.values.each { |handler| handler.#{method}!(super_method: true) }
|
11
|
-
end
|
12
|
-
RUBY
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
@@ -1,79 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'i18n/core_ext/hash' unless Hash.method_defined?(:deep_symbolize_keys)
|
4
|
-
|
5
|
-
module GuardRail
|
6
|
-
module ConnectionSpecification
|
7
|
-
class CacheCoherentHash < Hash
|
8
|
-
def initialize(spec)
|
9
|
-
@spec = spec
|
10
|
-
super
|
11
|
-
end
|
12
|
-
|
13
|
-
def []=(key, value)
|
14
|
-
super
|
15
|
-
@spec.instance_variable_set(:@current_config, nil)
|
16
|
-
@spec.instance_variable_get(:@config)[key] = value
|
17
|
-
end
|
18
|
-
|
19
|
-
def delete(key)
|
20
|
-
super
|
21
|
-
@spec.instance_variable_set(:@current_config, nil)
|
22
|
-
@spec.instance_variable_get(:@config).delete(key)
|
23
|
-
end
|
24
|
-
|
25
|
-
def dup
|
26
|
-
Hash[self]
|
27
|
-
end
|
28
|
-
|
29
|
-
# in rails 4.2, active support tries to create a copy of the original object's class
|
30
|
-
# instead of making a new Hash object, so it fails since initialize expects an argument
|
31
|
-
def transform_keys(&block)
|
32
|
-
dup.transform_keys(&block)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def initialize(name, config, adapter_method)
|
37
|
-
super(name, config.deep_symbolize_keys, adapter_method)
|
38
|
-
end
|
39
|
-
|
40
|
-
def initialize_dup(original)
|
41
|
-
@current_config = nil
|
42
|
-
super
|
43
|
-
end
|
44
|
-
|
45
|
-
def config
|
46
|
-
@current_config = nil if GuardRail.environment != @current_config_environment || GuardRail.global_config_sequence != @current_config_sequence
|
47
|
-
return @current_config if @current_config
|
48
|
-
|
49
|
-
@current_config_environment = GuardRail.environment
|
50
|
-
@current_config_sequence = GuardRail.global_config_sequence
|
51
|
-
config = @config.dup
|
52
|
-
if @config.has_key?(GuardRail.environment)
|
53
|
-
env_config = @config[GuardRail.environment]
|
54
|
-
# an array of databases for this environment; for now, just choose the first non-nil element
|
55
|
-
if env_config.is_a?(Array)
|
56
|
-
env_config = env_config.detect { |individual_config| !individual_config.nil? }
|
57
|
-
end
|
58
|
-
config.merge!(env_config.symbolize_keys)
|
59
|
-
end
|
60
|
-
|
61
|
-
config.keys.each do |key|
|
62
|
-
next unless config[key].is_a?(String)
|
63
|
-
config[key] = config[key] % config
|
64
|
-
end
|
65
|
-
|
66
|
-
config.merge!(GuardRail.global_config)
|
67
|
-
|
68
|
-
@current_config = CacheCoherentHash.new(self)
|
69
|
-
@current_config.replace(config)
|
70
|
-
|
71
|
-
@current_config
|
72
|
-
end
|
73
|
-
|
74
|
-
def config=(value)
|
75
|
-
@config = value
|
76
|
-
@current_config = nil
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
data/lib/guard_rail/railtie.rb
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module GuardRail
|
4
|
-
class Railtie < Rails::Railtie
|
5
|
-
initializer "guard_rail.extend_ar", :before => "active_record.initialize_database" do
|
6
|
-
ActiveSupport.on_load(:active_record) do
|
7
|
-
GuardRail.initialize!
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
data/spec/guard_rail_spec.rb
DELETED
@@ -1,169 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_record'
|
4
|
-
require 'byebug'
|
5
|
-
require 'rails'
|
6
|
-
require 'guard_rail'
|
7
|
-
|
8
|
-
# we're not actually bringing up ActiveRecord, so we need to initialize our stuff
|
9
|
-
GuardRail.initialize!
|
10
|
-
|
11
|
-
RSpec.configure do |config|
|
12
|
-
config.mock_framework = :mocha
|
13
|
-
end
|
14
|
-
|
15
|
-
describe GuardRail do
|
16
|
-
ConnectionSpecification = ActiveRecord::ConnectionAdapters::ConnectionSpecification
|
17
|
-
|
18
|
-
def spec_args(conf, adapter)
|
19
|
-
['dummy', conf, adapter]
|
20
|
-
end
|
21
|
-
|
22
|
-
it "should allow changing environments" do
|
23
|
-
conf = {
|
24
|
-
:adapter => 'postgresql',
|
25
|
-
:database => 'primary',
|
26
|
-
:username => 'canvas',
|
27
|
-
:deploy => {
|
28
|
-
:username => 'deploy'
|
29
|
-
},
|
30
|
-
:replica => {
|
31
|
-
:database => 'replica'
|
32
|
-
}
|
33
|
-
}
|
34
|
-
spec = ConnectionSpecification.new(*spec_args(conf, 'adapter'))
|
35
|
-
expect(spec.config[:username]).to eq('canvas')
|
36
|
-
expect(spec.config[:database]).to eq('primary')
|
37
|
-
GuardRail.activate(:deploy) do
|
38
|
-
expect(spec.config[:username]).to eq('deploy')
|
39
|
-
expect(spec.config[:database]).to eq('primary')
|
40
|
-
end
|
41
|
-
expect(spec.config[:username]).to eq('canvas')
|
42
|
-
expect(spec.config[:database]).to eq('primary')
|
43
|
-
GuardRail.activate(:replica) do
|
44
|
-
expect(spec.config[:username]).to eq('canvas')
|
45
|
-
expect(spec.config[:database]).to eq('replica')
|
46
|
-
end
|
47
|
-
expect(spec.config[:username]).to eq('canvas')
|
48
|
-
expect(spec.config[:database]).to eq('primary')
|
49
|
-
end
|
50
|
-
|
51
|
-
it "should allow using hash insertions" do
|
52
|
-
conf = {
|
53
|
-
:adapter => 'postgresql',
|
54
|
-
:database => 'primary',
|
55
|
-
:username => '%{schema_search_path}',
|
56
|
-
:schema_search_path => 'canvas',
|
57
|
-
:deploy => {
|
58
|
-
:username => 'deploy'
|
59
|
-
}
|
60
|
-
}
|
61
|
-
spec = ConnectionSpecification.new(*spec_args(conf, 'adapter'))
|
62
|
-
expect(spec.config[:username]).to eq('canvas')
|
63
|
-
GuardRail.activate(:deploy) do
|
64
|
-
expect(spec.config[:username]).to eq('deploy')
|
65
|
-
end
|
66
|
-
expect(spec.config[:username]).to eq('canvas')
|
67
|
-
end
|
68
|
-
|
69
|
-
it "should be cache coherent with modifying the config" do
|
70
|
-
conf = {
|
71
|
-
:adapter => 'postgresql',
|
72
|
-
:database => 'primary',
|
73
|
-
:username => '%{schema_search_path}',
|
74
|
-
:schema_search_path => 'canvas',
|
75
|
-
:deploy => {
|
76
|
-
:username => 'deploy'
|
77
|
-
}
|
78
|
-
}
|
79
|
-
spec = ConnectionSpecification.new(*spec_args(conf.dup, 'adapter'))
|
80
|
-
expect(spec.config[:username]).to eq('canvas')
|
81
|
-
spec.config[:schema_search_path] = 'bob'
|
82
|
-
expect(spec.config[:schema_search_path]).to eq('bob')
|
83
|
-
expect(spec.config[:username]).to eq('bob')
|
84
|
-
GuardRail.activate(:deploy) do
|
85
|
-
expect(spec.config[:schema_search_path]).to eq('bob')
|
86
|
-
expect(spec.config[:username]).to eq('deploy')
|
87
|
-
end
|
88
|
-
external_config = spec.config.dup
|
89
|
-
expect(external_config.class).to eq(Hash)
|
90
|
-
expect(external_config).to eq(spec.config)
|
91
|
-
|
92
|
-
spec.config = conf.dup
|
93
|
-
expect(spec.config[:username]).to eq('canvas')
|
94
|
-
end
|
95
|
-
|
96
|
-
it "does not share config objects when dup'ing specs" do
|
97
|
-
conf = {
|
98
|
-
:adapter => 'postgresql',
|
99
|
-
:database => 'primary',
|
100
|
-
:username => '%{schema_search_path}',
|
101
|
-
:schema_search_path => 'canvas',
|
102
|
-
:deploy => {
|
103
|
-
:username => 'deploy'
|
104
|
-
}
|
105
|
-
}
|
106
|
-
spec = ConnectionSpecification.new(*spec_args(conf.dup, 'adapter'))
|
107
|
-
expect(spec.config.object_id).not_to eq spec.dup.config.object_id
|
108
|
-
end
|
109
|
-
|
110
|
-
describe "activate" do
|
111
|
-
before do
|
112
|
-
#!!! trick it in to actually switching envs
|
113
|
-
Rails.env.stubs(:test?).returns(false)
|
114
|
-
|
115
|
-
# be sure to test bugs where the current env isn't yet included in this hash
|
116
|
-
GuardRail.connection_handlers.clear
|
117
|
-
|
118
|
-
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
|
119
|
-
end
|
120
|
-
|
121
|
-
it "should call ensure_handler when switching envs" do
|
122
|
-
old_handler = ActiveRecord::Base.connection_handler
|
123
|
-
GuardRail.expects(:ensure_handler).returns(old_handler).twice
|
124
|
-
GuardRail.activate(:replica) {}
|
125
|
-
end
|
126
|
-
|
127
|
-
it "should not close connections when switching envs" do
|
128
|
-
conn = ActiveRecord::Base.connection
|
129
|
-
replica_conn = GuardRail.activate(:replica) { ActiveRecord::Base.connection }
|
130
|
-
expect(conn).not_to eq(replica_conn)
|
131
|
-
expect(ActiveRecord::Base.connection).to eq(conn)
|
132
|
-
end
|
133
|
-
|
134
|
-
it "should track all activated environments" do
|
135
|
-
GuardRail.activate(:replica) {}
|
136
|
-
GuardRail.activate(:custom) {}
|
137
|
-
expected = Set.new([:primary, :replica, :custom])
|
138
|
-
expect(GuardRail.activated_environments & expected).to eq(expected)
|
139
|
-
end
|
140
|
-
|
141
|
-
context "non-transactional" do
|
142
|
-
it "should really disconnect all envs" do
|
143
|
-
ActiveRecord::Base.connection
|
144
|
-
expect(ActiveRecord::Base.connection_pool).to be_connected
|
145
|
-
|
146
|
-
GuardRail.activate(:replica) do
|
147
|
-
ActiveRecord::Base.connection
|
148
|
-
expect(ActiveRecord::Base.connection_pool).to be_connected
|
149
|
-
end
|
150
|
-
|
151
|
-
ActiveRecord::Base.clear_all_connections!
|
152
|
-
expect(ActiveRecord::Base.connection_pool).not_to be_connected
|
153
|
-
GuardRail.activate(:replica) do
|
154
|
-
expect(ActiveRecord::Base.connection_pool).not_to be_connected
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
it 'is thread safe' do
|
160
|
-
GuardRail.activate(:replica) do
|
161
|
-
Thread.new do
|
162
|
-
GuardRail.activate!(:deploy)
|
163
|
-
expect(GuardRail.environment).to eq :deploy
|
164
|
-
end.join
|
165
|
-
expect(GuardRail.environment).to eq :replica
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|