secret_config 0.8.0 → 0.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8df953d1a074c81118be3cc9458c9790b81fb72fbda9b075ae2a925393be657d
4
- data.tar.gz: e1605d3af8e236b458f25e3d5e6b6bd801c9be1e7767d1925d851fc778478a98
3
+ metadata.gz: bcd736808cbbcc2366bd1e6f8ab87eedc17bbd45bae3492aa7f28b9d1c7be046
4
+ data.tar.gz: bcea91adfdfe4e3b0f57d78c4954b07e61234870d746aafddd35448ee524d298
5
5
  SHA512:
6
- metadata.gz: 0d2527c9397af668b648e1cb4f49994db83bfc9ec0619435392f66b59556e5a67ffa2a1cbbff102d23dc5e23000f5cdd3e2344dd385e876b72951ac25fe70b52
7
- data.tar.gz: c0a9f535b97e9e2cd2a1ea429b1464365a24d85ac269213548eda15318916490f7d59a2e5fb8eefc33b88a774c1e01ddff4ca6f4112451d61a09f440b9161cdb
6
+ metadata.gz: 9c12a1b4709b92c0db325f209ac36ca244bf8ac7acf518ff165ebdbbcb2a84a658c25cdee931c13e86ebba033e16998c7acd96e53e6da6021c086c350a1277c2
7
+ data.tar.gz: 87c131f25083a20a0850369704f36311d6ae6e25cc940ba76845a2fcc45cf758a00d513f8157c332defd402990eddd6fe65a3e22997b72e5693ccf147d21150a
data/README.md CHANGED
@@ -5,6 +5,14 @@ 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
+ ## v0.9 Upgrade Notes
9
+
10
+ Note that the command line program name has changed from `secret_config` to `secret-config`.
11
+ Be careful that the arguments have also changed. The arguments are now consistent across operations.
12
+ The command line examples below have also been updated to reflect the changes.
13
+
14
+ Please run `secret-config --help` to see the new arguments and updated operations.
15
+
8
16
  ## Overview
9
17
 
10
18
  Securely store centralized configuration information such as:
@@ -579,20 +587,26 @@ Available interpolations:
579
587
  Secret Config has a command line interface for exporting, importing and copying between paths in the registry.
580
588
 
581
589
  ~~~
582
- secret_config [options]
583
- -e, --export [FILE_NAME] Export configuration to a file or stdout if no file_name supplied.
584
- -i, --import [FILE_NAME] Import configuration from a file or stdin if no file_name supplied.
585
- -C, --copy SOURCE_PATH Import configuration from a file or stdin if no file_name supplied.
586
- -D, --diff [FILE_NAME] Compare configuration from a file or stdin if no file_name supplied.
590
+ secret-config [options]
591
+ -e, --export SOURCE_PATH Export configuration. Use --file to specify the file name, otherwise stdout is used.
592
+ -i, --import TARGET_PATH Import configuration. Use --file to specify the file name, --path for the SOURCE_PATH, otherwise stdin is used.
593
+ --file FILE_NAME Import/Export/Diff to/from this file.
594
+ -p, --path PATH Import/Export/Diff to/from this path.
595
+ --diff TARGET_PATH Compare configuration to this path. Use --file to specify the source file name, --path for the SOURCE_PATH, otherwise stdin is used.
596
+ -s, --set KEY=VALUE Set one key to value. Example: --set mysql/database=localhost
597
+ -f, --fetch KEY Fetch the value for one setting. Example: --fetch mysql/database.
598
+ -d, --delete KEY Delete one specific key.
599
+ -r, --delete-tree PATH Recursively delete all keys under the specified path.
587
600
  -c, --console Start interactive console.
