config_o_mat 0.2.0 → 0.4.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: b780a147a3eed4c14e90e0905d9002a1810916bb10ba7cf8e18468044a291809
4
- data.tar.gz: 87c03b8c3ff63239e4c24efb30c118b8ac3e6abfba8c38c4193037eb50bd6854
3
+ metadata.gz: 26063499446b5cf904e29c5ef7e147c845bc6a2e72e8e37b32148c35f8b4eb96
4
+ data.tar.gz: 0cce14aaef3a7b479bce31bbe8dfeeb4f4159153c999de199227aa8dd49b9602
5
5
  SHA512:
6
- metadata.gz: e4f55821c2ff2fdc1bd701d5448d87d7f7a7cd168a04811395d3ffdbfff49ca4b25689567c21905fc597427e3993f3b79ee213b4add27961def2832d7f98b475
7
- data.tar.gz: e083e171bcea253ad814d53ba6ff713f4460cf60391e367a92a9dec620b7daea3633e976d5b9f4a0ae674023f35acc768f3598ac4c9f57d1b71615d63b03ef43
6
+ metadata.gz: 94869014f18070dfcb57e3907ad2c16b24d06f5693a353a7979f57e7b5333813ec9a4c458584173ec1622240c3fc6126dae0895ac53facef31fad11775879388
7
+ data.tar.gz: eff0f317b20431cb779765aa04af447a0402fc5ede9f46c69ddcbdc5403b11b85b79989e7315cb0f21262cad1818b06bb6bccf1b7e21a91638e91aba91db53ec
data/.gitignore CHANGED
@@ -7,3 +7,6 @@ coverage/
7
7
  # YARD doc db and output
8
8
  .yardoc/
9
9
  doc/
10
+
11
+ # Gem builds
12
+ pkg/
data/CHANGELOG.md ADDED
@@ -0,0 +1,28 @@
1
+ ## 0.4.0
2
+
3
+ NEW FEATURES:
4
+
5
+ * Facter support. In your configomat config set a top level `facter` key to either truthy or a string. If truthy, facter data will be exposed in a profile named `facter` in all templates. If set to a string, facter data will be exposed in a profile with the given name in all templates.
6
+ * Attempting to access a configuration variable from a profile or secret that does not exist using `#[]` will now raise an exception indicating the key being incorrectly accessed. If you need to access optionally present configuration variables from profiles or secrets use `#fetch(key, nil)`.
7
+
8
+ ## 0.3.0
9
+
10
+ NEW FEATURES:
11
+
12
+ * AWS Secrets Manager support, through an AWS AppConfig configuration. Values to pull from AWS Secrets Manager can be set using an "aws:secrets" key in any loaded AWS AppConfig JSON or YAML configuration. The value must be a dictionary. Keys in this dictionary will be exposed in the #secrets hash on the profile in templates. Values in this dictionary must be a dictionary containing at a minimum a `secret_id` key, which must be the id of an AWS Secrets Manager Secret. The dictionary may also contain a `version_id` or `version_stage` key to indicate which version of the secret to load, and a `content_type` key which may be one of `text/plan`, `application/json`, or `application/x-yaml` to indicate how the secret data should be parsed.
13
+
14
+ ## 0.2.1
15
+
16
+ BUG FIXES:
17
+
18
+ * restart_all actually works.
19
+
20
+ ## 0.2.0
21
+
22
+ NEW FEATURES:
23
+
24
+ * restart_all restart_mode for services to restart all running instances of an instantiated service.
25
+
26
+ ## 0.1.0
27
+
28
+ Initial release
data/Gemfile CHANGED
@@ -16,11 +16,4 @@
16
16
 
17
17
  source 'https://rubygems.org'
18
18
 
19
- gem 'aws-sdk-appconfig', '~> 1.18', require: false
20
- gem 'logsformyfamily', '~> 0.2', require: false
21
- gem 'lifecycle_vm', '~> 0.1.1', require: false
22
- gem 'ruby-dbus', '~> 0.16.0', require: false
23
- gem 'sd_notify', '~> 0.1', require: false
24
-
25
- gem 'rspec', '~> 3.10', group: :test, require: false
26
- gem 'simplecov', '~> 0.21', group: :test, require: false
19
+ gemspec
data/Gemfile.lock CHANGED
@@ -1,36 +1,55 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ config_o_mat (0.4.0)
5
+ aws-sdk-appconfig (~> 1.18)
6
+ aws-sdk-secretsmanager (~> 1.57)
7
+ facter (~> 4.2, >= 4.2.8)
8
+ lifecycle_vm (~> 0.1.1)
9
+ logsformyfamily (~> 0.2)
10
+ ruby-dbus (~> 0.16.0)
11
+ sd_notify (~> 0.1.1)
12
+
1
13
  GEM
2
14
  remote: https://rubygems.org/
3
15
  specs:
4
16
  aws-eventstream (1.2.0)
5
- aws-partitions (1.525.0)
6
- aws-sdk-appconfig (1.19.0)
7
- aws-sdk-core (~> 3, >= 3.122.0)
17
+ aws-partitions (1.574.0)
18
+ aws-sdk-appconfig (1.25.0)
19
+ aws-sdk-core (~> 3, >= 3.127.0)
8
20
  aws-sigv4 (~> 1.1)
9
- aws-sdk-core (3.122.0)
21
+ aws-sdk-core (3.130.0)
10
22
  aws-eventstream (~> 1, >= 1.0.2)
