config_o_mat 0.4.4 → 0.5.0.beta0

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: f6e9883d10f0ab6349aa447ef2b199692e05c02aeb8f17a95b8c1a7825ffab9b
4
- data.tar.gz: 2486dea3da2ad9f2113e88d966efcff92616fd1baec02e1ac4dacf392df19710
3
+ metadata.gz: f62a5fad3fe328d90a14019d838f16de18fe9398a380c02d9f49882c90bcb4e3
4
+ data.tar.gz: 1f2d4bcfacefe5481d965bf4898e8c53f980c4ffbaab84bc3d51fb9a513ac7e0
5
5
  SHA512:
6
- metadata.gz: edfc8bb95b506e73c191fba4f476e38cc18d3478a2917d159ee448e9392662c042b00991c1ad5f985b29c73f988a580c8fff1814155a71c3f120dc6dd0a117c2
7
- data.tar.gz: 80df6494340c67f9ea7b57b558080ae3e369d0249e9505511db33c7c070fc434144c3bba096adff07176a66085b57bbc4a8013329453b57343ef0024305018e5
6
+ metadata.gz: 15243ce8aff37e6218459cf7f629394200eb309b649edc99ce260d44ecb11150be97dad200a0d78a20b599f3af3bd9b38cd06d467400aaa8b8dfee3cce0fbd09
7
+ data.tar.gz: 62ba31b15aabb2a19ba22354d7837be93f17d9455564da40da878fb9949a6fe1eabfbf9db27baed5ecfb23bb49a7421717311d6d182135914dc9830fd7b7fca5
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## 0.5.0.beta0
2
+
3
+ NEW FEATURES:
4
+
5
+ * Profiles can now specify an s3_fallback hash with bucket: and object:. If AWS AppConfig is unreachable, we will fall back to loading the specified S3 object and interpreting it as a response from AWS AppConfig. For best results enable versioning on the bucket.
6
+ * AppConfig responses can request that the s3_fallback be used by setting a top level "aws:chaos_config" key to true. This allows for deployments which AppConfig deployments which can test the fallback mechanism. If a chaos config load fails, the source AppConfig profile will remain in use. Chaos config is ignored in error recovery scenarios.
7
+
8
+ BUG FIXES:
9
+
10
+ * config_o_mat-configurator/meta_configurator now correctly log the op that errored.
11
+
1
12
  ## 0.4.4
2
13
 
3
14
  BUG FIXES:
data/Gemfile.lock CHANGED
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- config_o_mat (0.4.0)
4
+ config_o_mat (0.4.4)
5
5
  aws-sdk-appconfig (~> 1.18)
6
+ aws-sdk-s3 (~> 1.114)
6
7
  aws-sdk-secretsmanager (~> 1.57)
7
8
  facter (~> 4.2, >= 4.2.8)
8
9
  lifecycle_vm (~> 0.1.1)
@@ -14,23 +15,30 @@ GEM
14
15
  remote: https://rubygems.org/
15
16
  specs:
16
17
  aws-eventstream (1.2.0)
17
- aws-partitions (1.574.0)
18
+ aws-partitions (1.593.0)
18
19
  aws-sdk-appconfig (1.25.0)
19
20
  aws-sdk-core (~> 3, >= 3.127.0)
20
21
  aws-sigv4 (~> 1.1)
21
- aws-sdk-core (3.130.0)
22
+ aws-sdk-core (3.131.1)
22
23
  aws-eventstream (~> 1, >= 1.0.2)
23
24
  aws-partitions (~> 1, >= 1.525.0)
24
25
  aws-sigv4 (~> 1.1)
25
- jmespath (~> 1.0)
26
- aws-sdk-secretsmanager (1.59.0)
26
+ jmespath (~> 1, >= 1.6.1)
27
+ aws-sdk-kms (1.57.0)
27
28
  aws-sdk-core (~> 3, >= 3.127.0)
28
29
  aws-sigv4 (~> 1.1)
29
- aws-sigv4 (1.4.0)
30
+ aws-sdk-s3 (1.114.0)
31
+ aws-sdk-core (~> 3, >= 3.127.0)
32
+ aws-sdk-kms (~> 1)
33
+ aws-sigv4 (~> 1.4)
34
+ aws-sdk-secretsmanager (1.62.0)
35
+ aws-sdk-core (~> 3, >= 3.127.0)
36
+ aws-sigv4 (~> 1.1)
37
+ aws-sigv4 (1.5.0)
30
38
  aws-eventstream (~> 1, >= 1.0.2)
