consul_application_settings 2.1.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8e6c1820af05af3bcc9adca21879296cbcfc8898e69ffa7136bac1d785ae1bb1
4
- data.tar.gz: 0e8b5c29bf2992a6d36e7a064b8887922b69ec5f820073157b53a21dc5905089
3
+ metadata.gz: fa6d5fd8ad5cf3b9df7fcbc3622a6efac55fc1bc3fcc81327ea061eab9a1ae77
4
+ data.tar.gz: 93434d8c607adf270f4acb503cc93bed92850b0fb366fb2d4513f679f39154e3
5
5
  SHA512:
6
- metadata.gz: 976fec73196a99d0a642d9f4d9243beb0a0925f44f140ff6e77091dac1919f36472b1467344e190da01b071803412d6ec612ccb31f8751a9d06d3f471a49fd1f
7
- data.tar.gz: e0d2e02025e94d2d5fc6ac4dd24a4073f9ac228969e67afbd9afb5cafa297c6a347dd68a53fac9a4ad6b4c2ce875b036372894b175e1bd36e6db138c1b31e9d9
6
+ metadata.gz: e86cddbca8a53cf608b72abb8dbe8c1aefdee558636f21cad45b6b3111ced6159f758175e4a377c55fbe2eba0ffab729b7fe427cf84ae43e77e25818a06593d1
7
+ data.tar.gz: 1ad1589204db6e0fa56ca42300f68c32a796a8c181b03c2aebde4104b66568c9100c2cec032b76ade78b811106228611717f92900f96fa451721be4fafebd240
data/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [3.0.0]
4
+ ### Breaking Changes
5
+ - Use Preloaded Consul Settings Provider by default
6
+ ### New features
7
+ - Configurable setting providers
8
+ - Preloaded Consul Settings Provider to prioritize performance over consistency
9
+ - Performance tests in spec
10
+ - Benchmarking script
11
+ ### Fixes
12
+ - Return nil instead of empty hash when reading missing setting from file
13
+ - Return nil instead of empty string when reading missing value from Consul
14
+ - Add missing load method on Settings Reader to create object with narrow scope
15
+
16
+ ## [2.1.1]
17
+ ### Changes
18
+ - Update Diplomat to latest version
19
+
20
+ ## [2.1.0]
21
+ ### Fixes
22
+ - Return nil for unknown keys
23
+
24
+ ## [2.0.0]
25
+ ### Breaking Changes
26
+ - Change default naming for setting files
27
+
28
+ ## [1.0.0]
29
+ ### Features
30
+ - Add support for second settings file (local settings for development)
31
+
32
+ ## [0.1.4]
33
+ ### Fixes
34
+ - Clone values before returning
35
+
3
36
  ## [0.1.3]
4
37
  ### Fixes
5
38
  - Add `Diplomat::PathNotFound` to the list of caught exceptions
@@ -22,7 +55,13 @@
22
55
  - Support deep settings search
23
56
  - Support nested configs
24
57
 
25
- [Unreleased]: https://github.com/matic-insurance/consul_application_settings/compare/0.1.3...HEAD
58
+ [Unreleased]: https://github.com/matic-insurance/consul_application_settings/compare/3.0.0...HEAD
59
+ [3.0.0]: https://github.com/matic-insurance/consul_application_settings/compare/2.0.0...3.0.0
60
+ [2.1.1]: https://github.com/matic-insurance/consul_application_settings/compare/2.1.0...2.1.1
61
+ [2.1.0]: https://github.com/matic-insurance/consul_application_settings/compare/2.0.0...2.1.0
62
+ [2.0.0]: https://github.com/matic-insurance/consul_application_settings/compare/1.0.0...2.0.0
63
+ [1.0.0]: https://github.com/matic-insurance/consul_application_settings/compare/0.1.4...1.0.0
64
+ [0.1.4]: https://github.com/matic-insurance/consul_application_settings/compare/0.1.3...0.1.4
26
65
  [0.1.3]: https://github.com/matic-insurance/consul_application_settings/compare/0.1.2...0.1.3