11
23
  aws-partitions (~> 1, >= 1.525.0)
12
24
  aws-sigv4 (~> 1.1)
13
25
  jmespath (~> 1.0)
26
+ aws-sdk-secretsmanager (1.59.0)
27
+ aws-sdk-core (~> 3, >= 3.127.0)
28
+ aws-sigv4 (~> 1.1)
14
29
  aws-sigv4 (1.4.0)
15
30
  aws-eventstream (~> 1, >= 1.0.2)
16
- diff-lcs (1.4.4)
31
+ diff-lcs (1.5.0)
17
32
  docile (1.4.0)
18
- jmespath (1.4.0)
19
- lifecycle_vm (0.1.2)
20
- logsformyfamily (0.2.3)
21
- rspec (3.10.0)
22
- rspec-core (~> 3.10.0)
23
- rspec-expectations (~> 3.10.0)
24
- rspec-mocks (~> 3.10.0)
25
- rspec-core (3.10.1)
26
- rspec-support (~> 3.10.0)
27
- rspec-expectations (3.10.1)
33
+ facter (4.2.9)
34
+ hocon (~> 1.3)
35
+ thor (>= 1.0.1, < 2.0)
36
+ hocon (1.3.1)
37
+ jmespath (1.6.1)
38
+ lifecycle_vm (0.1.3)
39
+ logsformyfamily (0.3.0)
40
+ rspec (3.11.0)
41
+ rspec-core (~> 3.11.0)
42
+ rspec-expectations (~> 3.11.0)
43
+ rspec-mocks (~> 3.11.0)
44
+ rspec-core (3.11.0)
45
+ rspec-support (~> 3.11.0)
46
+ rspec-expectations (3.11.0)
28
47
  diff-lcs (>= 1.2.0, < 2.0)
29
- rspec-support (~> 3.10.0)
30
- rspec-mocks (3.10.2)
48
+ rspec-support (~> 3.11.0)
49
+ rspec-mocks (3.11.0)
31
50
  diff-lcs (>= 1.2.0, < 2.0)
32
- rspec-support (~> 3.10.0)
33
- rspec-support (3.10.3)
51
+ rspec-support (~> 3.11.0)
52
+ rspec-support (3.11.0)
34
53
  ruby-dbus (0.16.0)
35
54
  sd_notify (0.1.1)
36
55
  simplecov (0.21.2)
@@ -38,19 +57,16 @@ GEM
38
57
  simplecov-html (~> 0.11)
39
58
  simplecov_json_formatter (~> 0.1)
40
59
  simplecov-html (0.12.3)
41
- simplecov_json_formatter (0.1.3)
60
+ simplecov_json_formatter (0.1.4)
61
+ thor (1.2.1)
42
62
 
43
63
  PLATFORMS
44
64
  ruby
45
65
 
46
66
  DEPENDENCIES
47
- aws-sdk-appconfig (~> 1.18)
48
- lifecycle_vm (~> 0.1.1)
49
- logsformyfamily (~> 0.2)
67
+ config_o_mat!
50
68
  rspec (~> 3.10)
51
- ruby-dbus (~> 0.16.0)
52
- sd_notify (~> 0.1)
53
- simplecov (~> 0.21)
69
+ simplecov (~> 0.21.2)
54
70
 
55
71
  BUNDLED WITH
56
72
  2.1.4
data/config_o_mat.gemspec CHANGED
@@ -39,10 +39,12 @@ 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-secretsmanager', '~> 1.57')
42
43
  spec.add_dependency('logsformyfamily', '~> 0.2')
43
44
  spec.add_dependency('lifecycle_vm', '~> 0.1.1')
44
45
  spec.add_dependency('ruby-dbus', '~> 0.16.0')
45
46
  spec.add_dependency('sd_notify', '~> 0.1.1')
47
+ spec.add_dependency('facter', ['~> 4.2', '>= 4.2.8'])
46
48
 
47
49
  spec.add_development_dependency('simplecov', '~> 0.21.2')
48
50
  spec.add_development_dependency('rspec', '~> 3.10')
@@ -25,7 +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, :systemd_interface
28
+ :region, :appconfig_client, :secretsmanager_client, :systemd_interface,
29
+ :secrets_loader_memory
29
30
 
30
31
  def initialize(
31
32
  argv: [],
@@ -55,7 +56,9 @@ module ConfigOMat
55
56
  retry_wait: 2,
56
57
  region: nil,
57
58
  appconfig_client: nil,
58
- systemd_interface: nil
59
+ secretsmanager_client: nil,
60
+ systemd_interface: nil,
61
+ secrets_loader_memory: nil
59
62
  )
60
63
  super()
61
64
 
@@ -86,7 +89,9 @@ module ConfigOMat
86
89
  @retry_wait = retry_wait
87
90
  @region = region
88
91
  @appconfig_client = appconfig_client
92
+ @secretsmanager_client = secretsmanager_client
89
93
  @systemd_interface = systemd_interface
94
+ @secrets_loader_memory = secrets_loader_memory
90
95
  end
91
96
  end
92
97
  end
@@ -17,18 +17,20 @@
17
17
  require 'lifecycle_vm/op_base'
18
18
 
19
19
  require 'aws-sdk-appconfig'
20
+ require 'aws-sdk-secretsmanager'
20
21
 
21
22
  module ConfigOMat
22
23
  module Op
