secret_config 0.10.2 → 1.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: 4dc04a5e5fea8efc7195b797bb23347e6a87eaa9a5b0eef6e8fa2e8e1882cdc1
4
- data.tar.gz: 287576509e9a80f0407771d53409efd00b28a2ce59e880719e1722a78eb85039
3
+ metadata.gz: f6aad3d7382cdcfce816fb2f045f134cfd8235df19ff66eb35e50030141cd864
4
+ data.tar.gz: c8538b3208265fa99ea8d6cd26b1e39da4ea26ed8919af56c3e2658811690b12
5
5
  SHA512:
6
- metadata.gz: 2c40c7e1f92ef57be28cb714e3713169b4950abc2cc115d7dd73ea16e2e4aaf748911336bfba01c5d244ae4363f3f52d6676f240c3bdb365f6f7805684459610
7
- data.tar.gz: 860ad65cc9ab5ffe34ef5506c69158d3c5107e8f8b5c1e44a09011da11d018f3c1ed23a73f66e98cf4064955c071cd7cc89dcaee780dfc1c43e2ec0c2575a469
6
+ metadata.gz: 40c6e537b1098a0f977c73ffce4ddada21dd415053789861a4ab5489118cd86b912d6a4444250e7b5eefe5997079b36a0f12d127e743572958fe816afaf85dce
7
+ data.tar.gz: 59423883acb034d91392d79e512513a9d058e5bff590fe16b8e9385c76f6371bfe491483f2c273d9f70efc73abaae6e06aa177be94a87b37a48db85de9ab9b15
data/README.md CHANGED
@@ -1,15 +1,13 @@
1
1
  # Secret Config