27
66
  [0.1.2]: https://github.com/matic-insurance/consul_application_settings/compare/0.1.1...0.1.2
28
67
  [0.1.1]: https://github.com/matic-insurance/consul_application_settings/compare/0.1.0...0.1.1
data/README.md CHANGED
@@ -45,6 +45,11 @@ ConsulApplicationSettings.configure do |config|
45
45
  config.local_file_path = Rails.root.join('config/my_settings.local.yml')
46
46
  # Specify whether exceprion should be thrown on Consul connection errors. Default: false
47
47
  config.disable_consul_connection_errors = true
48
+ # Specify setting providers. Default: [ConsulApplicationSettings::Providers::ConsulPreloaded, ConsulApplicationSettings::Providers::LocalStorage]
49
+ config.settings_providers = [
50
+ ConsulApplicationSettings::Providers::Consul,
51
+ ConsulApplicationSettings::Providers::LocalStorage
52
+ ]
48
53
  end
49
54
 
50
55
  APP_SETTINGS = ConsulApplicationSettings.load
@@ -139,6 +144,10 @@ All Gem configurations
139
144
  | base_file_path | no | 'config/application_settings.yml' | String | Path to the file with base settings |
140
145
  | local_file_path | no | 'config/application_settings.local.yml' | String | Path to the file with local settings overriding the base settings |
141
146
  | disable_consul_connection_errors | no | true | Boolean | Do not raise exception when consul is not available (useful for development) |
147
+ | 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
142
151
 
143
152
  ## Development
144
153
 
data/bin/benchmark ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'consul_application_settings'
5
+ require 'benchmark'
6
+
7
+ ITERATIONS = 1000
8
+
9
+ def kill_consul
10
+ `pgrep consul | xargs kill`
11
+ end
12
+
13
+ def start_consul
14
+ spawn('consul agent -dev -node machine > /dev/null 2>&1')
15
+ end
16
+
17
+ def application_settings(providers)
18
+ file = 'spec/fixtures/base_application_settings.yml'
19
+ ConsulApplicationSettings.configure do |config|
20
+ config.settings_providers = providers
21
+ end
22
+ ConsulApplicationSettings.load(file)
23
+ end
24
+
25
+ def benchmark_gem
26
+ Benchmark.bm(20) do |x|
27
+ x.report('Real Time Consul') do
28
+ ca = application_settings([
29
+ ConsulApplicationSettings::Providers::Consul,
30
+ ConsulApplicationSettings::Providers::LocalStorage
31
+ ])
32
+ ITERATIONS.times { ca.get('application/name') }
33
+ end
34
+
35
+ x.report('Preloaded Consul') do
36
+ ca = application_settings([
37
+ ConsulApplicationSettings::Providers::ConsulPreloaded,
38
+ ConsulApplicationSettings::Providers::LocalStorage
39
+ ])
40
+ ITERATIONS.times { ca.get('application/name') }
41
+ end
42
+
43
+ x.report('File Only') do
44
+ ca = application_settings([
45
+ ConsulApplicationSettings::Providers::ConsulPreloaded
46
+ ])
47
+ ITERATIONS.times { ca.get('application/name') }
48
+ end
49
+ end
50
+ end
51
+
52
+ puts '-' * 80
53
+ puts 'Benchmark without consul agent'
54
+ kill_consul
55
+ benchmark_gem
56
+
57
+
58
+ puts '-' * 80
59
+ puts 'Benchmark with consul agent running'
60
+ start_consul
61
+ benchmark_gem
62
+
63
+ kill_consul
@@ -1,8 +1,10 @@
1
1
  require 'consul_application_settings/version'
2
+ require 'consul_application_settings/providers/abstract'
3
+ require 'consul_application_settings/providers/consul'
4
+ require 'consul_application_settings/providers/consul_preloaded'
5
+ require 'consul_application_settings/providers/local_storage'
2
6
  require 'consul_application_settings/configuration'
