config_o_mat 0.2.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/CHANGELOG.md +28 -0
- data/Gemfile +1 -8
- data/Gemfile.lock +42 -26
- data/config_o_mat.gemspec +2 -0
- data/lib/config_o_mat/configurator/memory.rb +7 -2
- data/lib/config_o_mat/configurator/op/{connect_to_appconfig.rb → connect_to_aws.rb} +4 -2
- data/lib/config_o_mat/configurator/op/refresh_all_profiles.rb +79 -31
- data/lib/config_o_mat/configurator/op/refresh_profile.rb +55 -3
- data/lib/config_o_mat/configurator/op/reload_one_service.rb +2 -4
- data/lib/config_o_mat/configurator.rb +3 -3
- data/lib/config_o_mat/meta_configurator/op/generate_systemd_config.rb +4 -5
- data/lib/config_o_mat/secrets_loader/cond/requires_load.rb +29 -0
- data/lib/config_o_mat/secrets_loader/cond/secrets_to_load.rb +29 -0
- data/lib/config_o_mat/secrets_loader/cond.rb +17 -0
- data/lib/config_o_mat/secrets_loader/memory.rb +48 -0
- data/lib/config_o_mat/secrets_loader/op/check_cache.rb +47 -0
- data/lib/config_o_mat/secrets_loader/op/load_secret.rb +76 -0
- data/lib/config_o_mat/secrets_loader/op/stage_one_secret.rb +30 -0
- data/lib/config_o_mat/secrets_loader/op.rb +17 -0
- data/lib/config_o_mat/secrets_loader.rb +52 -0
- data/lib/config_o_mat/shared/op/load_meta_config.rb +10 -0
- data/lib/config_o_mat/shared/types.rb +228 -12
- data/lib/config_o_mat/version.rb +1 -1
- metadata +47 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 26063499446b5cf904e29c5ef7e147c845bc6a2e72e8e37b32148c35f8b4eb96
|
4
|
+
data.tar.gz: 0cce14aaef3a7b479bce31bbe8dfeeb4f4159153c999de199227aa8dd49b9602
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94869014f18070dfcb57e3907ad2c16b24d06f5693a353a7979f57e7b5333813ec9a4c458584173ec1622240c3fc6126dae0895ac53facef31fad11775879388
|
7
|
+
data.tar.gz: eff0f317b20431cb779765aa04af447a0402fc5ede9f46c69ddcbdc5403b11b85b79989e7315cb0f21262cad1818b06bb6bccf1b7e21a91638e91aba91db53ec
|
data/.gitignore
CHANGED
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
|
-
|
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.
|
6
|
-
aws-sdk-appconfig (1.
|
7
|
-
aws-sdk-core (~> 3, >= 3.
|
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.
|
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.
|
31
|
+
diff-lcs (1.5.0)
|
17
32
|
docile (1.4.0)
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
rspec
|
26
|
-
rspec-
|
27
|
-
|
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.
|
30
|
-
rspec-mocks (3.
|
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.
|
33
|
-
rspec-support (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.
|
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
|
-
|
48
|
-
lifecycle_vm (~> 0.1.1)
|
49
|
-
logsformyfamily (~> 0.2)
|
67
|
+
config_o_mat!
|
50
68
|
rspec (~> 3.10)
|
51
|
-
|
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
|
-
|
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
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
-
|
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
|
-
|
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:
|
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: :
|
44
|
+
on :compiling_templates, do: Op::CompileTemplates, then: :connecting_to_aws
|
45
45
|
|
46
|
-
on :
|
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: :
|
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
|
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
|
-
|
58
|
-
units << unit
|
57
|
+
units << service.restart_unit
|
59
58
|
|
60
|
-
write_dropin(
|
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
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
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?('
|
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
|
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
|
data/lib/config_o_mat/version.rb
CHANGED
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
|
+
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-
|
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/
|
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
|