588
- -p, --path PATH Path to import from / export to.
589
- -P, --provider PROVIDER Provider to use. [ssm | file]. Default: ssm
590
- -U, --no-filter Do not filter passwords and keys.
591
- -d, --prune During import delete all existing keys for which there is no key in the import file.
592
- -k, --key_id KEY_ID AWS KMS Key id or Key Alias to use when importing configuration values. Default: AWS Default key.
593
- -r, --region REGION AWS Region to use. Default: AWS_REGION env var.
594
- -R, --random_size INTEGER Size to use when generating random values. Whenever $random is encountered during an import. Default: 32
595
- -v, --version Display Symmetric Encryption version.
601
+ --provider PROVIDER Provider to use. [ssm | file]. Default: ssm
602
+ --no-filter For --export only. Do not filter passwords and keys.
603
+ --interpolate For --export only. Evaluate string interpolation and __import__.
604
+ --prune For --import only. During import delete all existing keys for which there is no key in the import file. Only works with --import.
605
+ --force For --import only. Overwrite all values, not just the changed ones. Useful for changing the KMS key.
606
+ --key_id KEY_ID For --import only. Encrypt config settings with this AWS KMS key id. Default: AWS Default key.
607
+ --key_alias KEY_ALIAS For --import only. Encrypt config settings with this AWS KMS alias.
608
+ --random_size INTEGER For --import only. Size to use when generating random values when $(random) is encountered in the source. Default: 32
609
+ -v, --version Display Secret Config version.
596
610
  -h, --help Prints this help.
597
611
  ~~~
598
612
 
@@ -624,18 +638,22 @@ secrets:
624
638
 
625
639
  Import a yaml file, into a path in AWS SSM Parameter Store:
626
640
 
627
- secret_config --import production.yml --path /production/my_application
641
+ secret-config --import /production/my_application --path production.yml
628
642
 
629
643
  Import a yaml file, into a path in AWS SSM Parameter Store, using a custom KMS key to encrypt the values:
630
644
 
631
- secret_config --import production.yml --path /production/my_application --key_id "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
645
+ secret-config --import /production/my_application --path production.yml --key_id "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
646
+
647
+ Import a yaml file, into a path in AWS SSM Parameter Store, using a custom KMS key alias to encrypt the values:
648
+
649
+ secret-config --import /production/my_application --path production.yml --key_alias my_key_alias
632
650
 
633
651
  #### Diff
634
652
 
635
653
  Before importing a new config file into the AWS SSM Parameter store, a diff can be performed to determine
636
654
  what the differences are that will be applied when the import is run with the `--prune` option.
637
655
 
638
- secret_config --diff production.yml --path /production/my_application
656
+ secret-config --diff /production/my_application --path production.yml
639
657
 
640
658
  Key:
641
659
 
@@ -650,15 +668,15 @@ Export the values from a specific path into a yaml or json file so that they are
650
668
 
651
669
  Export from a path in AWS SSM Parameter Store to a yaml file, where passwords are filtered:
652
670
 
653
- secret_config --export production.yml --path /production/my_application
671
+ secret-config --export /production/my_application --file production.yml
654
672
 
655
673
  Export from a path in AWS SSM Parameter Store to a yaml file, _without_ filtering out passwords:
656
674
 
657
- secret_config --export production.yml --path /production/my_application --no-filter
675
+ secret-config --export /production/my_application --file production.yml --no-filter
658
676
 
659
677
  Export from a path in AWS SSM Parameter Store to a json file, where passwords are filtered:
660
678
 
661
- secret_config --export production.json --path /production/my_application
679
+ secret-config --export /production/my_application --file production.json
662
680
 
663
681
  #### Copy values between paths in AWS SSM parameter store
664
682
 
@@ -666,9 +684,9 @@ It can be useful to keep a "master" copy of the values for an environment or sta
666
684
  in AWS Parameter Store. Then for each stack or environment that is spun up, copy the "master" / "common" values
667
685
  into the new path. Once copied the values specific to that path can be updated accordingly.
668
686
 
669
- Copy configuration from one path in AWS SSM Parameter Store to another path in AWS SSM Parameter Store:
687
+ Import configuration from an existing path in AWS SSM Parameter Store into another:
670
688
 
671
- secret_config --copy /production/my_application --path /tenant73/my_application
689
+ secret-config --import /tenant73/my_application --path /production/my_application
672
690
 