23
- class ConnectToAppconfig < LifecycleVM::OpBase
24
+ class ConnectToAws < LifecycleVM::OpBase
24
25
  reads :region
25
- writes :appconfig_client
26
+ writes :appconfig_client, :secretsmanager_client
26
27
 
27
28
  def call
28
29
  client_opts = { logger: logger }
29
30
  client_opts[:region] = region if region
30
31
 
31
32
  self.appconfig_client = Aws::AppConfig::Client.new(client_opts)
33
+ self.secretsmanager_client = Aws::SecretsManager::Client.new(client_opts)
32
34
  end
33
35
  end
34
36
  end
@@ -16,47 +16,95 @@
16
16
 
17
17
  require 'lifecycle_vm/op_base'
18
18
 
19
+ require 'config_o_mat/secrets_loader'
20
+
19
21
  module ConfigOMat
20
22
  module Op
21
23
  class RefreshAllProfiles < LifecycleVM::OpBase
22
- reads :profile_defs, :applied_profiles, :client_id, :appconfig_client
23
- writes :profiles_to_apply, :last_refresh_time
24
+ reads :profile_defs, :applied_profiles, :client_id, :appconfig_client, :secrets_loader_memory, :secretsmanager_client
25
+ writes :profiles_to_apply, :last_refresh_time, :secrets_loader_memory
24
26
 
25
27
  def call
26
28
  self.profiles_to_apply = []
27
29
  self.last_refresh_time = Time.now.to_i
28
30
 
29
31
  profile_defs.each do |(profile_name, definition)|
30
- request = {
31
- application: definition.application, environment: definition.environment,
32
- configuration: definition.profile, client_id: client_id
33
- }
34
-
35
- current_version = applied_profiles&.fetch(profile_name, nil)&.version
36
- request[:client_configuration_version] = current_version if current_version
37
-
38
- response =
39
- begin
40
- appconfig_client.get_configuration(request)
41
- rescue StandardError => e
42
- error profile_name, e
43
- nil
44
- end
45
-
46
- next if response.nil?
47
-
48
- loaded_version = response.configuration_version
49
- next if current_version && loaded_version == current_version
50
-
51
- logger&.notice(
52
- :updated_profile,
53
- name: profile_name, previous_version: current_version, new_version: loaded_version
54
- )
55
-
56
- profiles_to_apply << LoadedProfile.new(
57
- profile_name, loaded_version, response.content.read, response.content_type
58
- )
32
+ if definition.kind_of?(ConfigOMat::Profile)
33
+ refresh_appconfig_profile(profile_name, definition)
34
+ elsif definition.kind_of?(ConfigOMat::FacterProfile)
35
+ refresh_facter_profile(profile_name)
36
+ else
37
+ error profile_name, "unknown profile type #{definition.class.name}"
38
+ end
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def refresh_facter_profile(profile_name)
45
+ current_version = applied_profiles&.fetch(profile_name, nil)&.version
46
+ new_profile = ConfigOMat::LoadedFacterProfile.new(profile_name)
47
+
48
+ return if new_profile.version == current_version
49
+
50
+ logger&.notice(
51
+ :updated_profile,
52
+ name: profile_name, previous_version: current_version, new_version: new_profile.version
53
+ )
54
+
55
+ profiles_to_apply << LoadedProfile.new(new_profile, nil)
56
+ end
57
+
58
+ def refresh_appconfig_profile(profile_name, definition)
59
+ request = {
60
+ application: definition.application, environment: definition.environment,
61
+ configuration: definition.profile, client_id: client_id
62
+ }
63
+
64
+ current_version = applied_profiles&.fetch(profile_name, nil)&.version
65
+ request[:client_configuration_version] = current_version if current_version
66
+
67
+ response =
68
+ begin
69
+ appconfig_client.get_configuration(request)
70
+ rescue StandardError => e
71
+ error profile_name, e
72
+ nil
73
+ end
74
+
75
+ return if response.nil?
76
+
77
+ loaded_version = response.configuration_version
78
+ return if current_version && loaded_version == current_version
79
+
80
+ logger&.notice(
81
+ :updated_profile,
82
+ name: profile_name, previous_version: current_version, new_version: loaded_version
83
+ )
84
+
85
+ profile = LoadedAppconfigProfile.new(
86
+ profile_name, loaded_version, response.content.read, response.content_type
87
+ )
88
+
89
+ loaded_secrets = nil
90
+
91
+ if !profile.secret_defs.empty?
92
+ self.secrets_loader_memory ||= ConfigOMat::SecretsLoader::Memory.new(secretsmanager_client: secretsmanager_client)
93
+ secrets_loader_memory.update_secret_defs_to_load(profile.secret_defs.values)
94
+
95
+ vm = ConfigOMat::SecretsLoader::VM.new(secrets_loader_memory).call
96
+
97
+ if vm.errors?
98
+ error :"#{profile_name}_secrets", vm.errors
99
+ return
100
+ end
101
+
102
+ loaded_secrets = secrets_loader_memory.loaded_secrets.each_with_object({}) do |(key, value), hash|
103
+ hash[value.name] = value
104
+ end
59
105
  end
106
+
107
+ profiles_to_apply << LoadedProfile.new(profile, loaded_secrets)
60
108
  end
61
109
  end
62
110
  end