2
- [![Gem Version](https://img.shields.io/gem/v/secret_config.svg)](https://rubygems.org/gems/secret_config) [![Build Status](https://travis-ci.org/rocketjob/secret_config.svg?branch=master)](https://travis-ci.org/rocketjob/secret_config) [![License](https://img.shields.io/badge/license-Apache%202.0-brightgreen.svg)](http://opensource.org/licenses/Apache-2.0) ![](https://img.shields.io/badge/status-Production%20Ready-blue.svg) [![Gitter chat](https://img.shields.io/badge/IRC%20(gitter)-Support-brightgreen.svg)](https://gitter.im/rocketjob/support)
2
+ [![Gem Version](https://img.shields.io/gem/v/secret_config.svg)](https://rubygems.org/gems/secret_config) [![Build Status](https://github.com/rocketjob/secret_config/workflows/build/badge.svg)](https://github.com/rocketjob/secret_config/actions?query=workflow%3Abuild) [![License](https://img.shields.io/badge/license-Apache%202.0-brightgreen.svg)](http://opensource.org/licenses/Apache-2.0) ![](https://img.shields.io/badge/status-Production%20Ready-blue.svg) [![Gitter chat](https://img.shields.io/badge/IRC%20(gitter)-Support-brightgreen.svg)](https://gitter.im/rocketjob/support)
3
3
 
4
4
  Centralized Configuration and Secrets Management for Ruby and Rails applications.
5
5
 
6
6
  Securely store configuration information centrally, supporting multiple tenants of the same application.
7
7
 
8
- Checkout https://config.rocketjob.io/
9
-
10
8
  ## Documentation
11
9
 
12
- * [Guide](https://config.rocketjob.io/)
10
+ * [Secret Config](https://config.rocketjob.io/)
13
11
 
14
12
  ## Support
15
13
 
@@ -359,16 +359,16 @@ module SecretConfig
359
359
  # Ignore filtered values
360
360
  if (value != target[key].to_s) && (value != FILTERED)
361
361
  puts "#{Colors::KEY}#{key}:"
362
- puts "#{Colors::REMOVE}#{prefix_lines("- ", target[key])}"
363
- puts "#{Colors::ADD}#{prefix_lines("+ ", source[key])}#{Colors::CLEAR}\n\n"
362
+ puts "#{Colors::REMOVE}#{prefix_lines('- ', target[key])}"
363
+ puts "#{Colors::ADD}#{prefix_lines('+ ', source[key])}#{Colors::CLEAR}\n\n"
364
364
  end
365
365
  else
366
366
  puts "#{Colors::KEY}#{key}:"
367
- puts "#{Colors::REMOVE}#{prefix_lines("- ", target[key])}\n\n"
367
+ puts "#{Colors::REMOVE}#{prefix_lines('- ', target[key])}\n\n"
368
368
  end
369
369
  elsif source.key?(key)
370
370
  puts "#{Colors::KEY}#{key}:"
371
- puts "#{Colors::ADD}#{prefix_lines("+ ", source[key])}#{Colors::CLEAR}\n\n"
371
+ puts "#{Colors::ADD}#{prefix_lines('+ ', source[key])}#{Colors::CLEAR}\n\n"
372
372
  end
373
373
  end
374
374
  end
@@ -5,31 +5,45 @@ module SecretConfig
5
5
  def_delegator :registry, :refresh!
6
6
 
7
7
  def initialize(path, registry)
8
+ raise(ArgumentError, "path cannot be nil") if path.nil?
9
+
8
10
  @path = path
9
11
  @registry = registry
10
12
  end
11
13
 
12
14
  def fetch(sub_path, **options)
15
+ raise(ArgumentError, "sub_path cannot be nil") if sub_path.nil?
16
+
13
17
  registry.fetch(join_path(sub_path), **options)
14
18
  end
15
19
 
16
20
  def [](sub_path)
21
+ raise(ArgumentError, "sub_path cannot be nil") if sub_path.nil?
22
+
17
23
  registry[join_path(sub_path)]
18
24
  end
19
25
 
20
26
  def []=(sub_path, value)
27
+ raise(ArgumentError, "sub_path cannot be nil") if sub_path.nil?
28
+
21
29
  registry[join_path(sub_path)] = value
22
30
  end
23
31
 
24
32
  def key?(sub_path)
33
+ raise(ArgumentError, "sub_path cannot be nil") if sub_path.nil?
34
+
25
35
  registry.key?(join_path(sub_path))
26
36
  end
27
37
 
28
38
  def set(sub_path, value)
39
+ raise(ArgumentError, "sub_path cannot be nil") if sub_path.nil?
40
+
29
41
  registry.set(join_path(sub_path), value)
30
42
  end
31
43
 
32
44
  def delete(sub_path)
45
+ raise(ArgumentError, "sub_path cannot be nil") if sub_path.nil?
46
+
33
47
  registry.delete(join_path(sub_path))
34
48
  end
35
49
 
@@ -5,6 +5,9 @@ module SecretConfig
5
5
  class MissingMandatoryKey < Error
6
6
  end
7
7
 
8
+ class MissingEnvironmentVariable < Error
9
+ end
10
+
8
11
  class UndefinedRootError < Error
9
12
  end
10
13
 
@@ -38,11 +38,11 @@ module SecretConfig
38
38
  # - Imports cannot reference other imports at this time.
39
39
  def apply_imports
40
40
  tree.keys.each do |key|
41
- next unless (key =~ /\/__import__\Z/) || (key == "__import__")
41
+ next unless (key =~ %r{/__import__\Z}) || (key == "__import__")
42
42
 
43
43
  import_key = tree.delete(key)
44
- key, _ = ::File.split(key)
45
- key = nil if key == "."
44
+ key, = ::File.split(key)
45
+ key = nil if key == "."
46
46
 
47
47
  # binding.irb
48
48
 
@@ -51,7 +51,7 @@ module SecretConfig
51
51
 
52
52
  if relative_key?(import_key)
53
53
  tree.keys.each do |current_key|
54
- match = current_key.match(/\A#{import_key}\/(.*)/)
54
+ match = current_key.match(%r{\A#{import_key}/(.*)})
55
55
  next unless match
56
56
 
57
57
  imported_key = key.nil? ? match[1] : ::File.join(key, match[1])
@@ -71,6 +71,5 @@ module SecretConfig
71
71
  def relative_key?(key)
72
72
  !key.start_with?("/")
73
73
  end
74
-
75
74
  end
76
75
  end
@@ -23,19 +23,25 @@ module SecretConfig
23
23
  end
24
24
 
25
25
  # Returns the value or `nil` if not found
26
- def fetch(key)
26
+ def fetch(_key)
27
27
  values = fetch_path(path)
28
- value.is_a?(Hash) ? nil : value
28
+ values.is_a?(Hash) ? nil : values
29
29
  end
30
30
 
31
31
  private
32
32
 
33
33
  def fetch_path(path)
34
- config = YAML.load(ERB.new(::File.new(file_name).read).result)
34
+ config = load_yaml(ERB.new(::File.new(file_name).read).result)
35
35
 
36
36
  paths = path.sub(%r{\A/*}, "").sub(%r{/*\Z}, "").split("/")
37
37
  config.dig(*paths)
38
38
  end
39
+
40
+ def load_yaml(src)
41
+ return YAML.safe_load(src, permitted_classes: [Symbol], aliases: true) if Psych::VERSION > "4.0"
42
+
43
+ YAML.load(src)
44
+ end
39
45
  end
40
46
  end
41
47
  end
@@ -98,7 +98,7 @@ module SecretConfig
98
98
  end
99
99
 
100
100
  # Remove keys deleted from the central registry.
101
- (existing_keys - updated_keys).each { |key| provider.delete(key) }
101
+ (existing_keys - updated_keys).each { |key| cache.delete(key) }
102
102
 
103
103
  true
104
104
  end
@@ -154,7 +154,7 @@ module SecretConfig
154
154
  def convert_type(type, value)
155
155
  case type
156
156
  when :string
157
- value.to_s
157
+ value.nil? ? nil : value.to_s
158
158
  when :integer
159
159
  value.to_i
160
160
  when :float
@@ -4,16 +4,22 @@ require "securerandom"
4
4
  # * SecretConfig Interpolations
5
5
  #
6
6
  # Expanding values inline for date, time, hostname, pid and random values.
7
- # ${date} # Current date in the format of "%Y%m%d" (CCYYMMDD)
8
- # ${date:format} # Current date in the supplied format. See strftime
9
- # ${time} # Current date and time down to ms in the format of "%Y%m%d%Y%H%M%S%L" (CCYYMMDDHHMMSSmmm)
10
- # ${time:format} # Current date and time in the supplied format. See strftime
11
- # ${env:name} # Extract value from the named environment value.
12
- # ${hostname} # Full name of this host.
13
- # ${hostname:short} # Short name of this host. Everything up to the first period.
14
- # ${pid} # Process Id for this process.
15
- # ${random} # URL safe Random 32 byte value.
16
- # ${random:size} # URL safe Random value of `size` bytes.
7
+ # ${date} # Current date in the format of "%Y%m%d" (CCYYMMDD)
8
+ # ${date:format} # Current date in the supplied format. See strftime
9
+ # ${time} # Current date and time down to ms in the format of "%Y%m%d%Y%H%M%S%L" (CCYYMMDDHHMMSSmmm)
10
+ # ${time:format} # Current date and time in the supplied format. See strftime
11
+ # ${env:name} # Extract value from the named environment value.
12
+ # # Raises SecretConfig::MissingEnvironmentVariable when the env var is not defined.
13
+ # ${env:name,default} # Extract value from the named environment value.
14
+ # # Returns the supplied default value when the env var is not defined.
15
+ # ${hostname} # Full name of this host.
16
+ # ${hostname:short} # Short name of this host. Everything up to the first period.
17
+ # ${pid} # Process Id for this process.
18
+ # ${random} # URL safe Random 32 byte value.
19
+ # ${random:size} # URL safe Random value of `size` bytes.
20
+ # ${select:a,b,c,d} # Randomly select one of the supplied values. A new new value is selected on restart or refresh.
21
+ # # Values are separated by `,` and cannot include `,` in their values.
22
+ # # Values are stripped of leading and trailing spaces.
17
23
  module SecretConfig
18
24
  class SettingInterpolator < StringInterpolator
19
25
  def date(format = "%Y%m%d")
@@ -24,8 +30,12 @@ module SecretConfig
24
30
  Time.now.strftime(format)
25
31
  end
26
32
 
27
- def env(name)
28
- ENV[name]
33
+ def env(name, default = :no_default_supplied)
34
+ return ENV[name] if ENV.key?(name)
35
+
36
+ return default unless default == :no_default_supplied
37
+
38
+ raise(MissingEnvironmentVariable, "Missing mandatory environment variable: #{name}")
29
39
  end
30
40
 
31
41
  def hostname(format = nil)
@@ -41,5 +51,14 @@ module SecretConfig
41
51
  def random(size = 32)
42
52
  SecureRandom.urlsafe_base64(size)
43
53
  end
54
+
55
+ # Empty values return nil which removes the key entirely from the config
56
+ def select(*values)
57
+ if values.size < 2
58
+ raise(ConfigurationError, "Must supply at least 2 options when using select: #{values.inspect}")
59
+ end
60
+
61
+ values[SecureRandom.random_number(values.count)]
62
+ end
44
63
  end
45
64
  end
@@ -1,3 +1,3 @@
1
1
  module SecretConfig
2
- VERSION = "0.10.2".freeze
2
+ VERSION = "1.0.0".freeze
3
3
  end
@@ -132,12 +132,12 @@ class RegistryTest < Minitest::Test
132
132
 
133
133
  it "of integers" do
134
134
  value = registry.fetch("mysql/ports", type: :integer, separator: ",")
135
- assert_equal([12345, 5343, 26815], value)
135
+ assert_equal([12_345, 5343, 26_815], value)
136
136
  end
137
137
 
138
138
  it "of integers with spaces" do
139
139
  value = registry.fetch("mysql/ports2", type: :integer, separator: ",")
140
- assert_equal([12345, 5343, 26815], value)
140
+ assert_equal([12_345, 5343, 26_815], value)
141
141
  end
142
142
 
143
143
  it "accepts a default without requiring conversion" do
@@ -47,6 +47,14 @@ class SecretConfigTest < Minitest::Test
47
47
  assert_equal "secret_config_test", SecretConfig.fetch("mysql/database")
48
48
  end
49
49
 
50
+ it "fetches with default" do
51
+ assert_equal "default", SecretConfig.fetch("mysql/unknown", default: "default")
52
+ end
53
+
54
+ it "fetches with default of nil" do
55
+ assert_nil SecretConfig.fetch("mysql/unknown", default: nil)
56
+ end
57
+
50
58
  it "can be overridden by an environment variable" do
51
59
  ENV["MYSQL_DATABASE"] = "other"
52
60
 
@@ -96,6 +104,5 @@ class SecretConfigTest < Minitest::Test
96
104
  assert_equal true, database
97
105
  end
98
106
  end
99
-
100
107
  end
101
108
  end
@@ -101,8 +101,15 @@ module SecretConfig
101
101
 
102
102
  it "handles missing ENV var" do
103
103
  string = "${env:OTHER_TEST_SETTING}"
104
+ assert_raises SecretConfig::MissingEnvironmentVariable do
105
+ interpolator.parse(string)
106
+ end
107
+ end
108
+
109
+ it "uses default value for missing ENV var" do
110
+ string = "${env:OTHER_TEST_SETTING,My default value}"
104
111
  actual = interpolator.parse(string)
105
- assert_equal "", actual, string
112
+ assert_equal "My default value", actual, string
106
113
  end
107
114
  end
108
115
 
@@ -147,6 +154,28 @@ module SecretConfig
147
154
  end
148
155
  end
149
156
  end
157
+
158
+ describe "#select" do
159
+ it "randomly selects one of the supplied values" do
160
+ string = "${select:one, two,three}"
161
+ actual = interpolator.parse(string)
162
+ assert %w[one two three].include?(actual), actual
163
+ end
164
+
165
+ it "fails when less than 2 options are supplied" do
166
+ string = "${select:one}"
167
+ assert_raises ConfigurationError do
168
+ interpolator.parse(string)
169
+ end
170
+ end
171
+
172
+ it "fails when no options are supplied" do
173
+ string = "${select}"
174
+ assert_raises ConfigurationError do
175
+ interpolator.parse(string)
176
+ end
177
+ end
178
+ end
150
179
  end
151
180
  end
152
181
  end
data/test/test_helper.rb CHANGED
@@ -4,6 +4,6 @@ require "yaml"
4
4
  require "minitest/autorun"
5
5
  require "minitest/reporters"
6
6
  require "secret_config"
7
- require "awesome_print"
7
+ require "amazing_print"
8
8
 
9
9
  Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: secret_config
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.2
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Reid Morrison
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-12 00:00:00.000000000 Z
11
+ date: 2022-03-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -24,9 +24,8 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
- description:
27
+ description:
28
28
  email:
29
- - reidmo@gmail.com
30
29
  executables:
31
30
  - secret-config
32
31
  extensions: []
@@ -59,11 +58,11 @@ files:
59
58
  - test/setting_interpolator_test.rb
60
59
  - test/test_helper.rb
61
60
  - test/utils_test.rb
62
- homepage: https://github.com/rocketjob/secret_config
61
+ homepage: https://config.rocketjob.io
63
62
  licenses:
64
63
  - Apache-2.0
65
64
  metadata: {}
66
- post_install_message:
65
+ post_install_message:
67
66
  rdoc_options: []
68
67
  require_paths:
69
68
  - lib
@@ -78,17 +77,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
78
77
  - !ruby/object:Gem::Version
79
78
  version: '0'
80
79
  requirements: []
81
- rubygems_version: 3.0.8
82
- signing_key:
80
+ rubygems_version: 3.3.7
81
+ signing_key:
83
82
  specification_version: 4
84
83
  summary: Centralized Configuration and Secrets Management for Ruby and Rails applications.
85
84
  test_files:
86
85
  - test/config/application.yml
87
- - test/providers/ssm_test.rb
86
+ - test/parser_test.rb
88
87
  - test/providers/file_test.rb
88
+ - test/providers/ssm_test.rb
89
89
  - test/registry_test.rb
90
+ - test/secret_config_test.rb
90
91
  - test/setting_interpolator_test.rb
91
- - test/parser_test.rb
92
92
  - test/test_helper.rb
93
93
  - test/utils_test.rb
94
- - test/secret_config_test.rb