673
691
  #### Generating random passwords
674
692
 
File without changes
@@ -8,11 +8,29 @@ require "irb"
8
8
 
9
9
  module SecretConfig
10
10
  class CLI
11
- attr_reader :path, :region, :provider,
12
- :export, :no_filter,
11
+ module Colors
12
+ CLEAR = "\e[0m".freeze
13
+ BOLD = "\e[1m".freeze
14
+ BLACK = "\e[30m".freeze
15
+ RED = "\e[31m".freeze
16
+ GREEN = "\e[32m".freeze
17
+ YELLOW = "\e[33m".freeze
18
+ BLUE = "\e[34m".freeze
19
+ MAGENTA = "\e[35m".freeze
20
+ CYAN = "\e[36m".freeze
21
+ WHITE = "\e[37m".freeze
22
+
23
+ TITLE = "\e[1m".freeze
24
+ KEY = "\e[36m".freeze
25
+ REMOVE = "\e[31m".freeze
26
+ ADD = "\e[32m".freeze
27
+ end
28
+
29
+ attr_reader :path, :provider, :file_name,
30
+ :export, :no_filter, :interpolate,
13
31
  :import, :key_id, :key_alias, :random_size, :prune, :force,
14
32
  :diff_path, :import_path,
15
- :fetch_key, :delete_key, :set_key, :set_value, :delete_path,
33
+ :fetch_key, :delete_key, :set_key, :set_value, :delete_tree,
16
34
  :copy_path, :diff,
17
35
  :console,
18
36
  :show_version
@@ -29,7 +47,6 @@ module SecretConfig
29
47
  @path = nil
30
48
  @key_id = nil
31
49
  @key_alias = nil
32
- @region = ENV["AWS_REGION"]
33
50
  @provider = :ssm
34
51
  @random_size = 32
35
52
  @no_filter = false
@@ -43,10 +60,11 @@ module SecretConfig
43
60
  @set_value = nil
44
61
  @fetch_key = nil
45
62
  @delete_key = nil
46
- @delete_path = nil
63
+ @delete_tree = nil
47
64
  @diff_path = nil
48
65
  @import_path = nil
49
66
  @force = false
67
+ @interpolate = false
50
68
 
51
69
  if argv.empty?
52
70
  puts parser
@@ -58,27 +76,32 @@ module SecretConfig
58
76
  def run!
59
77
  if show_version
60
78
  puts "Secret Config v#{VERSION}"
61
- puts "Region: #{region}"
62
79
  elsif console
63
80
  run_console
64
81
  elsif export
65
- run_export(export, path, filtered: !no_filter)
82
+ raise(ArgumentError, "--path option is not valid for --export") if path
83
+
84
+ run_export(export, file_name || STDOUT, filtered: !no_filter)
66
85
  elsif import
67
- run_import(import, path, prune, force)
68
- elsif import_path
69
- run_import_path(import_path, path, prune, force)
86
+ if path
87
+ run_import_path(import, path, prune, force)
88
+ else
89
+ run_import(import, file_name || STDIN, prune, force)
90
+ end
70
91
  elsif diff
71
- run_diff(diff, path)
72
- elsif diff_path
73
- run_diff_path(diff_path, path)
92
+ if path
93
+ run_diff_path(diff, path)
94
+ else
95
+ run_diff(diff, file_name || STDIN)
96
+ end
74
97
  elsif set_key
75
98
  run_set(set_key, set_value)
76
99
  elsif fetch_key
77
100
  run_fetch(fetch_key)
78
101
  elsif delete_key
79
102
  run_delete(delete_key)
80
- elsif delete_path
81
- run_delete_path(delete_path)
103
+ elsif delete_tree
104
+ run_delete_tree(delete_tree)
82
105
  else
83
106
  puts parser
84
107
  end
@@ -91,27 +114,27 @@ module SecretConfig
91
114
 
92
115
  For more information, see: https://rocketjob.github.io/secret_config/