31
39
  diff-lcs (1.5.0)
32
40
  docile (1.4.0)
33
- facter (4.2.9)
41
+ facter (4.2.10)
34
42
  hocon (~> 1.3)
35
43
  thor (>= 1.0.1, < 2.0)
36
44
  hocon (1.3.1)
@@ -36,12 +36,12 @@ vm = ConfigOMat::Configurator::VM.new(memory)
36
36
  vm.call
37
37
 
38
38
  if vm.errors?
39
- warn "Errored executing #{vm.error_op.class.name}"
39
+ warn "Errored executing #{vm.error_op.op.name}"
40
40
  warn "Errors: #{vm.errors}"
41
41
 
42
42
  if vm.recovery_errors?
43
43
  warn ''
44
- warn "Errors recovering from error. Errored executing recovery #{vm.current_op.class.name}"
44
+ warn "Errors recovering from error. Errored executing recovery #{vm.current_op.op.name}"
45
45
  warn "Errors: #{vm.recovery_errors}"
46
46
  end
47
47
 
@@ -36,12 +36,12 @@ vm = ConfigOMat::MetaConfigurator::VM.new(memory)
36
36
  vm.call
37
37
 
38
38
  if vm.errors?
39
- warn "Errored executing #{vm.error_op.class.name}"
39
+ warn "Errored executing #{vm.error_op.op.name}"
40
40
  warn "Errors: #{vm.errors}"
41
41
 
42
42
  if vm.recovery_errors?
43
43
  warn ''
44
- warn "Errors recovering from error. Errored executing recovery #{vm.current_op.class.name}"
44
+ warn "Errors recovering from error. Errored executing recovery #{vm.current_op.op.name}"
45
45
  warn "Errors: #{vm.recovery_errors}"
46
46
  end
47
47
 
data/config_o_mat.gemspec CHANGED
@@ -39,6 +39,7 @@ Gem::Specification.new do |spec|
39
39
  spec.executables = ["config_o_mat-configurator", "config_o_mat-meta_configurator"]
40
40
 
41
41
  spec.add_dependency('aws-sdk-appconfig', '~> 1.18')
42
+ spec.add_dependency('aws-sdk-s3', '~> 1.114')
42
43
  spec.add_dependency('aws-sdk-secretsmanager', '~> 1.57')
43
44
  spec.add_dependency('logsformyfamily', '~> 0.2')
44
45
  spec.add_dependency('lifecycle_vm', '~> 0.1.1')
@@ -22,13 +22,19 @@ module ConfigOMat
22
22
  reads :error_op, :retries_left, :applying_profile
23
23
 
24
24
  def call
25
- logger&.error(:op_failure, op: error_op.class.name, errors: error_op.errors)
25
+ logger&.error(:retry_from_failure, state: error_op.state_name, op: error_op.op.name, errors: error_op.errors)
26
26
 
27
27
  # If we aren't currently applying a profile then there's nothing for us to retry.
28
- return false if applying_profile.nil?
28
+ if applying_profile.nil?
29
+ logger&.error(:cannot_retry, reason: 'not applying profile')
30
+ return false
31
+ end
29
32
 
30
33
  # If we're out of retries, well, no more retrying
31
- return false if retries_left.zero?
34
+ if retries_left.zero?
35
+ logger&.error(:cannot_retry, reason: 'out of retries')
36
+ return false
37
+ end
32
38
 
33
39
  true
34
40
  end
@@ -25,8 +25,8 @@ module ConfigOMat
25
25
  :client_id, :compiled_templates, :applied_profiles, :applying_profile,
26
26
  :generated_templates, :services_to_reload, :profiles_to_apply,
27
27
  :last_refresh_time, :next_state, :retry_count, :retries_left, :retry_wait,
28
- :region, :appconfig_client, :secretsmanager_client, :systemd_interface,
29
- :secrets_loader_memory, :gc_compact, :gc_stat
28
+ :region, :appconfig_client, :secretsmanager_client, :s3_client,
29
+ :systemd_interface, :secrets_loader_memory, :gc_compact, :gc_stat
30
30
 