3
- require 'consul_application_settings/consul_provider'
4
- require 'consul_application_settings/file_provider'
5
- require 'consul_application_settings/settings_provider'
7
+ require 'consul_application_settings/reader'
6
8
  require 'consul_application_settings/utils'
7
9
 
8
10
  # The gem provides possibility to load settings from Consul and automatically fall back to data stored in file system
@@ -21,6 +23,6 @@ module ConsulApplicationSettings
21
23
  end
22
24
 
23
25
  def self.load(path = '')
24
- SettingsProvider.new(path, config)
26
+ Reader.new(path, config)
25
27
  end
26
28
  end
@@ -3,12 +3,17 @@ module ConsulApplicationSettings
3
3
  class Configuration
4
4
  DEFAULT_BASE_FILE_PATH = 'config/app_settings.yml'.freeze
5
5
  DEFAULT_LOCAL_FILE_PATH = 'config/app_settings.local.yml'.freeze
6
- attr_accessor :base_file_path, :local_file_path, :disable_consul_connection_errors
6
+ DEFAULT_PROVIDERS = [
7
+ ConsulApplicationSettings::Providers::ConsulPreloaded,
8
+ ConsulApplicationSettings::Providers::LocalStorage
9
+ ]
10
+ attr_accessor :base_file_path, :local_file_path, :disable_consul_connection_errors, :settings_providers
7
11
 
8
12
  def initialize
9
13
  @base_file_path = DEFAULT_BASE_FILE_PATH
10
14
  @local_file_path = DEFAULT_LOCAL_FILE_PATH
11
15
  @disable_consul_connection_errors = true
16
+ @settings_providers = DEFAULT_PROVIDERS
12
17
  end
13
18
  end
14
19
  end