@@ -19,10 +19,42 @@ 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
23
- writes :applying_profile
22
+ reads :profile_defs, :client_id, :applying_profile, :appconfig_client, :secretsmanager_client, :secrets_loader_memory
23
+ writes :applying_profile, :secrets_loader_memory
24
24
 
25
25
  def call
26
+ if applying_profile.loaded_profile_data.kind_of?(ConfigOMat::LoadedAppconfigProfile)
27
+ refresh_appconfig_profile
28
+ elsif applying_profile.loaded_profile_data.kind_of?(ConfigOMat::LoadedFacterProfile)
29
+ refresh_facter_profile
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def refresh_facter_profile
36
+ profile_name = applying_profile.name
37
+ profile_version = applying_profile.version
38
+ definition = profile_defs[profile_name]
39
+ new_profile = ConfigOMat::LoadedFacterProfile.new(profile_name)
40
+
41
+ if new_profile.version == profile_version
42
+ logger&.warning(
43
+ :no_update,
44
+ name: profile_name, version: profile_version
45
+ )
46
+ return
47
+ end
48
+
49
+ logger&.notice(
50
+ :updated_profile,
51
+ name: profile_name, previous_version: profile_version, new_version: new_profile.version
52
+ )
53
+
54
+ self.applying_profile = LoadedProfile.new(new_profile, nil)
55
+ end
56
+
57
+ def refresh_appconfig_profile
26
58
  profile_name = applying_profile.name
27
59
  profile_version = applying_profile.version
28
60
  definition = profile_defs[profile_name]
@@ -56,9 +88,29 @@ module ConfigOMat
56
88
  name: profile_name, previous_version: profile_version, new_version: loaded_version
57
89
  )
58
90
 
59
- self.applying_profile = LoadedProfile.new(
91
+ profile = LoadedAppconfigProfile.new(
60
92
  profile_name, loaded_version, response.content.read, response.content_type
61
93
  )
94
+
95
+ loaded_secrets = nil
96
+
97
+ if !profile.secret_defs.empty?
98
+ self.secrets_loader_memory ||= ConfigOMat::SecretsLoader::Memory.new(secretsmanager_client: secretsmanager_client)
99
+ secrets_loader_memory.update_secret_defs_to_load(profile.secret_defs.values)
100
+
101
+ vm = ConfigOMat::SecretsLoader::VM.new(secrets_loader_memory).call
102
+
103
+ if vm.errors?
104
+ error :"#{profile_name}_secrets", vm.errors
105
+ return
106
+ end
107
+
108
+ loaded_secrets = secrets_loader_memory.loaded_secrets.each_with_object({}) do |(key, value), hash|
109
+ hash[value.name] = value
110
+ end
111
+ end
112
+
113
+ self.applying_profile = LoadedProfile.new(profile, loaded_secrets)
62
114
  end
63
115
  end
64
116
  end
@@ -41,13 +41,11 @@ module ConfigOMat
41
41
  private
42
42
 
43
43
  def do_restart(service, service_def)
44
- unit = service_def.systemd_unit
45
-
46
- file_path = File.join(runtime_directory, unit + '.restart')
44
+ file_path = File.join(runtime_directory, service_def.restart_unit + '.restart')
47
45
 
48
46
  logger&.notice(
49
47
  :service_restart,
50
- name: service, systemd_unit: unit, touched_path: file_path
48
+ name: service, systemd_unit: service_def.systemd_unit, touched_path: file_path
51
49
  )
52
50
 
53
51
  FileUtils.touch(file_path)
@@ -41,9 +41,9 @@ module ConfigOMat
41
41
 
42
42
  on :reading_meta_config, do: Op::LoadMetaConfig, then: :compiling_templates
43
43
 
44
- on :compiling_templates, do: Op::CompileTemplates, then: :connecting_to_appconfig
44
+ on :compiling_templates, do: Op::CompileTemplates, then: :connecting_to_aws
45
45
 
46
- on :connecting_to_appconfig, do: Op::ConnectToAppconfig, then: :refreshing_profiles
46
+ on :connecting_to_aws, do: Op::ConnectToAws, then: :refreshing_profiles
47
47
 
48
48
  on :refreshing_profiles,
49
49
  do: Op::RefreshAllProfiles,
@@ -116,7 +116,7 @@ module ConfigOMat
116
116
  }
117
117
  }
118
118
 
119
- on :refreshing_profile, do: Op::RefreshProfile, then: :applying_profile
119
+ on :refreshing_profile, do: Op::RefreshProfile, then: :generating_templates
120
120
  end
121
121
  end
122
122
  end
@@ -27,10 +27,10 @@ module ConfigOMat
27
27
 
28
28
  def call
29
29
  grouped_restart_modes = service_defs.values.group_by(&:restart_mode)
30
- restarts = grouped_restart_modes[:restart]
30
+ restarts = grouped_restart_modes.fetch(:restart, []) + grouped_restart_modes.fetch(:restart_all, [])
31
31
  flip_flops = grouped_restart_modes[:flip_flop]
32
32
 
33
- enable_restarts(restarts) if restarts
33
+ enable_restarts(restarts) if !restarts.empty?
34
34
  enable_flip_flops(flip_flops) if flip_flops
35
35
 
36
36
  systemd_interface.daemon_reload
@@ -54,10 +54,9 @@ module ConfigOMat
54
54
  def enable_restarts(services)
55
55
  units = []
56
56
  services.each do |service|
57
- unit = service.systemd_unit
58
- units << unit
57
+ units << service.restart_unit
59
58
 