31
31
  def initialize(
32
32
  argv: [],
@@ -57,6 +57,7 @@ module ConfigOMat
57
57
  region: nil,
58
58
  appconfig_client: nil,
59
59
  secretsmanager_client: nil,
60
+ s3_client: nil,
60
61
  systemd_interface: nil,
61
62
  secrets_loader_memory: nil,
62
63
  gc_compact: 0,
@@ -92,6 +93,7 @@ module ConfigOMat
92
93
  @region = region
93
94
  @appconfig_client = appconfig_client
94
95
  @secretsmanager_client = secretsmanager_client
96
+ @s3_client = s3_client
95
97
  @systemd_interface = systemd_interface
96
98
  @secrets_loader_memory = secrets_loader_memory
97
99
  @gc_compact = gc_compact
@@ -17,19 +17,21 @@
17
17
  require 'lifecycle_vm/op_base'
18
18
 
19
19
  require 'aws-sdk-appconfig'
20
+ require 'aws-sdk-s3'
20
21
  require 'aws-sdk-secretsmanager'
21
22
 
22
23
  module ConfigOMat
23
24
  module Op
24
25
  class ConnectToAws < LifecycleVM::OpBase
25
26
  reads :region
26
- writes :appconfig_client, :secretsmanager_client
27
+ writes :appconfig_client, :secretsmanager_client, :s3_client
27
28
 
28
29
  def call
29
30
  client_opts = { logger: logger }
30
31
  client_opts[:region] = region if region
31
32
 
32
33
  self.appconfig_client = Aws::AppConfig::Client.new(client_opts)
34
+ self.s3_client = Aws::S3::Client.new(client_opts)
33
35
  self.secretsmanager_client = Aws::SecretsManager::Client.new(client_opts)
34
36
  end
35
37
  end
@@ -21,7 +21,8 @@ require 'config_o_mat/secrets_loader'
21
21
  module ConfigOMat
22
22
  module Op
23
23
  class RefreshAllProfiles < LifecycleVM::OpBase
24
- reads :profile_defs, :applied_profiles, :client_id, :appconfig_client, :secrets_loader_memory, :secretsmanager_client
24
+ reads :profile_defs, :applied_profiles, :client_id, :appconfig_client, :secrets_loader_memory,
25
+ :secretsmanager_client, :s3_client
25
26
  writes :profiles_to_apply, :last_refresh_time, :secrets_loader_memory
26
27
 
27
28
  def call
@@ -55,6 +56,22 @@ module ConfigOMat
55
56
  profiles_to_apply << LoadedProfile.new(new_profile, nil)
56
57
  end
57
58
 
59
+ def request_from_s3(profile_name, definition, ignore_errors)
60
+ fallback = definition.s3_fallback
61
+ begin
62
+ s3_response = s3_client.get_object(bucket: fallback[:bucket], key: fallback[:object])
63
+ OpenStruct.new(
64
+ content: s3_response.body,
65
+ content_type: s3_response.content_type,
66
+ configuration_version: s3_response.version_id
67
+ )
68
+ rescue StandardError => e
69
+ logger&.error(:s3_fallback_load_failed, name: profile_name, reason: e)
70
+ error profile_name, e unless ignore_errors
71
+ nil
72
+ end
73
+ end
74
+
58
75
  def refresh_appconfig_profile(profile_name, definition)
59
76
  request = {
60
77
  application: definition.application, environment: definition.environment,
@@ -63,13 +80,20 @@ module ConfigOMat
63
80
 
64
81
  current_version = applied_profiles&.fetch(profile_name, nil)&.version
65
82
  request[:client_configuration_version] = current_version if current_version
83
+ fallback = false
66
84
 
67
85
  response =
68
86
  begin
69
87
  appconfig_client.get_configuration(request)
70
88
  rescue StandardError => e
71
- error profile_name, e
72
- nil
89
+ if definition.s3_fallback
90
+ fallback = true
91
+ logger&.error(:s3_fallback_request, name: profile_name, reason: e)
92
+ request_from_s3(profile_name, definition, false)
93
+ else
94
+ error profile_name, e
95
+ nil
96
+ end
73
97
  end
74
98
 
75
99
  return if response.nil?
@@ -83,9 +107,27 @@ module ConfigOMat
83
107
  )
84
108
 
85
109
  profile = LoadedAppconfigProfile.new(
86
- profile_name, loaded_version, response.content.read, response.content_type
110
+ profile_name, loaded_version, response.content.read, response.content_type, fallback
87
111
  )
88
112
 
113
+ if profile.chaos_config? && !fallback
114
+ logger&.notice(:chaos_config_requested, name: profile_name)
115
+ if !definition.s3_fallback
116
+ logger&.error(:refusing_chaos_config, name: profile_name, reason: 'no s3 fallback provided')
117
+ else
118
+ response = request_from_s3(profile_name, definition, true)
119
+ if !response
120
+ logger&.error(:chaos_config_load_failed, name: profile_name)
121
+ else
122
+ loaded_version = response.configuration_version
123
+ logger&.notice(:chaos_config_profile_loaded, name: profile_name, new_version: loaded_version)
124
+ profile = LoadedAppconfigProfile.new(
125
+ profile_name, loaded_version, response.content.read, response.content_type, true
126
+ )
127
+ end
128
+ end
129
+ end
130
+
89
131
  loaded_secrets = nil
90
132
 
91
133
  if !profile.secret_defs.empty?
@@ -19,7 +19,8 @@ require 'lifecycle_vm/op_base'
19
19
  module ConfigOMat
20
20
  module Op
21
21
  class RefreshProfile < LifecycleVM::OpBase
22
- reads :profile_defs, :client_id, :applying_profile, :appconfig_client, :secretsmanager_client, :secrets_loader_memory
22
+ reads :profile_defs, :client_id, :applying_profile, :appconfig_client, :secretsmanager_client,
23
+ :secrets_loader_memory, :s3_client
23
24
  writes :applying_profile, :secrets_loader_memory
24
25
 
25
26
  def call
@@ -54,6 +55,21 @@ module ConfigOMat
54
55
  self.applying_profile = LoadedProfile.new(new_profile, nil)
55
56
  end
56
57
 
58
+ def request_from_s3(profile_name, definition)
59
+ fallback = definition.s3_fallback
60
+ begin
61
+ s3_response = s3_client.get_object(bucket: fallback[:bucket], key: fallback[:object])
62
+ OpenStruct.new(
63
+ content: s3_response.body,
64
+ content_type: s3_response.content_type,
65
+ configuration_version: s3_response.version_id
66
+ )
67
+ rescue StandardError => e
68
+ error profile_name, e
69
+ nil
70
+ end
71
+ end
72
+
57
73
  def refresh_appconfig_profile
58
74
  profile_name = applying_profile.name
59
75
  profile_version = applying_profile.version
@@ -63,13 +79,20 @@ module ConfigOMat
63
79
  configuration: definition.profile, client_id: client_id,
64
80
  client_configuration_version: profile_version
65
81
  }