@@ -0,0 +1,25 @@
1
+ module ConsulApplicationSettings
2
+ module Providers
3
+ # Abstract class with basic functionality
4
+ class Abstract
5
+ def initialize(base_path, config)
6
+ @base_path, @config = base_path, config
7
+ end
8
+
9
+ def get(path)
10
+ raise NotImplementedError
11
+ end
12
+
13
+ protected
14
+
15
+ def absolute_key_path(path)
16
+ ConsulApplicationSettings::Utils.generate_path(@base_path, path)
17
+ end
18
+
19
+ def get_value_from_hash(path, data)
20
+ parts = ConsulApplicationSettings::Utils.decompose_path(path)
21
+ data.dig(*parts).clone
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,24 @@
1
+ require 'diplomat'
2
+
3
+ module ConsulApplicationSettings
4
+ module Providers
5
+ # Provides access to settings stored in Consul
6
+ class Consul < Abstract
7
+ def get(path)
8
+ full_path = absolute_key_path(path)
9
+ value = get_from_consul(full_path)
10
+ ConsulApplicationSettings::Utils.cast_consul_value(value)
11
+ end
12
+
13
+ private
14
+
15
+ def get_from_consul(path)
16
+ Diplomat::Kv.get(path, {})
17
+ rescue Diplomat::KeyNotFound
18
+ return nil
19
+ rescue SystemCallError, Faraday::ConnectionFailed, Diplomat::PathNotFound => e
20
+ raise e unless @config.disable_consul_connection_errors
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,26 @@
1
+ module ConsulApplicationSettings
2
+ module Providers
3
+ # Provides access to settings stored in Consul. Loads them once
4
+ class ConsulPreloaded < Abstract
5
+ def initialize(base_path, config)
6
+ super
7
+ @data = get_all_from_consul
8
+ end
9
+
10
+ def get(path)
11
+ value = get_value_from_hash(absolute_key_path(path), @data)
12
+ ConsulApplicationSettings::Utils.cast_consul_value(value)
13
+ end
14
+
15
+ protected
16
+
17
+ def get_all_from_consul
18
+ Diplomat::Kv.get_all(@base_path, { convert_to_hash: true })
19
+ rescue Diplomat::KeyNotFound
20
+ {}
21
+ rescue SystemCallError, Faraday::ConnectionFailed, Diplomat::PathNotFound => e
22
+ raise e unless @config.disable_consul_connection_errors
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,42 @@
1
+ require 'yaml'
2
+
3
+ module ConsulApplicationSettings
4
+ module Providers
5
+ # Provides access to settings stored in file system with support of base and local files
6
+ class LocalStorage < Abstract
7
+ def initialize(base_path, config)
8
+ super
9
+ @data = load
10
+ end
11
+
12
+ def get(path)
13
+ get_value_from_hash(absolute_key_path(path), @data)
14
+ end
15
+
16
+ private
17
+
18
+ def load
19
+ base_yml = read_yml(base_file_path)
20
+ local_yml = read_yml(local_file_path)
21
+ DeepMerge.deep_merge!(local_yml, base_yml, preserve_unmergeables: false, overwrite_arrays: true,
22
+ merge_nil_values: true)
23
+ end
24
+
25
+ def base_file_path
26
+ @config.base_file_path
27
+ end
28
+
29
+ def local_file_path
30
+ @config.local_file_path
31
+ end
32
+
33
+ def read_yml(path)
34
+ return {} unless File.exist?(path)
35
+
36
+ YAML.safe_load(IO.read(path))
37
+ rescue Psych::SyntaxError, Errno::ENOENT => e
38
+ raise ConsulApplicationSettings::Error, "Cannot read settings file at #{path}: #{e.message}"
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,25 @@
1
+ module ConsulApplicationSettings
2
+ # Provides access to settings stored in Consul or in file system
3
+ class Reader
4
+ def initialize(base_path, config)
5
+ @base_path = base_path
6
+ @config = config
7
+ @providers = config.settings_providers.map { |provider| provider.new(base_path, config) }
8
+ end
9
+
10
+ def get(path)
11
+ @providers.each do |provider|
12
+ value = provider.get(path)
13
+ return value unless value.nil?
14
+ end
15
+ nil
16
+ end
17
+
18
+ alias [] get
19
+
20
+ def load(sub_path)
21
+ new_path = ConsulApplicationSettings::Utils.generate_path(@base_path, sub_path)
22
+ self.class.new(new_path, @config)
23
+ end
24
+ end
25
+ end
@@ -6,6 +6,7 @@ module ConsulApplicationSettings
6
6
 
7
7
  class << self
8
8
  def cast_consul_value(value)
9
+ return nil if value.nil?
9
10
  return false if value == 'false'
10
11
  return true if value == 'true'
11
12
 
@@ -1,3 +1,3 @@
1
1
  module ConsulApplicationSettings
2
- VERSION = '2.1.1'.freeze
2
+ VERSION = '3.0.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: consul_application_settings
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 3.0.0
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-02-22 00:00:00.000000000 Z
11
+ date: 2021-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: diplomat
@@ -143,15 +143,18 @@ files:
143
143
  - LICENSE.txt
144
144
  - README.md
145
145
  - Rakefile
146
+ - bin/benchmark
146
147
  - bin/console
147
148
  - bin/rspec
148
149
  - bin/setup
149
150
  - consul_application_settings.gemspec
150
151
  - lib/consul_application_settings.rb
151
152
  - lib/consul_application_settings/configuration.rb
152
- - lib/consul_application_settings/consul_provider.rb
153
- - lib/consul_application_settings/file_provider.rb
154
- - lib/consul_application_settings/settings_provider.rb
153
+ - lib/consul_application_settings/providers/abstract.rb
154
+ - lib/consul_application_settings/providers/consul.rb
155
+ - lib/consul_application_settings/providers/consul_preloaded.rb
156
+ - lib/consul_application_settings/providers/local_storage.rb
157
+ - lib/consul_application_settings/reader.rb
155
158
  - lib/consul_application_settings/utils.rb