93
116
 
94
- secret_config [options]
117
+ secret-config [options]
95
118
  BANNER
96
119
 
97
- opts.on "-e", "--export [FILE_NAME]", "Export configuration to a file or stdout if no file_name supplied. --path SOURCE_PATH is required." do |file_name|
98
- @export = file_name || STDOUT
120
+ opts.on "-e", "--export SOURCE_PATH", "Export configuration. Use --file to specify the file name, otherwise stdout is used." do |path|
121
+ @export = path
99
122
  end
100
123
 
101
- opts.on "-i", "--import [FILE_NAME]", "Import configuration from a file or stdin if no file_name supplied. --path TARGET_PATH is required." do |file_name|
102
- @import = file_name || STDIN
124
+ opts.on "-i", "--import TARGET_PATH", "Import configuration. Use --file to specify the file name, --path for the SOURCE_PATH, otherwise stdin is used." do |path|
125
+ @import = path
103
126
  end
104
127
 
105
- opts.on "--import-path SOURCE_PATH", "Import configuration from the configuration on another path. --path TARGET_PATH is required." do |path|
106
- @import_path = path
128
+ opts.on "-f", "--file FILE_NAME", "Import/Export/Diff to/from this file." do |file_name|
129
+ @file_name = file_name
107
130
  end
108
131
 
109
- opts.on "--diff [FILE_NAME]", "Compare configuration from a file or stdin if no file_name supplied. --path TARGET_PATH is required." do |file_name|
110
- @diff = file_name
132
+ opts.on "-p", "--path PATH", "Import/Export/Diff to/from this path." do |path|
133
+ @path = path
111
134
  end
112
135
 
113
- opts.on "--diff-path SOURCE_PATH", "Diff configuration with the configuration on another path. --path TARGET_PATH is required." do |path|
114
- @diff_path = path
136
+ opts.on "--diff TARGET_PATH", "Compare configuration to this path. Use --file to specify the source file name, --path for the SOURCE_PATH, otherwise stdin is used." do |file_name|
137
+ @diff = file_name
115
138
  end
116
139
 
117
140
  opts.on "-s", "--set KEY=VALUE", "Set one key to value. Example: --set mysql/database=localhost" do |param|
@@ -121,59 +144,55 @@ module SecretConfig
121
144
  end
122
145
  end
123
146
 
124
- opts.on "-f", "--fetch KEY", "Fetch the value for one setting. Example: --get mysql/database. " do |key|
147
+ opts.on "-f", "--fetch KEY", "Fetch the value for one setting. Example: --fetch mysql/database." do |key|
125
148
  @fetch_key = key
126
149
  end
127
150
 
128
- opts.on "-d", "--delete KEY", "Delete one specific key. See --delete-path to delete all keys under a specific path " do |key|
151
+ opts.on "-d", "--delete KEY", "Delete one specific key." do |key|
129
152
  @delete_key = key
130
153
  end
131
154
 
132
- opts.on "-r", "--delete-path PATH", "Recursively delete all keys under the specified path.. " do |path|
133
- @delete_path = path
155
+ opts.on "-r", "--delete-tree PATH", "Recursively delete all keys under the specified path." do |path|
156
+ @delete_tree = path
134
157
  end
135
158
 
136
159
  opts.on "-c", "--console", "Start interactive console." do
137
160
  @console = true
138
161
  end
139
162
 
140
- opts.on "-p", "--path PATH", "Path in central configuration to use." do |path|
141
- @path = path
142
- end
143
-
144
163
  opts.on "--provider PROVIDER", "Provider to use. [ssm | file]. Default: ssm" do |provider|
145
164
  @provider = provider.to_sym
146
165
  end
147
166
 
148
- opts.on "--no-filter", "Do not filter passwords and keys." do
167
+ opts.on "--no-filter", "For --export only. Do not filter passwords and keys." do
149
168
  @no_filter = true
150
169
  end
151
170
 