60
- write_dropin(unit, service)
59
+ write_dropin(service.systemd_unit, service)
61
60
  end
62
61
 
63
62
  systemd_interface.enable_restart_paths(units)
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021 Teak.io, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'lifecycle_vm/cond_base'
18
+
19
+ module ConfigOMat
20
+ module Cond
21
+ class RequiresLoad < LifecycleVM::CondBase
22
+ reads :loading_secret
23
+
24
+ def call
25
+ !loading_secret.nil?
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021 Teak.io, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'lifecycle_vm/cond_base'
18
+
19
+ module ConfigOMat
20
+ module Cond
21
+ class SecretsToLoad < LifecycleVM::CondBase
22
+ reads :secret_defs_to_load
23
+
24
+ def call
25
+ !secret_defs_to_load.empty?
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021 Teak.io, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ Dir[File.join(__dir__, 'cond', '*')].sort.each { |file| require file }
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021 Teak.io, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'lifecycle_vm'
18
+
19
+ module ConfigOMat
20
+ module SecretsLoader
21
+ class Memory < LifecycleVM::Memory
22
+ attr_reader :secretsmanager_client
23
+
24
+ attr_accessor :secrets_cache, :secret_defs_to_load, :loaded_secrets, :loading_secret
25
+
26
+ def initialize(
27
+ secretsmanager_client: nil,
28
+ secret_defs_to_load: [],
29
+ secrets_cache: {},
30
+ loading_secret: nil,
31
+ logger: nil
32
+ )
33
+ @secretsmanager_client = secretsmanager_client
34
+ @secret_defs_to_load = secret_defs_to_load
35
+ @secrets_cache = secrets_cache
36
+ @loading_secret = loading_secret
37
+ @loaded_secrets = {}
38
+ @logger = logger
39
+ end
40
+
41
+ def update_secret_defs_to_load(defs)
42
+ @loaded_secrets = {}
43
+ @secret_defs_to_load = defs
44
+ self
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021 Teak.io, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'lifecycle_vm/op_base'
18
+
19
+ module ConfigOMat
20
+ module Op
21
+ class CheckCache < LifecycleVM::OpBase
22
+ reads :loading_secret, :secrets_cache, :loaded_secrets
23
+ writes :loading_secret, :loaded_secrets
24
+
25
+ def call
26
+ # If we're referencing a secret by its version stage, never cache it, since these can be
27
+ # updated out from under us.
28
+ return unless loading_secret.version_stage.nil? || loading_secret.version_stage.empty?
29
+
30
+ cached_secret = secrets_cache[loading_secret.secret_id]
31
+
32
+ if cached_secret && cached_secret.version_id == loading_secret.version_id
33
+ logger&.info(:cached_secret, name: loading_secret.name, version_id: loading_secret.version_id)
34
+ loaded_secrets[loading_secret] = cached_secret
35
+ self.loading_secret = nil
36
+ elsif cached_secret
37
+ logger&.info(
38
+ :invalid_cached_secret,
39
+ name: loading_secret.name,
40
+ expected_version_id: loading_secret.version_id,
41
+ cached_version_id: cached_secret.version_id
42
+ )
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021 Teak.io, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'lifecycle_vm/op_base'
18
+
19
+ require 'config_o_mat/shared/types'
20
+
21
+ module ConfigOMat
22
+ module Op
23
+ class LoadSecret < LifecycleVM::OpBase
24
+ reads :loading_secret, :secrets_cache, :loaded_secrets, :secretsmanager_client
25
+ writes :loading_secret, :loaded_secrets, :secrets_cache
26
+
27
+ def call
28
+ opts = {
29
+ secret_id: loading_secret.secret_id
30
+ }
31
+
32
+ if loading_secret.version_id
33
+ opts[:version_id] = loading_secret.version_id
34
+ else
35
+ opts[:version_stage] = loading_secret.version_stage
36
+ end
37
+
38
+ response =
39
+ begin
40
+ secretsmanager_client.get_secret_value(opts)
41
+ rescue StandardError => e
42
+ error loading_secret.name, e
43
+ nil
44
+ end
45
+
46
+ return if response.nil? || errors?
47
+
48
+ loaded_secret = LoadedSecret.new(
49
+ loading_secret.name, loading_secret.secret_id, response.version_id,
50
+ response.secret_string, loading_secret.content_type
51
+ )
52
+
53
+ logger&.info(
54
+ :loaded_secret, name: loading_secret.name, arn: response.arn,
55
+ version_id: response.version_id
56
+ )
57
+
58
+ begin
59
+ loaded_secret.validate!
60
+ rescue StandardError => e
61
+ logger&.error(
62
+ :invalid_secret, name: loading_secret.name, arn: response.arn,
63
+ version_id: response.version_id, errors: e
64
+ )
65
+ error loaded_secret.name, e
66
+ end
67
+
68
+ return if errors?
69
+
70
+ secrets_cache[loading_secret.secret_id] = loaded_secret
71
+ loaded_secrets[loading_secret] = loaded_secret
72
+ self.loading_secret = nil
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021 Teak.io, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'lifecycle_vm/op_base'
18
+
19
+ module ConfigOMat
20
+ module Op
21
+ class StageOneSecret < LifecycleVM::OpBase
22
+ reads :secret_defs_to_load
23
+ writes :loading_secret, :secret_defs_to_load
24
+
25
+ def call
26
+ self.loading_secret = secret_defs_to_load.pop
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021 Teak.io, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ Dir[File.join(__dir__, 'op', '*')].sort.each { |file| require file }
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021 Teak.io, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'lifecycle_vm'
18
+
19
+ require 'config_o_mat/secrets_loader/op'
20
+ require 'config_o_mat/secrets_loader/cond'
21
+ require 'config_o_mat/secrets_loader/memory'
22
+
23
+ module ConfigOMat
24
+ module SecretsLoader
25
+ class VM < LifecycleVM::VM
26
+ memory_class ConfigOMat::SecretsLoader::Memory
27
+
28
+ on :start, then: {
29
+ case: Cond::SecretsToLoad,
30
+ when: {
31
+ true => {
32
+ do: Op::StageOneSecret,
33
+ then: :check_cache
34
+ },
35
+ false => {
36
+ then: :exit
37
+ }
38
+ }
39
+ }
40
+
41
+ on :check_cache, do: Op::CheckCache, then: {
42
+ case: Cond::RequiresLoad,
43
+ when: {
44
+ true => :load_secret,
45
+ false => :start
46
+ }
47
+ }
48
+
49
+ on :load_secret, do: Op::LoadSecret, then: :start
50
+ end
51
+ end
52
+ end
@@ -126,6 +126,16 @@ module ConfigOMat
126
126
  self.template_defs = instantiate.call(:templates, Template)
