consul_application_settings 3.0.1 → 4.0.0.pre.alpha
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 +4 -4
- data/.github/workflows/main.yml +9 -2
- data/.rubocop.yml +2 -1
- data/CHANGELOG.md +11 -1
- data/Gemfile.lock +7 -0
- data/README.md +100 -59
- data/bin/benchmark +1 -2
- data/consul_application_settings.gemspec +3 -1
- data/docker-compose.yml +17 -0
- data/lib/consul_application_settings/configuration.rb +7 -2
- data/lib/consul_application_settings/providers/abstract.rb +4 -3
- data/lib/consul_application_settings/providers/consul.rb +10 -2
- data/lib/consul_application_settings/providers/consul_preloaded.rb +22 -4
- data/lib/consul_application_settings/providers/local_storage.rb +1 -1
- data/lib/consul_application_settings/reader.rb +31 -6
- data/lib/consul_application_settings/resolvers/abstract.rb +14 -0
- data/lib/consul_application_settings/resolvers/env.rb +19 -0
- data/lib/consul_application_settings/resolvers/erb.rb +18 -0
- data/lib/consul_application_settings/resolvers/vault.rb +24 -0
- data/lib/consul_application_settings/utils.rb +12 -0
- data/lib/consul_application_settings/version.rb +1 -1
- data/lib/consul_application_settings.rb +3 -1
- metadata +40 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 709d93b5794a39ed2f78eb61462cc07ff0ed77dccd66343ad22fc13abc45c267
|
4
|
+
data.tar.gz: 6daa5f0e9266fad7baee4e44efe8ed49ac5451744bd3abb60667afb1afb1a132
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 399c74e1bed212dea80f9b5d3f0f202c702b2e7704824214703f8003841b35792b66e3c6707ec7594d4b6860acbdd1b341c65e85312448d9fef3e2aa4a1c6c16
|
7
|
+
data.tar.gz: 4b04c272e96bc975d0cae6984dc6c44e4d442b0dc1a78a51b9d6ca772b4d5b09bcc10ce6155113a955210e9abe4d91ccb446224fd881f0a79f29da55aebb6fc7
|
data/.github/workflows/main.yml
CHANGED
@@ -14,12 +14,19 @@ jobs:
|
|
14
14
|
runs-on: ubuntu-latest
|
15
15
|
strategy:
|
16
16
|
matrix:
|
17
|
-
ruby: [ '2.5.x', '2.6.x' ]
|
17
|
+
ruby: [ '2.5.x', '2.6.x', '2.7.x', '3.0.x' ]
|
18
18
|
services:
|
19
|
-
|
19
|
+
consul:
|
20
20
|
image: consul:1.6
|
21
21
|
ports:
|
22
22
|
- '8500:8500'
|
23
|
+
vault:
|
24
|
+
image: vault
|
25
|
+
ports:
|
26
|
+
- "8200:8200"
|
27
|
+
env:
|
28
|
+
VAULT_DEV_ROOT_TOKEN_ID: root_token
|
29
|
+
SKIP_SETCAP: true
|
23
30
|
steps:
|
24
31
|
- name: Checkout
|
25
32
|
uses: actions/checkout@v1
|
data/.rubocop.yml
CHANGED
@@ -2,6 +2,7 @@ AllCops:
|
|
2
2
|
TargetRubyVersion: 2.5
|
3
3
|
Exclude:
|
4
4
|
- 'tmp/**/*'
|
5
|
+
- 'bin/**/*'
|
5
6
|
|
6
7
|
Metrics/LineLength:
|
7
8
|
Max: 120
|
@@ -10,4 +11,4 @@ Style/FrozenStringLiteralComment:
|
|
10
11
|
Enabled: false
|
11
12
|
|
12
13
|
Metrics/BlockLength:
|
13
|
-
ExcludedMethods: ['describe', 'context']
|
14
|
+
ExcludedMethods: ['describe', 'context', 'shared_examples']
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [4.0.0-alpha]
|
4
|
+
### Breaking Changes
|
5
|
+
- When using `settings.get` to retrieve object - exception is raised
|
6
|
+
### New features
|
7
|
+
- Introduce resolvers concept
|
8
|
+
- Add environment resolver
|
9
|
+
- Add erb resolver
|
10
|
+
- Add vault alpha resolver
|
11
|
+
|
3
12
|
## [3.0.1]
|
4
13
|
### Fixes
|
5
14
|
- Fix exception when preloading settings without consul
|
@@ -59,7 +68,8 @@
|
|
59
68
|
- Support deep settings search
|
60
69
|
- Support nested configs
|
61
70
|
|
62
|
-
[Unreleased]: https://github.com/matic-insurance/consul_application_settings/compare/
|
71
|
+
[Unreleased]: https://github.com/matic-insurance/consul_application_settings/compare/4.0.0-alpha...HEAD
|
72
|
+
[4.0.0-alpha]: https://github.com/matic-insurance/consul_application_settings/compare/3.0.1...4.0.0-alpha
|
63
73
|
[3.0.1]: https://github.com/matic-insurance/consul_application_settings/compare/3.0.0...3.0.1
|
64
74
|
[3.0.0]: https://github.com/matic-insurance/consul_application_settings/compare/2.0.0...3.0.0
|
65
75
|
[2.1.1]: https://github.com/matic-insurance/consul_application_settings/compare/2.1.0...2.1.1
|
data/Gemfile.lock
CHANGED
@@ -2,12 +2,16 @@ PATH
|
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
4
|
consul_application_settings (0.0.0)
|
5
|
+
deep_merge (~> 1.2)
|
5
6
|
diplomat (~> 2.5.1)
|
6
7
|
|
7
8
|
GEM
|
8
9
|
remote: https://rubygems.org/
|
9
10
|
specs:
|
10
11
|
ast (2.4.0)
|
12
|
+
aws-eventstream (1.2.0)
|
13
|
+
aws-sigv4 (1.4.0)
|
14
|
+
aws-eventstream (~> 1, >= 1.0.2)
|
11
15
|
codecov (0.4.3)
|
12
16
|
simplecov (>= 0.15, < 0.22)
|
13
17
|
deep_merge (1.2.1)
|
@@ -61,6 +65,8 @@ GEM
|
|
61
65
|
simplecov-html (~> 0.10.0)
|
62
66
|
simplecov-html (0.10.2)
|
63
67
|
unicode-display_width (1.5.0)
|
68
|
+
vault (0.16.0)
|
69
|
+
aws-sigv4
|
64
70
|
|
65
71
|
PLATFORMS
|
66
72
|
ruby
|
@@ -74,6 +80,7 @@ DEPENDENCIES
|
|
74
80
|
rubocop (~> 0.66)
|
75
81
|
rubocop-rspec (~> 1.32.0)
|
76
82
|
simplecov (~> 0.16)
|
83
|
+
vault (~> 0.16)
|
77
84
|
|
78
85
|
BUNDLED WITH
|
79
86
|
2.1.2
|
data/README.md
CHANGED
@@ -3,26 +3,16 @@
|
|
3
3
|