152
- opts.on "--prune", "During import delete all existing keys for which there is no key in the import file. Only applies to --import and --import-path." do
171
+ opts.on "--interpolate", "For --export only. Evaluate string interpolation and __import__." do
172
+ @interpolate = true
173
+ end
174
+
175
+ opts.on "--prune", "For --import only. During import delete all existing keys for which there is no key in the import file. Only works with --import." do
153
176
  @prune = true
154
177
  end
155
178
 
156
- opts.on "--force", "During import overwrite all values, not just the changed ones. Useful for changing the KMS key. Only applies to --import and --import-path." do
179
+ opts.on "--force", "For --import only. Overwrite all values, not just the changed ones. Useful for changing the KMS key." do
157
180
  @force = true
158
181
  end
159
182
 
160
- opts.on "--key_id KEY_ID", "Encrypt config settings with this AWS KMS key id. Default: AWS Default key." do |key_id|
183
+ opts.on "--key_id KEY_ID", "For --import only. Encrypt config settings with this AWS KMS key id. Default: AWS Default key." do |key_id|
161
184
  @key_id = key_id
162
185
  end
163
186
 
164
- opts.on "--key_alias KEY_ALIAS", "Encrypt config settings with this AWS KMS alias." do |key_alias|
187
+ opts.on "--key_alias KEY_ALIAS", "For --import only. Encrypt config settings with this AWS KMS alias." do |key_alias|
165
188
  @key_alias = key_alias
166
189
  end
167
190
 
168
- opts.on "--region REGION", "AWS Region to use. Default: AWS_REGION env var." do |region|
169
- @region = region
170
- end
171
-
172
- opts.on "--random_size INTEGER", Integer, "Size to use when generating random values. Whenever #{RANDOM} is encountered during an import. Default: 32" do |random_size|
191
+ opts.on "--random_size INTEGER", Integer, "For --import only. Size to use when generating random values when $(random) is encountered in the source. Default: 32" do |random_size|
173
192
  @random_size = random_size
174
193
  end
175
194
 
176
- opts.on "-v", "--version", "Display Symmetric Encryption version." do
195
+ opts.on "-v", "--version", "Display Secret Config version." do
177
196
  @show_version = true
178
197
  end
179
198
 
@@ -198,57 +217,55 @@ module SecretConfig
198
217
  end
199
218
  end
200
219
 
201
- def run_export(file_name, path, filtered: true)
202
- raise(ArgumentError, "Missing required option --path") unless path
220
+ def run_export(source_path, file_name, filtered: true)
221
+ puts("Exporting #{provider}:#{source_path} to #{file_name}") if file_name.is_a?(String)
203
222
 
204
- config = fetch_config(path, filtered: filtered)
223
+ config = fetch_config(source_path, filtered: filtered)
205
224
  write_config_file(file_name, config)
206
-
207
- puts("Exported #{path} from #{provider} to #{file_name}") if file_name.is_a?(String)
208
225
  end
209
226
 
210
- def run_import(file_name, path, prune, force)
211
- raise(ArgumentError, "Missing required option --path") unless path
212
-
227
+ def run_import(target_path, file_name, prune, force)
228
+ puts "#{Colors::TITLE}--- #{provider}:#{target_path}"
229
+ puts "+++ #{file_name}#{Colors::CLEAR}"
213
230
  config = read_config_file(file_name)
214
- import_config(config, path, prune, force)
215
-
216
- puts("Imported #{file_name} to #{path} on provider: #{provider}") if file_name.is_a?(String)
231
+ import_config(config, target_path, prune, force)
217
232
  end
218
233
 
219
- def run_import_path(source_path, path, prune, force)
220
- raise(ArgumentError, "Missing required option --path") unless path
234
+ def run_import_path(target_path, source_path, prune, force)
235
+ puts "#{Colors::TITLE}--- #{provider}:#{target_path}"
236
+ puts "+++ #{provider}:#{source_path}#{Colors::CLEAR}"
221
237
 
222
238
  config = fetch_config(source_path, filtered: false)
223
- import_config(config, path, prune, force)
239
+ import_config(config, target_path, prune, force)
224
240
 