82
+ fallback = false
66
83
 
67
84
  response =
68
85
  begin
69
86
  appconfig_client.get_configuration(request)
70
87
  rescue StandardError => e
71
- error profile_name, e
72
- nil
88
+ if definition.s3_fallback
89
+ fallback = true
90
+ logger&.error(:s3_fallback_request, name: profile_name, reason: e)
91
+ request_from_s3(profile_name, definition)
92
+ else
93
+ error profile_name, e
94
+ nil
95
+ end
73
96
  end
74
97
 
75
98
  return if response.nil? || errors?
@@ -89,7 +112,7 @@ module ConfigOMat
89
112
  )
90
113
 
91
114
  profile = LoadedAppconfigProfile.new(
92
- profile_name, loaded_version, response.content.read, response.content_type
115
+ profile_name, loaded_version, response.content.read, response.content_type, fallback
93
116
  )
94
117
 
95
118
  loaded_secrets = nil
@@ -164,27 +164,43 @@ module ConfigOMat
164
164
  end
165
165
 
166
166
  class Profile < ConfigItem
167
- attr_reader :application, :environment, :profile
167
+ attr_reader :application, :environment, :profile, :s3_fallback
168
168
 
169
169
  def initialize(opts)
170
170
  @application = opts[:application]
171
171
  @environment = opts[:environment]
172
172
  @profile = opts[:profile]
173
+ @s3_fallback = opts[:s3_fallback]
173
174
  end
174
175
 
175
176
  def validate
176
177
  error :application, 'must be present' if @application.nil? || @application.empty?
177
178
  error :environment, 'must be present' if @environment.nil? || @environment.empty?
178
179
  error :profile, 'must be present' if @profile.nil? || @profile.empty?
180
+ if !@s3_fallback.nil?
181
+ if !@s3_fallback.kind_of?(Hash)
182
+ error :s3_fallback, 'must be a hash'
183
+ else
184
+ bucket = @s3_fallback[:bucket]
185
+ object = @s3_fallback[:object]
186
+ error :s3_fallback, 'must include bucket' if bucket.nil? || bucket.empty?
187
+ error :s3_fallback, 'must include object' if object.nil? || object.empty?
188
+ end
189
+ end
179
190
  end