127
127
  self.profile_defs = instantiate.call(:profiles, Profile)
128
128
 
129
+ facter = merged_config[:facter]
130
+ if facter
131
+ facter_key = facter.kind_of?(String) ? facter.to_sym : :facter
132
+ if profile_defs.key?(facter_key)
133
+ error :facter, "conflicts with profile #{facter_key}"
134
+ else
135
+ profile_defs[facter_key] = ConfigOMat::FacterProfile.new
136
+ end
137
+ end
138
+
129
139
  self.logger = LogsForMyFamily::Logger.new if !logger
130
140
 
131
141
  log_type = merged_config[:log_type]&.to_sym || :stdout
@@ -14,6 +14,8 @@
14
14
  # See the License for the specific language governing permissions and
15
15
  # limitations under the License.
16
16
 
17
+ require 'facter'
18
+
17
19
  require 'json'
18
20
  require 'yaml'
19
21
  require 'digest'
@@ -87,23 +89,21 @@ module ConfigOMat
87
89
  class Service < ConfigItem
88
90
  RESTART_MODES = %i[restart flip_flop restart_all].freeze
89
91
 
90
- attr_reader :systemd_unit, :restart_mode, :templates
92
+ attr_reader :systemd_unit, :restart_mode, :templates, :restart_unit
91
93
 
92
94
  def initialize(opts)
93
95
  @systemd_unit = (opts[:systemd_unit] || '')
94
96
  @restart_mode = opts[:restart_mode]&.to_sym
95
97
  @templates = opts[:templates]
96
98
 
97
- if @restart_mode == :flip_flop && !@systemd_unit.include?('@')
99
+ if (@restart_mode == :flip_flop || @restart_mode == :restart_all) && !@systemd_unit.include?('@')
98
100
  @systemd_unit = "#{@systemd_unit}@"
99
- elsif @restart_mode == :restart_all
100
- if @systemd_unit.include?('@')
101
- if !@systemd_unit.end_with?('\\x2a')
102
- @systemd_unit = "#{@systemd_unit}\\x2a"
103
- end
104
- else
105
- @systemd_unit = "#{@systemd_unit}@\\x2a"
106
- end
101
+ end
102
+
103
+ @restart_unit = @systemd_unit
104
+
105
+ if @restart_mode == :restart_all
106
+ @restart_unit = "#{@restart_unit}\\x2a"
107
107
  end
108
108
  end
109
109
 
@@ -123,7 +123,7 @@ module ConfigOMat
123
123
  error :systemd_unit, 'must not be a naked instantiated unit when restart_mode=restart'
124
124
  end
125
125
 
126
- if restart_mode == :restart_all && !@systemd_unit.end_with?('@\\x2a')
126
+ if restart_mode == :restart_all && !@systemd_unit.end_with?('@')
127
127
  error :systemd_unit, 'must not be an instantiated unit when restart_mode=restart_all'
128
128
  end
129
129
  end
@@ -189,9 +189,86 @@ module ConfigOMat
189
189
  end
190
190
  end
191
191
 
192
- class LoadedProfile < ConfigItem
192
+ class FacterProfile < ConfigItem
193
+ end
194
+
195
+ class LoadedFacterProfile < ConfigItem
196
+ CLEAR_FROM_FACTER = [
197
+ "memoryfree", "memoryfree_mb", "load_averages", "uptime", "system_uptime", "uptime_seconds", "uptime_hours", "uptime_days",
198
+ {"ec2_metadata" => ["identity-credentials"]},
199
+ {"memory" => [{"system" => ["capacity", "available_bytes", "used", "used_bytes", "available"] }] }
200
+ ].freeze
201
+
193
202
  attr_reader :name, :version, :contents
194
203
 
204
+ def initialize(name)
205
+ @name = name
206
+ load_from_facter
207
+ @version = contents.hash
208
+ end
209
+
210
+ def validate
211
+ error :name, 'must be present' if @name.nil? || @name.empty?
212
+ error :contents, 'must be present' if @contents.nil? || @contents.empty?
213
+ error :contents, 'must be a hash' if !@contents.kind_of?(Hash)
214
+ end
215
+
216
+ def hash
217
+ @name.hash ^ @version
218
+ end
219
+
220
+ def to_h
221
+ @contents
222
+ end
223
+
224
+ def eql?(other)
225
+ return false if !super(other)
226
+ return false if other.version != version || other.name != name
227
+ true
228
+ end
229
+
230
+ private
231
+
232
+ def load_from_facter
233
+ Facter.clear
234
+ # This is to work around a bug in Facter wherein it fails to invalidate a second cache of the
235
+ # IMDSv2 token.
236
+ Facter::Resolvers::Ec2.instance_variable_set(:@v2_token, nil)
237
+ new_facts = Facter.to_hash
238
+ clear(new_facts, CLEAR_FROM_FACTER)
239
+ transform(new_facts)
240
+ @contents = new_facts
241
+ end
242
+
243
+ def clear(hash, diffs)
244
+ diffs.each do |diff|
245
+ if diff.kind_of?(Hash)
246
+ diff.each do |(key, values)|
247
+ clear(hash[key], values) if hash.key?(key)
248
+ end
249
+ elsif hash
250
+ hash.delete(diff)
251
+ end
252
+ end
253
+ end
254
+
255
+ def transform(hash)
256
+ return unless hash.kind_of?(Hash)
257
+ hash.transform_keys!(&:to_sym)
258
+ hash.default_proc = proc { |hash, key| raise KeyError.new("No key #{key.inspect} in profile #{name}", key: key, receiver: hash) }
259
+ hash.each_value do |value|
260
+ if value.kind_of?(Hash)
261
+ transform(value)
262
+ elsif value.kind_of?(Array)
263
+ value.each { |v| transform(v) }
264
+ end
265
+ end
266
+ end
267
+ end
268
+
269
+ class LoadedAppconfigProfile < ConfigItem
270
+ attr_reader :name, :version, :contents, :secret_defs
271
+
195
272
  PARSERS = {
196
273
  'text/plain' => proc { |str| str },
197
274
  'application/json' => proc { |str| JSON.parse(str, symbolize_names: true) },
@@ -201,12 +278,19 @@ module ConfigOMat
201
278
  def initialize(name, version, contents, content_type)
202
279
  @name = name
203
280
  @version = version
281
+ @secret_defs = {}
204
282
 
205
283
  parser = PARSERS[content_type]
206
284
 
207
285
  if parser
208
286
  begin
209
287
  @contents = parser.call(contents)
288
+ if @contents.kind_of?(Hash)
289
+ parse_secrets
290
+ @contents.default_proc = proc do |hash, key|
291
+ raise KeyError.new("No key #{key.inspect} in profile #{name}", key: key, receiver: hash)
292
+ end
293
+ end
210
294
  rescue StandardError => e
211
295
  error :contents, e
212
296
  end
@@ -235,6 +319,138 @@ module ConfigOMat
235
319
  return false if other.version != version || other.contents != contents || other.name != name
236
320
  true
237
321
  end
322
+
323
+ private
324
+
325
+ def parse_secrets
326
+ secret_entries = @contents.fetch(:"aws:secrets", nil)
327
+ return if secret_entries.nil?
328
+
329
+ error :contents_secrets, 'must be a dictionary' if !secret_entries.kind_of?(Hash)
330
+
331
+ secret_entries.each do |(secret_name, secret_conf)|
332
+ secret_def = Secret.new(secret_name, secret_conf)
333
+ secret_def.validate
334
+ error :"contents_secrets_#{secret_name}", secret_def.errors if secret_def.errors?
335
+
336
+ @secret_defs[secret_name] = secret_def
337
+ end
338
+ end
339
+ end
340
+
341
+ class Secret < ConfigItem
342
+ VALID_CONTENT_TYPES = LoadedAppconfigProfile::PARSERS.keys.freeze
343
+
344
+ attr_reader :name, :secret_id, :version_id, :version_stage, :content_type
345
+
346
+ def initialize(name, opts)
347
+ @name = name
348
+ @secret_id = opts[:secret_id]
349
+ @version_id = opts[:version_id]
350
+ @version_stage = opts[:version_stage]
351
+ @content_type = opts[:content_type]&.downcase
352
+
353
+ if (@version_id.nil? || @version_id.empty?) && (@version_stage.nil? || @version_stage.empty?)
354
+ @version_stage = 'AWSCURRENT'
355
+ end
356
+
357
+ @content_type ||= 'application/json'
358
+ end
359
+
360
+ def validate
361
+ error :secret_id, 'must be present' if @secret_id.nil? || @secret_id.empty?
362
+ error :content_type, "must be one of #{VALID_CONTENT_TYPES}" unless VALID_CONTENT_TYPES.include?(@content_type)
363
+ end
364
+
365
+ def hash
366
+ secret_id.hash ^ version_id.hash ^ version_stage.hash & content_type.hash
367
+ end
368
+
369
+ def eql?(other)
370
+ return false if !super(other)
371
+ if other.name != name ||
372
+ other.secret_id != secret_id ||
373
+ other.version_id != version_id ||
374
+ other.version_stage != version_stage ||
375
+ other.content_type != content_type
376
+ return false
377
+ end
378
+ true
379
+ end
380
+ end
381
+
382
+ class LoadedSecret < ConfigItem
383
+ attr_reader :name, :secret_id, :version_id, :contents
384
+
385
+ def initialize(name, secret_id, version_id, secret_string, content_type)
386
+ @name = name
387
+ @secret_id = secret_id
388
+ @version_id = version_id
389
+
390
+ begin
391
+ @contents = LoadedAppconfigProfile::PARSERS[content_type].call(secret_string)
392
+ if @contents.kind_of?(Hash)
393
+ @contents.default_proc = proc do |hash, key|
394
+ raise KeyError.new("No key #{key.inspect} in secret #{name}", key: key, receiver: hash)
395
+ end
396
+ end
397
+ rescue StandardError => e
398
+ error :contents, e
399
+ end
400
+ end
401
+
402
+ def validate
403
+ # Since name and version_id are coming from AWS and must be present, I'm not going to check
404
+ # them here.
405
+ end
406
+
407
+ def hash
408
+ @name.hash ^ @secret_id.hash ^ @version_id.hash ^ @contents.hash
409
+ end
410
+
411
+ def eql?(other)
412
+ return false if !super(other)
413
+ if other.name != name ||
414
+ other.secret_id != secret_id ||
415
+ other.version_id != version_id ||
416
+ other.contents != contents
417
+ return false
418
+ end
419
+ true
420
+ end
421
+ end
422
+
423
+
424
+ class LoadedProfile < ConfigItem
425
+ extend Forwardable
426
+
427
+ attr_reader :secrets, :loaded_profile_data
428
+
429
+ def_delegators :@loaded_profile_data, :name, :version, :contents
430
+
431
+ def initialize(loaded_profile_data, secrets)
432
+ @loaded_profile_data = loaded_profile_data
433
+ @secrets = secrets || {}
434
+
435
+ @errors = @loaded_profile_data.errors if @loaded_profile_data.errors?
436
+ end
437
+
438
+ def validate
439
+ end
440
+
441
+ def hash
442
+ @loaded_profile_data.hash ^ @secrets.hash
443
+ end
444
+
445
+ def to_h
446
+ contents
447
+ end
448
+
449
+ def eql?(other)
450
+ return false if !super(other)
451
+ return false if other.loaded_profile_data != @loaded_profile_data || other.secrets != @secrets
452
+ true
453
+ end
238
454
  end
239
455
 
240
456
  class GeneratedTemplate < ConfigItem
@@ -15,5 +15,5 @@
15
15
  # limitations under the License.
16
16
 
17
17
  module ConfigOMat
18
- VERSION = "0.2.0"
18
+ VERSION = "0.4.0"
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.2.0
4
+ version: 0.4.0
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-02-15 00:00:00.000000000 Z
11
+ date: 2022-04-07 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-secretsmanager
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.57'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.57'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: logsformyfamily
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +94,26 @@ dependencies:
80
94
  - - "~>"
81
95
  - !ruby/object:Gem::Version
82
96
  version: 0.1.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: facter
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '4.2'
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: 4.2.8
107
+ type: :runtime
108
+ prerelease: false
109
+ version_requirements: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - "~>"
112
+ - !ruby/object:Gem::Version
113
+ version: '4.2'
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: 4.2.8
83
117
  - !ruby/object:Gem::Dependency
84
118
  name: simplecov
85
119
  requirement: !ruby/object:Gem::Requirement
@@ -121,6 +155,7 @@ files:
121
155
  - ".rspec"
122
156
  - ".ruby-gemset"
123
157
  - ".ruby-version"
158
+ - CHANGELOG.md
124
159
  - Gemfile
125
160
  - Gemfile.lock
126
161
  - LICENSE
@@ -144,7 +179,7 @@ files:
144
179
  - lib/config_o_mat/configurator/op/apply_all_profiles.rb
145
180
  - lib/config_o_mat/configurator/op/commit_staged_profile.rb
146
181
  - lib/config_o_mat/configurator/op/compile_templates.rb
147
- - lib/config_o_mat/configurator/op/connect_to_appconfig.rb
182
+ - lib/config_o_mat/configurator/op/connect_to_aws.rb
148
183
  - lib/config_o_mat/configurator/op/generate_all_templates.rb
149
184
  - lib/config_o_mat/configurator/op/next_tick.rb
150
185
  - lib/config_o_mat/configurator/op/notify_systemd_start.rb
@@ -170,6 +205,15 @@ files:
170
205
  - lib/config_o_mat/meta_configurator/op.rb
171
206
  - lib/config_o_mat/meta_configurator/op/generate_systemd_config.rb
172
207
  - lib/config_o_mat/meta_configurator/op/parse_meta_cli.rb
208
+ - lib/config_o_mat/secrets_loader.rb
209
+ - lib/config_o_mat/secrets_loader/cond.rb
210
+ - lib/config_o_mat/secrets_loader/cond/requires_load.rb
211
+ - lib/config_o_mat/secrets_loader/cond/secrets_to_load.rb
212
+ - lib/config_o_mat/secrets_loader/memory.rb
213
+ - lib/config_o_mat/secrets_loader/op.rb
214
+ - lib/config_o_mat/secrets_loader/op/check_cache.rb
215
+ - lib/config_o_mat/secrets_loader/op/load_secret.rb
216
+ - lib/config_o_mat/secrets_loader/op/stage_one_secret.rb
173
217
  - lib/config_o_mat/shared/cond.rb
174
218
  - lib/config_o_mat/shared/cond/early_exit.rb
175
219
  - lib/config_o_mat/shared/op.rb