225
- puts("Imported #{source_path} to #{path} on provider: #{provider}")
241
+ puts("Imported #{target_path} from #{source_path} on provider: #{provider}")
226
242
  end
227
243
 
228
- def run_diff(file_name, path)
229
- raise(ArgumentError, "Missing required option --path") unless path
244
+ def run_diff(target_path, file_name)
245
+ source_config = read_config_file(file_name)
246
+ source = Utils.flatten(source_config, target_path)
230
247
 
231
- file_config = read_config_file(file_name)
232
- file = Utils.flatten(file_config, path)
248
+ target_config = fetch_config(target_path, filtered: false)
249
+ target = Utils.flatten(target_config, target_path)
233
250
 
234
- registry_config = fetch_config(path, filtered: false)
235
- registry = Utils.flatten(registry_config, path)
236
-
237
- puts("Comparing #{file_name} to #{path} on provider: #{provider}") if file_name.is_a?(String)
238
- diff_config(file, registry)
251
+ if file_name.is_a?(String)
252
+ puts "#{Colors::TITLE}--- #{provider}:#{target_path}"
253
+ puts "+++ #{file_name}#{Colors::CLEAR}"
254
+ end
255
+ diff_config(target, source)
239
256
  end
240
257
 
241
- def run_diff_path(source_path, path)
242
- raise(ArgumentError, "Missing required option --path") unless path
243
-
258
+ def run_diff_path(target_path, source_path)
244
259
  source_config = fetch_config(source_path, filtered: false)
245
260
  source = Utils.flatten(source_config)
246
261
 
247
- target_config = fetch_config(path, filtered: false)
262
+ target_config = fetch_config(target_path, filtered: false)
248
263
  target = Utils.flatten(target_config)
249
264
 
250
- puts("Comparing #{source_path} to #{path} on provider: #{provider}")
251
- diff_config(source, target)
265
+ puts "#{Colors::TITLE}--- #{provider}:#{target_path}"
266
+ puts "+++ #{provider}:#{source_path}#{Colors::CLEAR}"
267
+
268
+ diff_config(target, source)
252
269
  end
253
270
 
254
271
  def run_console
@@ -256,14 +273,18 @@ module SecretConfig
256
273
  end
257
274
 
258
275
  def run_delete(key)
276
+ puts "#{Colors::TITLE}--- #{provider}:#{path}"
277
+ puts "#{Colors::REMOVE}- #{key}#{Colors::CLEAR}"
259
278
  provider_instance.delete(key)
260
279
  end
261
280
 
262
- def run_delete_path(path)
281
+ def run_delete_tree(path)
263
282
  source_config = fetch_config(path)
264
- source = Utils.flatten(source_config, path)
283
+ puts "#{Colors::TITLE}--- #{provider}:#{path}#{Colors::CLEAR}"
284
+
285
+ source = Utils.flatten(source_config, path)
265
286
  source.each_key do |key|
266
- puts("Deleting #{key}")
287
+ puts "#{Colors::REMOVE}- #{key}#{Colors::CLEAR}"
267
288
  provider_instance.delete(key)
268
289
  end
269
290
  end
@@ -277,8 +298,8 @@ module SecretConfig
277
298
  provider_instance.set(key, value)
278
299
  end
279
300
 
280
- def current_values
281
- @current_values ||= Utils.flatten(fetch_config(path, filtered: false), path)
301
+ def current_values(path)
302
+ Utils.flatten(fetch_config(path, filtered: false), path)
282
303
  end
283
304
 
284
305
  def read_config_file(file_name)
@@ -306,38 +327,53 @@ module SecretConfig
306
327
  # Ignore filtered values
307
328
  next
308
329
  end
309
- puts "Setting: #{key}"
330
+
331
+ if current_values.key?(key)
332
+ puts "#{Colors::KEY}* #{key}#{Colors::CLEAR}"
333
+ else
334
+ puts "#{Colors::ADD}+ #{key}#{Colors::CLEAR}"
335
+ end
336
+
310
337
  provider_instance.set(key, value)