180
191
 
181
192
  def hash
182
- application.hash ^ environment.hash ^ profile.hash
193
+ application.hash ^ environment.hash ^ profile.hash ^ s3_fallback.hash
183
194
  end
184
195
 
185
196
  def eql?(other)
186
197
  return false if !super(other)
187
- return false if other.application != application || other.environment != environment || other.profile != profile
198
+ if other.application != application ||
199
+ other.environment != environment ||
200
+ other.profile != profile ||
201
+ other.s3_fallback != s3_fallback
202
+ return false
203
+ end
188
204
  true
189
205
  end
190
206
  end
@@ -207,6 +223,10 @@ module ConfigOMat
207
223
  @version = contents.hash
208
224
  end
209
225
 
226
+ def fallback?
227
+ false
228
+ end
229
+
210
230
  def validate
211
231
  error :name, 'must be present' if @name.nil? || @name.empty?
212
232
  error :contents, 'must be present' if @contents.nil? || @contents.empty?
@@ -275,10 +295,12 @@ module ConfigOMat
275
295
  'application/x-yaml' => proc { |str| YAML.safe_load(str, symbolize_names: true) }
276
296
  }.freeze
277
297
 
278
- def initialize(name, version, contents, content_type)
298
+ def initialize(name, version, contents, content_type, fallback = false)
279
299
  @name = name
280
300
  @version = version
281
301
  @secret_defs = {}
302
+ @fallback = fallback
303
+ @chaos_config = false
282
304
 
283
305
  parser = PARSERS[content_type]
284
306
 
@@ -287,6 +309,7 @@ module ConfigOMat
287
309
  @contents = parser.call(contents)
288
310
  if @contents.kind_of?(Hash)
289
311
  parse_secrets
312
+ @chaos_config = @contents.fetch(:"aws:chaos_config", false)
290
313
  @contents.default_proc = proc do |hash, key|
291
314
  raise KeyError.new("No key #{key.inspect} in profile #{name}", key: key, receiver: hash)
292
315
  end
@@ -307,16 +330,30 @@ module ConfigOMat
307
330
  end
308
331
 
309
332
  def hash
310
- @name.hash ^ @version.hash ^ @contents.hash
333
+ @name.hash ^ @version.hash ^ @contents.hash ^ @fallback.hash ^ @chaos_config.hash
311
334
  end
312
335
 
313
336
  def to_h
314
337
  @contents
315
338
  end
316
339
 
340
+ def fallback?
341
+ @fallback
342
+ end
343
+
344
+ def chaos_config?
345
+ @chaos_config
346
+ end
347
+
317
348
  def eql?(other)
318
349
  return false if !super(other)
319
- return false if other.version != version || other.contents != contents || other.name != name
350
+ if other.version != version ||
351
+ other.contents != contents ||
352
+ other.name != name ||
353
+ other.fallback? != fallback? ||
354
+ other.chaos_config? != chaos_config?
355
+ return false
356
+ end
320
357
  true
321
358
  end
322
359
 
@@ -426,7 +463,7 @@ module ConfigOMat
426
463
 
427
464
  attr_reader :secrets, :loaded_profile_data
428
465
 
429
- def_delegators :@loaded_profile_data, :name, :version, :contents
466
+ def_delegators :@loaded_profile_data, :name, :version, :contents, :fallback?
430
467
 
431
468
  def initialize(loaded_profile_data, secrets)
432
469
  @loaded_profile_data = loaded_profile_data
@@ -15,5 +15,5 @@
15
15
  # limitations under the License.
16
16
 
17
17
  module ConfigOMat
18
- VERSION = "0.4.4"
18
+ VERSION = "0.5.0.beta0"
19
19
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: config_o_mat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.4
4
+ version: 0.5.0.beta0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Scarborough
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-14 00:00:00.000000000 Z
11
+ date: 2022-05-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-appconfig
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.18'
27
+ - !ruby/object:Gem::Dependency
28
+ name: aws-sdk-s3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.114'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.114'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: aws-sdk-secretsmanager
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -245,9 +259,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
245
259
  version: 2.7.0
246
260
  required_rubygems_version: !ruby/object:Gem::Requirement
247
261
  requirements:
248
- - - ">="
262
+ - - ">"
249
263
  - !ruby/object:Gem::Version
250
- version: '0'
264
+ version: 1.3.1
251
265
  requirements: []
252
266
  rubygems_version: 3.1.6
253
267
  signing_key: