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 +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
|