311
338
  end
312
339
  end
313
340
 
314
341
  def fetch_config(path, filtered: true)
315
- registry = Registry.new(path: path, provider: provider_instance)
342
+ registry = Registry.new(path: path, provider: provider_instance, interpolate: interpolate)
316
343
  config = filtered ? registry.configuration : registry.configuration(filters: nil)
317
344
  sort_hash_by_key!(config)
318
345
  end
319
346
 
320
347
  # Diffs two configs and displays the results
321
- def diff_config(source, target)
348
+ def diff_config(target, source)
322
349
  (source.keys + target.keys).sort.uniq.each do |key|
323
350
  if target.key?(key)
324
351
  if source.key?(key)
325
352
  value = source[key].to_s
326
353
  # Ignore filtered values
327
- puts "* #{key}: #{target[key]} => #{source[key]}" if (value != target[key].to_s) && (value != FILTERED)
354
+ if (value != target[key].to_s) && (value != FILTERED)
355
+ puts "#{Colors::KEY}#{key}:"
356
+ puts "#{Colors::REMOVE}#{prefix_lines("- ", target[key])}"
357
+ puts "#{Colors::ADD}#{prefix_lines("+ ", source[key])}#{Colors::CLEAR}\n\n"
358
+ end
328
359
  else
329
- puts "- #{key}"
360
+ puts "#{Colors::KEY}#{key}:"
361
+ puts "#{Colors::REMOVE}#{prefix_lines("- ", target[key])}\n\n"
330
362
  end
331
363
  elsif source.key?(key)
332
- puts "+ #{key}: #{source[key]}"
364
+ puts "#{Colors::KEY}#{key}:"
365
+ puts "#{Colors::ADD}#{prefix_lines("+ ", source[key])}#{Colors::CLEAR}\n\n"
333
366
  end
334
367
  end
335
368
  end
336
369
 
337
- def import_config(config, path, prune, force)
338
- raise(ArgumentError, "Missing required option --path") unless path
370
+ def prefix_lines(prefix, value)
371
+ value.to_s.lines.collect { |line| "#{prefix}#{line}" }.join("")
372
+ end
339
373
 
340
- delete_keys = prune ? current_values.keys - Utils.flatten(config, path).keys : []
374
+ def import_config(config, path, prune, force)
375
+ current = current_values(path)
376
+ delete_keys = prune ? current.keys - Utils.flatten(config, path).keys : []
341
377
 
342
378
  unless delete_keys.empty?
343
379
  puts "Going to delete the following keys:"
@@ -345,10 +381,10 @@ module SecretConfig
345
381
  sleep(5)
346
382
  end
347
383
 
348
- set_config(config, path, force ? {} : current_values)
384
+ set_config(config, path, force ? {} : current)
349
385
 
350
386
  delete_keys.each do |key|
351
- puts "Deleting: #{key}"
387
+ puts "#{Colors::REMOVE}- #{key}#{Colors::CLEAR}"
352
388
  provider_instance.delete(key)
353
389
  end
354
390
  end
@@ -2,26 +2,26 @@ module SecretConfig
2
2
  class Parser
3
3
  attr_reader :tree, :path, :registry, :interpolator
4
4
 
5
- def initialize(path, registry)
5
+ def initialize(path, registry, interpolate: true)
6
6
  @path = path
7
7
  @registry = registry
8
8
  @fetch_list = {}
9
9
  @import_list = {}
10
10
  @tree = {}
11
- @interpolator = SettingInterpolator.new
11
+ @interpolator = interpolate ? SettingInterpolator.new : nil
12
12
  end
13
13
 
14
14
  # Returns a flat path of keys and values from the provider without looking in the local path.
15
15
  # Keys are returned with path names relative to the supplied path.
16
16
  def parse(key, value)
17
17
  relative_key = relative_key?(key) ? key : key.sub("#{path}/", "")