|
4
4
|
[](https://codecov.io/gh/matic-insurance/consul_application_settings)
|
5
5
|
|
6
|
-
|
7
|
-
to host application settings.
|
8
|
-
to simplify storage and control of application with Consul KV storage.
|
6
|
+
This gem that simplifies usage of Consul (via [Diplomat gem](https://github.com/WeAreFarmGeek/diplomat))
|
7
|
+
to host application settings.
|
9
8
|
|
10
|
-
|
11
|
-
|
9
|
+
Except reading value from Consul the gem also:
|
10
|
+
- Fallbacks to YAML if value is missing in consul
|
11
|
+
- Resolve actual value from other sources to facilitate overriding via ENV, storing secret values in Vault,
|
12
|
+
or executing small ERB snippets
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
- One engineer has created a new feature that depend on consul key/value.
|
16
|
-
|
17
|
-
How enginner can notify other engineers that they need to set this value in their consul environments?
|
18
|
-
|
19
|
-
- DevOps team responsible to configure and maintain deployment.
|
20
|
-
|
21
|
-
How do they learn (have reference) of what settings and structure application expect?
|
22
|
-
|
23
|
-
Gem reads any particular setting from consul and if it is missing tries to find value in YAML defaults file
|
24
|
-
|
25
|
-
**NOTE** Consul is requested every time you query the settings. Defaults YAML file is loaded in memory and is not changing.
|
14
|
+
Default values in YAML also can be considered as a way to communicate structure of settings to other engineers.
|
15
|
+
Default values also support local settings to allow override on local environment or deployment in production.
|
26
16
|
|
27
17
|
## Installation
|
28
18
|
|
@@ -39,22 +29,27 @@ gem 'consul_application_settings'
|
|
39
29
|
At the load of application:
|
40
30
|
```ruby
|
41
31
|
ConsulApplicationSettings.configure do |config|
|
42
|
-
# Specify path to the base settings YML. Default: 'config/
|
43
|
-
config.base_file_path = Rails.root.join('config/
|
44
|
-
# Specify path to the local settings YML, which overrides the base file. Default: 'config/
|
45
|
-
config.local_file_path = Rails.root.join('config/
|
46
|
-
# Specify whether
|
32
|
+
# Specify path to the base settings YML. Default: 'config/app_settings.yml'
|
33
|
+
config.base_file_path = Rails.root.join('config/app_settings.yml')
|
34
|
+
# Specify path to the local settings YML, which overrides the base file. Default: 'config/app_settings.local.yml'
|
35
|
+
config.local_file_path = Rails.root.join('config/app_settings.local.yml')
|
36
|
+
# Specify whether exception should be thrown on Consul connection errors. Default: false
|
47
37
|
config.disable_consul_connection_errors = true
|
48
38
|
# Specify setting providers. Default: [ConsulApplicationSettings::Providers::ConsulPreloaded, ConsulApplicationSettings::Providers::LocalStorage]
|
49
39
|
config.settings_providers = [
|
50
40
|
ConsulApplicationSettings::Providers::Consul,
|
51
41
|
ConsulApplicationSettings::Providers::LocalStorage
|
52
42
|
]
|
43
|
+
# Specify how values will be additionally resolved. Default: [ConsulApplicationSettings::Resolvers::Env]
|
44
|
+
config.value_resolvers = [
|
45
|
+
ConsulApplicationSettings::Resolvers::Erb,
|
46
|
+
ConsulApplicationSettings::Resolvers::Env,
|
47
|
+
]
|
53
48
|
end
|
54
49
|
|
55
|
-
APP_SETTINGS = ConsulApplicationSettings.load
|
56
50
|
# Specify path to settings both in YML files and Consul
|
57
|
-
AUTH_SETTIGNS = ConsulApplicationSettings.load('
|
51
|
+
AUTH_SETTIGNS = ConsulApplicationSettings.load('my_cool_app')
|
52
|
+
# Load at root without any prefix: APP_SETTINGS = ConsulApplicationSettings.load
|
58
53
|
```
|
59
54
|
|
60
55
|
**NOTE** For rails you can add this code to custom initializer `console_application_settings.rb` in `app/config/initializers`
|
@@ -65,35 +60,32 @@ AUTH_SETTIGNS = ConsulApplicationSettings.load('authentication')
|
|
65
60
|
|
66
61
|
Assuming your defaults file in repository `config/application_settings.yml` looks like:
|
67
62
|
```yaml
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
webhook_url: 'https://hooks.slack.com/services/XXXXXX/XXXXX/XXXXXXX'
|
63
|
+
my_cool_app:
|
64
|
+
app_name: 'MyCoolApp'
|
65
|
+
hostname: 'http://localhost:3001'
|
66
|
+
|
67
|
+
integrations:
|
68
|
+
database:
|
69
|
+
domain: localhost
|
70
|
+
user: app
|
71
|
+
password: password1234
|
72
|
+
slack:
|
73
|
+
enabled: false
|
74
|
+
webhook_url: 'https://hooks.slack.com/services/XXXXXX/XXXXX/XXXXXXX'
|
81
75
|
```
|
82
76
|
|
83
77
|
And consul has following settings
|
84
78
|
```json
|
85
79
|
{
|
86
|
-
"
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
"
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
"
|
95
|
-
"enabled": "true"
|
96
|
-
}
|
80
|
+
"my_cool_app": {
|
81
|
+
"hostname": "https://mycoolapp.com",
|
82
|
+
"integrations": {
|
83
|
+
"database": {
|
84
|
+
"domain": "194.78.92.19",
|
85
|
+
"password": "*************"
|
86
|
+
},
|
87
|
+
"slack": {
|
88
|
+
"enabled": "true"
|
97
89
|
}
|
98
90
|
}
|
99
91
|
}
|
@@ -120,10 +112,60 @@ gem provides interface to avoid duplicating absolute path
|
|
120
112
|
|
121
113
|
```ruby
|
122
114
|
# You can load subsettings from root object
|
123
|
-
db_settings = APP_SETTINGS.load('integrations/database')
|
115
|
+
db_settings = APP_SETTINGS.load('integrations/database') # ConsulApplicationSettings::Reader
|
124
116
|
db_settings.get(:domain) # "194.78.92.19"
|
125
117
|
db_settings['user'] # "app"
|
126
|
-
|
118
|
+
|
119
|
+
#if you try to get subsettings via get - error is raised
|
120
|
+
APP_SETTINGS.get('integrations/database') # raise ConsulApplicationSettings::Error
|
121
|
+
```
|
122
|
+
|
123
|
+
## Advanced Configurations
|
124
|
+
|
125
|
+
### Setting Providers
|
126
|
+
Providers controls how and in which order settings are retrieved.
|
127
|
+
When application asks for specific setting - gem retrieves them from every provider in order of configuration
|
128
|
+
until one returns not nil value.
|
129
|
+
|
130
|
+
Default order for providers is:
|
131
|
+
1. `ConsulApplicationSettings::Providers::ConsulPreloaded`
|
132
|
+
2. `ConsulApplicationSettings::Providers::LocalStorage`
|
133
|
+
|
134
|
+
List of built in providers:
|
135
|
+
- `ConsulApplicationSettings::Providers::ConsulPreloaded` - Retrieves all settings from consul on every `.load`
|
136
|
+
- `ConsulApplicationSettings::Providers::Consul` - Retrieves setting every time `.get` method is called
|
137
|
+
- `ConsulApplicationSettings::Providers::LocalStorage` - Retrieves all settings from local files on every `.load`
|
138
|
+
|
139
|
+
Custom provider can be added as long as it support following interface:
|
140
|
+
```ruby
|
141
|
+
class CustomProvider
|
142
|
+
#constructor
|
143
|
+
def initialize(base_path, config)
|
144
|
+
end
|
145
|
+
|
146
|
+
# get value by `base_path + '/' + path`
|
147
|
+
def get(path)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
```
|
151
|
+
|
152
|
+
### Resolvers
|
153
|
+
Once value is retrieved - it will be additionally processed by resolvers.
|
154
|
+
This allows for additional flexibility like getting values from external sources.
|
155
|
+
While every resolver can be implemented in a form of a provider - one will be limited by the structure of settings,
|
156
|
+
while other system might not be compatible with this.
|
157
|
+
|
158
|
+
When value is retrieved - gem finds **first** provider that can resolve value and resolves it.
|
159
|
+
Resolved value is returned to application.
|
160
|
+
|
161
|
+
Default list of resolvers:
|
162
|
+
- `ConsulApplicationSettings::Resolvers::Env`
|
163
|
+
|
164
|
+
List of built in resolvers
|
165
|
+
- `ConsulApplicationSettings::Resolvers::Env` - resolves any value by looking up environment variable.
|
166
|
+
Matching any value that starts with `env://`. Value like `env://TEST_URL` will be resolved as `ENV['TEST_URL']`
|
167
|
+
- `ConsulApplicationSettings::Resolvers::Erb` - resolves value by rendering it via ERB.
|
168
|
+
Matching any value that contains `<%` and `%>` in it. Value like `<%= 2 + 2 %>` will be resolved as `4`
|
127
169
|
|
128
170
|
### Gem Configuration
|
129
171
|
You can configure gem with block:
|
@@ -145,18 +187,17 @@ All Gem configurations
|
|
145
187
|
| local_file_path | no | 'config/application_settings.local.yml' | String | Path to the file with local settings overriding the base settings |
|
146
188
|
| disable_consul_connection_errors | no | true | Boolean | Do not raise exception when consul is not available (useful for development) |
|
147
189
|
| settings_providers | no | Array(Provider) | Array | Specify custom setting provider lists |
|
148
|
-
|
149
|
-
### Performance vs Consistency
|
150
|
-
To be defined in future iterations on Consul Providers
|
190
|
+
| value_resolvers | no | Array(Resolver) | Array | Specify custom value resolvers lists |
|
151
191
|
|
152
192
|
## Development
|
153
193
|
|
154
|
-
1. [Install Consul](https://www.consul.io/docs/install/index.html)
|
155
194
|
1. Run `bin/setup` to install dependencies
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
195
|
+
2. Run `docker-compose up` to spin up dependencies (Consul)
|
196
|
+
3. Run tests `rspec`
|
197
|
+
4. Add new test
|
198
|
+
5. Add new code
|
199
|
+
6. Go to step 3
|
200
|
+
7. Create PR
|
160
201
|
|
161
202
|
## Contributing
|
162
203
|
|
data/bin/benchmark
CHANGED
@@ -29,13 +29,15 @@ Gem::Specification.new do |spec|
|
|
29
29
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
30
30
|
spec.require_paths = ['lib']
|
31
31
|
|
32
|
+
spec.add_dependency 'deep_merge', '~> 1.2'
|
32
33
|
spec.add_dependency 'diplomat', '~> 2.5.1'
|
33
34
|
|
34
35
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
36
|
+
spec.add_development_dependency 'codecov', '~> 0.4'
|
35
37
|
spec.add_development_dependency 'rake', '~> 13.0'
|
36
38
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
37
39
|
spec.add_development_dependency 'rubocop', '~> 0.66'
|
38
40
|
spec.add_development_dependency 'rubocop-rspec', '~> 1.32.0'
|
39
41
|
spec.add_development_dependency 'simplecov', '~> 0.16'
|
40
|
-
spec.add_development_dependency '
|
42
|
+
spec.add_development_dependency 'vault', '~> 0.16'
|
41
43
|
end
|
data/docker-compose.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# dependencies needed for development environment
|
2
|
+
version: '3'
|
3
|
+
services:
|
4
|
+
vault:
|
5
|
+
image: vault
|
6
|
+
ports:
|
7
|
+
- "8200:8200"
|
8
|
+
cap_add:
|
9
|
+
- IPC_LOCK
|
10
|
+
environment:
|
11
|
+
VAULT_DEV_ROOT_TOKEN_ID: root_token
|
12
|
+
consul:
|
13
|
+
image: consul
|
14
|
+
ports:
|
15
|
+
- "8500:8500"
|
16
|
+
environment:
|
17
|
+
CONSUL_BIND_INTERFACE: eth0
|
@@ -6,14 +6,19 @@ module ConsulApplicationSettings
|
|
6
6
|
DEFAULT_PROVIDERS = [
|
7
7
|
ConsulApplicationSettings::Providers::ConsulPreloaded,
|
8
8
|
ConsulApplicationSettings::Providers::LocalStorage
|
9
|
-
]
|
10
|
-
|
9
|
+
].freeze
|
10
|
+
DEFAULT_RESOLVERS = [
|
11
|
+
ConsulApplicationSettings::Resolvers::Env
|
12
|
+
].freeze
|
13
|
+
attr_accessor :base_file_path, :local_file_path, :disable_consul_connection_errors,
|
14
|
+
:settings_providers, :value_resolvers
|
11
15
|
|
12
16
|
def initialize
|
13
17
|
@base_file_path = DEFAULT_BASE_FILE_PATH
|
14
18
|
@local_file_path = DEFAULT_LOCAL_FILE_PATH
|
15
19
|
@disable_consul_connection_errors = true
|
16
20
|
@settings_providers = DEFAULT_PROVIDERS
|
21
|
+
@value_resolvers = DEFAULT_RESOLVERS
|
17
22
|
end
|
18
23
|
end
|
19
24
|
end
|
@@ -3,10 +3,11 @@ module ConsulApplicationSettings
|
|
3
3
|
# Abstract class with basic functionality
|
4
4
|
class Abstract
|
5
5
|
def initialize(base_path, config)
|
6
|
-
@base_path
|
6
|
+
@base_path = base_path
|
7
|
+
@config = config
|
7
8
|
end
|
8
9
|
|
9
|
-
def get(
|
10
|
+
def get(_path)
|
10
11
|
raise NotImplementedError
|
11
12
|
end
|
12
13
|
|
@@ -22,4 +23,4 @@ module ConsulApplicationSettings
|
|
22
23
|
end
|
23
24
|
end
|
24
25
|
end
|
25
|
-
end
|
26
|
+
end
|
@@ -7,18 +7,26 @@ module ConsulApplicationSettings
|
|
7
7
|
def get(path)
|
8
8
|
full_path = absolute_key_path(path)
|
9
9
|
value = get_from_consul(full_path)
|
10
|
+
value = resolve_tree_response(value, full_path)
|
10
11
|
ConsulApplicationSettings::Utils.cast_consul_value(value)
|
11
12
|
end
|
12
13
|
|
13
14
|
private
|
14
15
|
|
15
16
|
def get_from_consul(path)
|
16
|
-
Diplomat::Kv.get(path,
|
17
|
+
Diplomat::Kv.get(path, recurse: true)
|
17
18
|
rescue Diplomat::KeyNotFound
|
18
|
-
|
19
|
+
nil
|
19
20
|
rescue SystemCallError, Faraday::ConnectionFailed, Diplomat::PathNotFound => e
|
20
21
|
raise e unless @config.disable_consul_connection_errors
|
21
22
|
end
|
23
|
+
|
24
|
+
def resolve_tree_response(value, full_path)
|
25
|
+
return value unless value.is_a?(Array)
|
26
|
+
|
27
|
+
value.each { |item| item[:key] = item[:key].delete_prefix("#{full_path}/") }
|
28
|
+
value
|
29
|
+
end
|
22
30
|
end
|
23
31
|
end
|
24
32
|
end
|
@@ -4,24 +4,42 @@ module ConsulApplicationSettings
|
|
4
4
|
class ConsulPreloaded < Abstract
|
5
5
|
def initialize(base_path, config)
|
6
6
|
super
|
7
|
-
@data =
|
7
|
+
@data = read_all_from_consul
|
8
8
|
end
|
9
9
|
|
10
10
|
def get(path)
|
11
11
|
value = get_value_from_hash(absolute_key_path(path), @data)
|
12
|
+
value = resolve_tree_response(value)
|
12
13
|
ConsulApplicationSettings::Utils.cast_consul_value(value)
|
13
14
|
end
|
14
15
|
|
15
16
|
protected
|
16
17
|
|
17
|
-
def
|
18
|
-
Diplomat::Kv.get_all(@base_path,
|
18
|
+
def read_all_from_consul
|
19
|
+
Diplomat::Kv.get_all(@base_path, convert_to_hash: true)
|
19
20
|
rescue Diplomat::KeyNotFound
|
20
21
|
{}
|
21
22
|
rescue SystemCallError, Faraday::ConnectionFailed, Diplomat::PathNotFound => e
|
22
23
|
raise e unless @config.disable_consul_connection_errors
|
24
|
+
|
23
25
|
{}
|
24
26
|
end
|
27
|
+
|
28
|
+
def resolve_tree_response(value)
|
29
|
+
return value unless value.is_a?(Hash)
|
30
|
+
|
31
|
+
deep_cast(value)
|
32
|
+
end
|
33
|
+
|
34
|
+
def deep_cast(hash)
|
35
|
+
hash.each_pair do |k, v|
|
36
|
+
if v.is_a?(Hash)
|
37
|
+
deep_cast(v)
|
38
|
+
else
|
39
|
+
hash[k] = ConsulApplicationSettings::Utils.cast_consul_value(v)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
25
43
|
end
|
26
44
|
end
|
27
|
-
end
|
45
|
+
end
|
@@ -19,7 +19,7 @@ module ConsulApplicationSettings
|
|
19
19
|
base_yml = read_yml(base_file_path)
|
20
20
|
local_yml = read_yml(local_file_path)
|
21
21
|
DeepMerge.deep_merge!(local_yml, base_yml, preserve_unmergeables: false, overwrite_arrays: true,
|
22
|
-
|
22
|
+
merge_nil_values: true)
|
23
23
|
end
|
24
24
|
|
25
25
|
def base_file_path
|
@@ -1,18 +1,16 @@
|
|
1
1
|
module ConsulApplicationSettings
|
2
|
-
#
|
2
|
+
# Orchestrates fetching values from provider and resolving them
|
3
3
|
class Reader
|
4
4
|
def initialize(base_path, config)
|
5
5
|
@base_path = base_path
|
6
6
|
@config = config
|
7
7
|
@providers = config.settings_providers.map { |provider| provider.new(base_path, config) }
|
8
|
+
@resolvers = config.value_resolvers.map(&:new)
|
8
9
|
end
|
9
10
|
|
10
11
|
def get(path)
|
11
|
-
|
12
|
-
|
13
|
-
return value unless value.nil?
|
14
|
-
end
|
15
|
-
nil
|
12
|
+
value = fetch_value(path)
|
13
|
+
resolve_value(value, path)
|
16
14
|
end
|
17
15
|
|
18
16
|
alias [] get
|
@@ -21,5 +19,32 @@ module ConsulApplicationSettings
|
|
21
19
|
new_path = ConsulApplicationSettings::Utils.generate_path(@base_path, sub_path)
|
22
20
|
self.class.new(new_path, @config)
|
23
21
|
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def check_deep_structure(value, path)
|
26
|
+
return unless value.is_a?(Hash)
|
27
|
+
|
28
|
+
message = "Getting value of complex object at path: '#{path}'. Use #load method to get new scoped instance"
|
29
|
+
raise ConsulApplicationSettings::Error, message if value.is_a?(Hash)
|
30
|
+
end
|
31
|
+
|
32
|
+
def fetch_value(path)
|
33
|
+
@providers.each do |provider|
|
34
|
+
value = provider.get(path)
|
35
|
+
check_deep_structure(value, path)
|
36
|
+
return value unless value.nil?
|
37
|
+
end
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def resolve_value(value, path)
|
42
|
+
resolver = @resolvers.detect { |r| r.resolvable?(value, path) }
|
43
|
+
if resolver
|
44
|
+
resolver.resolve(value, path)
|
45
|
+
else
|
46
|
+
value
|
47
|
+
end
|
48
|
+
end
|
24
49
|
end
|
25
50
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ConsulApplicationSettings
|
2
|
+
module Resolvers
|
3
|
+
# Resolve values in environment
|
4
|
+
class Env
|
5
|
+
IDENTIFIER = 'env://'.freeze
|
6
|
+
|
7
|
+
def resolvable?(value, _path)
|
8
|
+
return unless value.respond_to?(:start_with?)
|
9
|
+
|
10
|
+
value.start_with?(IDENTIFIER)
|
11
|
+
end
|
12
|
+
|
13
|
+
def resolve(value, _path)
|
14
|
+
env_path = value.to_s.delete_prefix(IDENTIFIER)
|
15
|
+
ENV[env_path]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ConsulApplicationSettings
|
2
|
+
module Resolvers
|
3
|
+
# Run values through ERB
|
4
|
+
class Erb
|
5
|
+
IDENTIFIER = /(<%).*(%>)/.freeze
|
6
|
+
|
7
|
+
def resolvable?(value, _path)
|
8
|
+
return unless value.is_a?(String)
|
9
|
+
|
10
|
+
IDENTIFIER.match?(value)
|
11
|
+
end
|
12
|
+
|
13
|
+
def resolve(value, _path)
|
14
|
+
ERB.new(value.to_s).result
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ConsulApplicationSettings
|
2
|
+
module Resolvers
|
3
|
+
# Resolve values using vault
|
4
|
+
class Vault
|
5
|
+
IDENTIFIER = 'vault://'.freeze
|
6
|
+
|
7
|
+
def resolvable?(value, _path)
|
8
|
+
return unless value.respond_to?(:start_with?)
|
9
|
+
|
10
|
+
value.start_with?(IDENTIFIER)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Expect value in format `vault://mount/path/to/secret?attribute_name`
|
14
|
+
def resolve(value, _path)
|
15
|
+
value = value.delete_prefix(IDENTIFIER)
|
16
|
+
mount, secret = value.split('/', 2)
|
17
|
+
secret, attribute = secret.split('?')
|
18
|
+
attribute ||= 'value'
|
19
|
+
secret = ::Vault.kv(mount).read(secret)
|
20
|
+
secret && secret.data[attribute.to_sym]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -9,6 +9,8 @@ module ConsulApplicationSettings
|
|
9
9
|
return nil if value.nil?
|
10
10
|
return false if value == 'false'
|
11
11
|
return true if value == 'true'
|
12
|
+
return value if value.is_a?(Hash)
|
13
|
+
return convert_to_hash(value) if value.is_a?(Array)
|
12
14
|
|
13
15
|
cast_complex_value(value)
|
14
16
|
end
|
@@ -34,6 +36,16 @@ module ConsulApplicationSettings
|
|
34
36
|
end
|
35
37
|
value.to_s
|
36
38
|
end
|
39
|
+
|
40
|
+
def convert_to_hash(data)
|
41
|
+
data_h = data.map do |item|
|
42
|
+
value = cast_consul_value(item[:value])
|
43
|
+
item[:key].split('/').reverse.reduce(value) { |h, v| { v => h } }
|
44
|
+
end
|
45
|
+
data_h.reduce({}) do |dest, source|
|
46
|
+
DeepMerge.deep_merge!(source, dest, preserve_unmergeables: true)
|
47
|
+
end
|
48
|
+
end
|
37
49
|
end
|
38
50
|
end
|
39
51
|
end
|
@@ -3,6 +3,9 @@ require 'consul_application_settings/providers/abstract'
|
|
3
3
|
require 'consul_application_settings/providers/consul'
|
4
4
|
require 'consul_application_settings/providers/consul_preloaded'
|
5
5
|
require 'consul_application_settings/providers/local_storage'
|
6
|
+
require 'consul_application_settings/resolvers/abstract'
|
7
|
+
require 'consul_application_settings/resolvers/env'
|
8
|
+
require 'consul_application_settings/resolvers/erb'
|
6
9
|
require 'consul_application_settings/configuration'
|
7
10
|
require 'consul_application_settings/reader'
|
8
11
|
require 'consul_application_settings/utils'
|
@@ -13,7 +16,6 @@ module ConsulApplicationSettings
|
|
13
16
|
|
14
17
|
class << self
|
15
18
|
attr_accessor :config
|
16
|
-
attr_accessor :defaults
|
17
19
|
end
|
18
20
|
|
19
21
|
self.config ||= ConsulApplicationSettings::Configuration.new
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: consul_application_settings
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0.pre.alpha
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Volodymyr Mykhailyk
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-12-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: deep_merge
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.2'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: diplomat
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -38,6 +52,20 @@ dependencies:
|
|
38
52
|
- - "~>"
|
39
53
|
- !ruby/object:Gem::Version
|
40
54
|
version: '2.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: codecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.4'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.4'
|
41
69
|
- !ruby/object:Gem::Dependency
|
42
70
|
name: rake
|
43
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -109,19 +137,19 @@ dependencies:
|
|
109
137
|
- !ruby/object:Gem::Version
|
110
138
|
version: '0.16'
|
111
139
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
140
|
+
name: vault
|
113
141
|
requirement: !ruby/object:Gem::Requirement
|
114
142
|
requirements:
|
115
143
|
- - "~>"
|
116
144
|
- !ruby/object:Gem::Version
|
117
|
-
version: '0.
|
145
|
+
version: '0.16'
|
118
146
|
type: :development
|
119
147
|
prerelease: false
|
120
148
|
version_requirements: !ruby/object:Gem::Requirement
|
121
149
|
requirements:
|
122
150
|
- - "~>"
|
123
151
|
- !ruby/object:Gem::Version
|
124
|
-
version: '0.
|
152
|
+
version: '0.16'
|
125
153
|
description: |-
|
126
154
|
Gem that simplifies usage of Consul (via Diplomat gem) to host application settings.
|
127
155
|
Gem provides defaults and utilities
|
@@ -148,6 +176,7 @@ files:
|
|
148
176
|
- bin/rspec
|
149
177
|
- bin/setup
|
150
178
|
- consul_application_settings.gemspec
|
179
|
+
- docker-compose.yml
|
151
180
|
- lib/consul_application_settings.rb
|
152
181
|
- lib/consul_application_settings/configuration.rb
|
153
182
|
- lib/consul_application_settings/providers/abstract.rb
|
@@ -155,6 +184,10 @@ files:
|
|
155
184
|
- lib/consul_application_settings/providers/consul_preloaded.rb
|
156
185
|
- lib/consul_application_settings/providers/local_storage.rb
|
157
186
|
- lib/consul_application_settings/reader.rb
|
187
|
+
- lib/consul_application_settings/resolvers/abstract.rb
|
188
|
+
- lib/consul_application_settings/resolvers/env.rb
|
189
|
+
- lib/consul_application_settings/resolvers/erb.rb
|
190
|
+
- lib/consul_application_settings/resolvers/vault.rb
|
158
191
|
- lib/consul_application_settings/utils.rb
|
159
192
|
- lib/consul_application_settings/version.rb
|
160
193
|
homepage: https://github.com/matic-insurance/consul_application_settings
|
@@ -175,9 +208,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
175
208
|
version: '0'
|
176
209
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
177
210
|
requirements:
|
178
|
-
- - "
|
211
|
+
- - ">"
|
179
212
|
- !ruby/object:Gem::Version
|
180
|
-
version:
|
213
|
+
version: 1.3.1
|
181
214
|
requirements: []
|
182
215
|
rubygems_version: 3.0.3.1
|
183
216
|
signing_key:
|