secret_config 0.8.0 → 0.9.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: 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