18
- tree[relative_key] = value.is_a?(String) && value.include?("%{") ? interpolator.parse(value) : value
18
+ value = interpolator.parse(value) if interpolator && value.is_a?(String) && value.include?("%{")
19
+ tree[relative_key] = value
19
20
  end
20
21
 
21
22
  # Returns a flat Hash of the rendered paths.
22
23
  def render
23
- # apply_fetches
24
- apply_imports
24
+ apply_imports if interpolator
25
25
  tree
26
26
  end
27
27
 
@@ -38,10 +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/
41
+ next unless (key =~ /\/__import__\Z/) || (key == "__import__")
42
42
 
43
43
  import_key = tree.delete(key)
44
44
  key, _ = ::File.split(key)
45
+ key = nil if key == "."
45
46
 
46
47
  # binding.irb
47
48
 
@@ -53,13 +54,13 @@ module SecretConfig
53
54
  match = current_key.match(/\A#{import_key}\/(.*)/)
54
55
  next unless match
55
56
 
56
- imported_key = ::File.join(key, match[1])
57
+ imported_key = key.nil? ? match[1] : ::File.join(key, match[1])
57
58
  tree[imported_key] = tree[current_key] unless tree.key?(imported_key)
58
59
  end
59
60
  else
60
61
  relative_paths = registry.send(:fetch_path, import_key)
61
62
  relative_paths.each_pair do |relative_key, value|
62
- imported_key = ::File.join(key, relative_key)
63
+ imported_key = key.nil? ? relative_key : ::File.join(key, relative_key)
63
64
  tree[imported_key] = value unless tree.key?(imported_key)
64
65
  end
65
66
  end
@@ -4,18 +4,19 @@ require "concurrent-ruby"
4
4
  module SecretConfig
5
5
  # Centralized configuration with values stored in AWS System Manager Parameter Store
6
6
  class Registry
7
- attr_reader :provider
7
+ attr_reader :provider, :interpolate
8
8
  attr_accessor :path
9
9
 
10
- def initialize(path: nil, provider: nil, provider_args: nil)
10
+ def initialize(path: nil, provider: nil, provider_args: nil, interpolate: true)
11
11
  @path = default_path(path)
12
12
  raise(UndefinedRootError, "Root must start with /") unless @path.start_with?("/")
13
13
 
14
14
  resolved_provider = default_provider(provider)
15
15
  provider_args = nil if resolved_provider != provider
16
16
 
17
- @provider = create_provider(resolved_provider, provider_args)
18
- @cache = Concurrent::Map.new
17
+ @provider = create_provider(resolved_provider, provider_args)
18
+ @cache = Concurrent::Map.new
19
+ @interpolate = interpolate
19
20
  refresh!
20
21
  end
21
22
 
@@ -115,7 +116,7 @@ module SecretConfig
115
116
  # Returns a flat path of keys and values from the provider without looking in the local path.
116
117
  # Keys are returned with path names relative to the supplied path.
117
118
  def fetch_path(path)
118
- parser = Parser.new(path, self)
119
+ parser = Parser.new(path, self, interpolate: interpolate)
119
120
  provider.each(path) { |key, value| parser.parse(key, value) }
120
121
  parser.render
121
122
  end
@@ -1,3 +1,3 @@
1
1
  module SecretConfig
2
- VERSION = "0.8.0".freeze
2
+ VERSION = "0.9.0".freeze
3
3
  end
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.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Reid Morrison
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-29 00:00:00.000000000 Z
11
+ date: 2020-05-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -28,14 +28,14 @@ description:
28
28
  email:
29
29
  - reidmo@gmail.com
30
30
  executables:
31
- - secret_config
31
+ - secret-config
32
32
  extensions: []
33
33
  extra_rdoc_files: []
34
34
  files:
35
35
  - LICENSE
36
36
  - README.md
37
37
  - Rakefile
38
- - bin/secret_config
38
+ - bin/secret-config
39
39
  - lib/secret_config.rb
40
40
  - lib/secret_config/cli.rb
41
41
  - lib/secret_config/config.rb