156
159
  - lib/consul_application_settings/version.rb
157
160
  homepage: https://github.com/matic-insurance/consul_application_settings
@@ -176,7 +179,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
176
179
  - !ruby/object:Gem::Version
177
180
  version: '0'
178
181
  requirements: []
179
- rubygems_version: 3.0.3
182
+ rubygems_version: 3.0.3.1
180
183
  signing_key:
181
184
  specification_version: 4
182
185
  summary: Application settings via Consul with yaml defaults
@@ -1,33 +0,0 @@
1
- require 'diplomat'
2
-
3
- module ConsulApplicationSettings
4
- # Provides access to settings stored in Consul
5
- class ConsulProvider
6
- def initialize(base_path, config)
7
- @base_path = base_path
8
- @config = config
9
- end
10
-
11
- def get(path)
12
- value = fetch_value(path)
13
- ConsulApplicationSettings::Utils.cast_consul_value(value)
14
- end
15
-
16
- private
17
-
18
- def fetch_value(path)
19
- full_path = generate_full_path(path)
20
- Diplomat::Kv.get(full_path, {}, :return)
21
- rescue SystemCallError, Faraday::ConnectionFailed, Diplomat::PathNotFound => e
22
- raise e unless disable_consul_connection_errors?
23
- end
24
-
25
- def generate_full_path(path)
26
- ConsulApplicationSettings::Utils.generate_path(@base_path, path)
27
- end
28
-
29
- def disable_consul_connection_errors?
30
- @config.disable_consul_connection_errors
31
- end
32
- end
33
- end
@@ -1,54 +0,0 @@
1
- require 'yaml'
2
-
3
- module ConsulApplicationSettings
4
- # Provides access to settings stored in file system with support of base and local files
5
- class FileProvider
6
- def initialize(base_path, config)
7
- @base_path = base_path
8
- @config = config
9
- load
10
- end
11
-
12
- def get(path)
13
- read_path(path).clone
14
- end
15
-
16
- private
17
-
18
- def load
19
- base_yml = read_yml(base_file_path)
20
- local_yml = read_yml(local_file_path)
21
- @data = DeepMerge.deep_merge!(local_yml, base_yml, preserve_unmergeables: false, overwrite_arrays: true,
22
- merge_nil_values: true)
23
- end
24
-
25
- def base_file_path
26
- @config.base_file_path
27
- end
28
-
29
- def local_file_path
30
- @config.local_file_path
31
- end
32
-
33
- def read_yml(path)
34
- return {} unless File.exist?(path)
35
-
36
- YAML.safe_load(IO.read(path))
37
- rescue Psych::SyntaxError, Errno::ENOENT => e
38
- raise ConsulApplicationSettings::Error, "Cannot read settings file at #{path}: #{e.message}"
39
- end
40
-
41
- def read_path(path)
42
- full_path = ConsulApplicationSettings::Utils.generate_path(@base_path, path)
43
- parts = ConsulApplicationSettings::Utils.decompose_path(full_path)
44
- parts.reduce(@data, &method(:read_value))
45
- end
46
-
47
- def read_value(hash, key)
48
- raise ConsulApplicationSettings::Error, 'reading arrays not implemented' if hash.is_a? Array
49
- return {} if hash.nil?
50
-
51
- hash.fetch(key.to_s, nil)
52
- end
53
- end
54
- end
@@ -1,16 +0,0 @@
1
- module ConsulApplicationSettings
2
- # Provides access to settings stored in Consul or in file system
3
- class SettingsProvider
4
- def initialize(base_path, config)
5
- @consul_provider = ConsulProvider.new(base_path, config)
6
- @file_provider = FileProvider.new(base_path, config)
7
- end
8
-
9
- def get(path)
10
- consul_value = @consul_provider.get(path)
11
- !consul_value.nil? && consul_value != '' ? consul_value : @file_provider.get(path)
12
- end
13
-
14
- alias [] get
15
- end
16
- end