config_o_mat 0.2.1 → 0.3.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/Gemfile +1 -8
- data/Gemfile.lock +34 -24
- data/config_o_mat.gemspec +1 -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 +25 -3
- data/lib/config_o_mat/configurator/op/refresh_profile.rb +23 -3
- data/lib/config_o_mat/configurator.rb +3 -3
- 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/types.rb +131 -2
- data/lib/config_o_mat/version.rb +1 -1
- metadata +26 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bd094e1ed00e6019a324570d47475448e924c6c31abe2c9fb7c31cd37064ddfe
|
|
4
|
+
data.tar.gz: e9712d360ae7da7adf5c15142bf3a2365465832071998790ebd8e5a089429f9a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1a63b431f2fe289c0defaadc88a7105910d0198bf8dc3862da8effb908953f2b480687cc7750fc4f282cc410dcc6f3dc4e8d54659523e909c07f5e8e8059a1e3
|
|
7
|
+
data.tar.gz: 8763d1eff5f21f0cb2cfc603595fb8b4e198dad30ccbb8f2727ee930df1438d8b5a5c5075de674e269b99a25313f6648be160b72ed6caa3d921d595af7c8fb83
|
data/.gitignore
CHANGED
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,50 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
config_o_mat (0.2.1)
|
|
5
|
+
aws-sdk-appconfig (~> 1.18)
|
|
6
|
+
aws-sdk-secretsmanager (~> 1.57)
|
|
7
|
+
lifecycle_vm (~> 0.1.1)
|
|
8
|
+
logsformyfamily (~> 0.2)
|
|
9
|
+
ruby-dbus (~> 0.16.0)
|
|
10
|
+
sd_notify (~> 0.1.1)
|
|
11
|
+
|
|
1
12
|
GEM
|
|
2
13
|
remote: https://rubygems.org/
|
|
3
14
|
specs:
|
|
4
15
|
aws-eventstream (1.2.0)
|
|
5
|
-
aws-partitions (1.
|
|
6
|
-
aws-sdk-appconfig (1.
|
|
7
|
-
aws-sdk-core (~> 3, >= 3.
|
|
16
|
+
aws-partitions (1.554.0)
|
|
17
|
+
aws-sdk-appconfig (1.24.0)
|
|
18
|
+
aws-sdk-core (~> 3, >= 3.126.0)
|
|
8
19
|
aws-sigv4 (~> 1.1)
|
|
9
|
-
aws-sdk-core (3.
|
|
20
|
+
aws-sdk-core (3.126.1)
|
|
10
21
|
aws-eventstream (~> 1, >= 1.0.2)
|
|
11
22
|
aws-partitions (~> 1, >= 1.525.0)
|
|
12
23
|
aws-sigv4 (~> 1.1)
|
|
13
24
|
jmespath (~> 1.0)
|
|
25
|
+
aws-sdk-secretsmanager (1.57.0)
|
|
26
|
+
aws-sdk-core (~> 3, >= 3.126.0)
|
|
27
|
+
aws-sigv4 (~> 1.1)
|
|
14
28
|
aws-sigv4 (1.4.0)
|
|
15
29
|
aws-eventstream (~> 1, >= 1.0.2)
|
|
16
|
-
diff-lcs (1.
|
|
30
|
+
diff-lcs (1.5.0)
|
|
17
31
|
docile (1.4.0)
|
|
18
|
-
jmespath (1.
|
|
32
|
+
jmespath (1.6.0)
|
|
19
33
|
lifecycle_vm (0.1.2)
|
|
20
34
|
logsformyfamily (0.2.3)
|
|
21
|
-
rspec (3.
|
|
22
|
-
rspec-core (~> 3.
|
|
23
|
-
rspec-expectations (~> 3.
|
|
24
|
-
rspec-mocks (~> 3.
|
|
25
|
-
rspec-core (3.
|
|
26
|
-
rspec-support (~> 3.
|
|
27
|
-
rspec-expectations (3.
|
|
35
|
+
rspec (3.11.0)
|
|
36
|
+
rspec-core (~> 3.11.0)
|
|
37
|
+
rspec-expectations (~> 3.11.0)
|
|
38
|
+
rspec-mocks (~> 3.11.0)
|
|
39
|
+
rspec-core (3.11.0)
|
|
40
|
+
rspec-support (~> 3.11.0)
|
|
41
|
+
rspec-expectations (3.11.0)
|
|
28
42
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
29
|
-
rspec-support (~> 3.
|
|
30
|
-
rspec-mocks (3.
|
|
43
|
+
rspec-support (~> 3.11.0)
|
|
44
|
+
rspec-mocks (3.11.0)
|
|
31
45
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
32
|
-
rspec-support (~> 3.
|
|
33
|
-
rspec-support (3.
|
|
46
|
+
rspec-support (~> 3.11.0)
|
|
47
|
+
rspec-support (3.11.0)
|
|
34
48
|
ruby-dbus (0.16.0)
|
|
35
49
|
sd_notify (0.1.1)
|
|
36
50
|
simplecov (0.21.2)
|
|
@@ -38,19 +52,15 @@ GEM
|
|
|
38
52
|
simplecov-html (~> 0.11)
|
|
39
53
|
simplecov_json_formatter (~> 0.1)
|
|
40
54
|
simplecov-html (0.12.3)
|
|
41
|
-
simplecov_json_formatter (0.1.
|
|
55
|
+
simplecov_json_formatter (0.1.4)
|
|
42
56
|
|
|
43
57
|
PLATFORMS
|
|
44
58
|
ruby
|
|
45
59
|
|
|
46
60
|
DEPENDENCIES
|
|
47
|
-
|
|
48
|
-
lifecycle_vm (~> 0.1.1)
|
|
49
|
-
logsformyfamily (~> 0.2)
|
|
61
|
+
config_o_mat!
|
|
50
62
|
rspec (~> 3.10)
|
|
51
|
-
|
|
52
|
-
sd_notify (~> 0.1)
|
|
53
|
-
simplecov (~> 0.21)
|
|
63
|
+
simplecov (~> 0.21.2)
|
|
54
64
|
|
|
55
65
|
BUNDLED WITH
|
|
56
66
|
2.1.4
|
data/config_o_mat.gemspec
CHANGED
|
@@ -39,6 +39,7 @@ Gem::Specification.new do |spec|
|
|
|
39
39
|
spec.executables = ["config_o_mat-configurator", "config_o_mat-meta_configurator"]
|
|
40
40
|
|
|
41
41
|
spec.add_dependency('aws-sdk-appconfig', '~> 1.18')
|
|
42
|
+
spec.add_dependency('aws-sdk-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')
|
|
@@ -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,11 +16,13 @@
|
|
|
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 = []
|
|
@@ -53,9 +55,29 @@ module ConfigOMat
|
|
|
53
55
|
name: profile_name, previous_version: current_version, new_version: loaded_version
|
|
54
56
|
)
|
|
55
57
|
|
|
56
|
-
|
|
58
|
+
profile = LoadedAppconfigProfile.new(
|
|
57
59
|
profile_name, loaded_version, response.content.read, response.content_type
|
|
58
60
|
)
|
|
61
|
+
|
|
62
|
+
loaded_secrets = nil
|
|
63
|
+
|
|
64
|
+
if !profile.secret_defs.empty?
|
|
65
|
+
self.secrets_loader_memory ||= ConfigOMat::SecretsLoader::Memory.new(secretsmanager_client: secretsmanager_client)
|
|
66
|
+
secrets_loader_memory.update_secret_defs_to_load(profile.secret_defs.values)
|
|
67
|
+
|
|
68
|
+
vm = ConfigOMat::SecretsLoader::VM.new(secrets_loader_memory).call
|
|
69
|
+
|
|
70
|
+
if vm.errors?
|
|
71
|
+
error :"#{profile_name}_secrets", vm.errors
|
|
72
|
+
next
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
loaded_secrets = secrets_loader_memory.loaded_secrets.each_with_object({}) do |(key, value), hash|
|
|
76
|
+
hash[value.name] = value
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
profiles_to_apply << LoadedProfile.new(profile, loaded_secrets)
|
|
59
81
|
end
|
|
60
82
|
end
|
|
61
83
|
end
|
|
@@ -19,8 +19,8 @@ require 'lifecycle_vm/op_base'
|
|
|
19
19
|
module ConfigOMat
|
|
20
20
|
module Op
|
|
21
21
|
class RefreshProfile < LifecycleVM::OpBase
|
|
22
|
-
reads :profile_defs, :client_id, :applying_profile, :appconfig_client
|
|
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
26
|
profile_name = applying_profile.name
|
|
@@ -56,9 +56,29 @@ module ConfigOMat
|
|
|
56
56
|
name: profile_name, previous_version: profile_version, new_version: loaded_version
|
|
57
57
|
)
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
profile = LoadedAppconfigProfile.new(
|
|
60
60
|
profile_name, loaded_version, response.content.read, response.content_type
|
|
61
61
|
)
|
|
62
|
+
|
|
63
|
+
loaded_secrets = nil
|
|
64
|
+
|
|
65
|
+
if !profile.secret_defs.empty?
|
|
66
|
+
self.secrets_loader_memory ||= ConfigOMat::SecretsLoader::Memory.new(secretsmanager_client: secretsmanager_client)
|
|
67
|
+
secrets_loader_memory.update_secret_defs_to_load(profile.secret_defs.values)
|
|
68
|
+
|
|
69
|
+
vm = ConfigOMat::SecretsLoader::VM.new(secrets_loader_memory).call
|
|
70
|
+
|
|
71
|
+
if vm.errors?
|
|
72
|
+
error :"#{profile_name}_secrets", vm.errors
|
|
73
|
+
return
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
loaded_secrets = secrets_loader_memory.loaded_secrets.each_with_object({}) do |(key, value), hash|
|
|
77
|
+
hash[value.name] = value
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
self.applying_profile = LoadedProfile.new(profile, loaded_secrets)
|
|
62
82
|
end
|
|
63
83
|
end
|
|
64
84
|
end
|
|
@@ -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
|
|
@@ -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
|
|
@@ -187,8 +187,8 @@ module ConfigOMat
|
|
|
187
187
|
end
|
|
188
188
|
end
|
|
189
189
|
|
|
190
|
-
class
|
|
191
|
-
attr_reader :name, :version, :contents
|
|
190
|
+
class LoadedAppconfigProfile < ConfigItem
|
|
191
|
+
attr_reader :name, :version, :contents, :secret_defs
|
|
192
192
|
|
|
193
193
|
PARSERS = {
|
|
194
194
|
'text/plain' => proc { |str| str },
|
|
@@ -199,12 +199,14 @@ module ConfigOMat
|
|
|
199
199
|
def initialize(name, version, contents, content_type)
|
|
200
200
|
@name = name
|
|
201
201
|
@version = version
|
|
202
|
+
@secret_defs = {}
|
|
202
203
|
|
|
203
204
|
parser = PARSERS[content_type]
|
|
204
205
|
|
|
205
206
|
if parser
|
|
206
207
|
begin
|
|
207
208
|
@contents = parser.call(contents)
|
|
209
|
+
parse_secrets if @contents.kind_of?(Hash)
|
|
208
210
|
rescue StandardError => e
|
|
209
211
|
error :contents, e
|
|
210
212
|
end
|
|
@@ -233,6 +235,133 @@ module ConfigOMat
|
|
|
233
235
|
return false if other.version != version || other.contents != contents || other.name != name
|
|
234
236
|
true
|
|
235
237
|
end
|
|
238
|
+
|
|
239
|
+
private
|
|
240
|
+
|
|
241
|
+
def parse_secrets
|
|
242
|
+
secret_entries = @contents[:"aws:secrets"]
|
|
243
|
+
return if secret_entries.nil?
|
|
244
|
+
|
|
245
|
+
error :contents_secrets, 'must be a dictionary' if !secret_entries.kind_of?(Hash)
|
|
246
|
+
|
|
247
|
+
secret_entries.each do |(secret_name, secret_conf)|
|
|
248
|
+
secret_def = Secret.new(secret_name, secret_conf)
|
|
249
|
+
secret_def.validate
|
|
250
|
+
error :"contents_secrets_#{secret_name}", secret_def.errors if secret_def.errors?
|
|
251
|
+
|
|
252
|
+
@secret_defs[secret_name] = secret_def
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
class Secret < ConfigItem
|
|
258
|
+
VALID_CONTENT_TYPES = LoadedAppconfigProfile::PARSERS.keys.freeze
|
|
259
|
+
|
|
260
|
+
attr_reader :name, :secret_id, :version_id, :version_stage, :content_type
|
|
261
|
+
|
|
262
|
+
def initialize(name, opts)
|
|
263
|
+
@name = name
|
|
264
|
+
@secret_id = opts[:secret_id]
|
|
265
|
+
@version_id = opts[:version_id]
|
|
266
|
+
@version_stage = opts[:version_stage]
|
|
267
|
+
@content_type = opts[:content_type]&.downcase
|
|
268
|
+
|
|
269
|
+
if (@version_id.nil? || @version_id.empty?) && (@version_stage.nil? || @version_stage.empty?)
|
|
270
|
+
@version_stage = 'AWSCURRENT'
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
@content_type ||= 'application/json'
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def validate
|
|
277
|
+
error :secret_id, 'must be present' if @secret_id.nil? || @secret_id.empty?
|
|
278
|
+
error :content_type, "must be one of #{VALID_CONTENT_TYPES}" unless VALID_CONTENT_TYPES.include?(@content_type)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def hash
|
|
282
|
+
secret_id.hash ^ version_id.hash ^ version_stage.hash & content_type.hash
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def eql?(other)
|
|
286
|
+
return false if !super(other)
|
|
287
|
+
if other.name != name ||
|
|
288
|
+
other.secret_id != secret_id ||
|
|
289
|
+
other.version_id != version_id ||
|
|
290
|
+
other.version_stage != version_stage ||
|
|
291
|
+
other.content_type != content_type
|
|
292
|
+
return false
|
|
293
|
+
end
|
|
294
|
+
true
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
class LoadedSecret < ConfigItem
|
|
299
|
+
attr_reader :name, :secret_id, :version_id, :contents
|
|
300
|
+
|
|
301
|
+
def initialize(name, secret_id, version_id, secret_string, content_type)
|
|
302
|
+
@name = name
|
|
303
|
+
@secret_id = secret_id
|
|
304
|
+
@version_id = version_id
|
|
305
|
+
|
|
306
|
+
begin
|
|
307
|
+
@contents = LoadedAppconfigProfile::PARSERS[content_type].call(secret_string)
|
|
308
|
+
rescue StandardError => e
|
|
309
|
+
error :contents, e
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def validate
|
|
314
|
+
# Since name and version_id are coming from AWS and must be present, I'm not going to check
|
|
315
|
+
# them here.
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def hash
|
|
319
|
+
@name.hash ^ @secret_id.hash ^ @version_id.hash ^ @contents.hash
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def eql?(other)
|
|
323
|
+
return false if !super(other)
|
|
324
|
+
if other.name != name ||
|
|
325
|
+
other.secret_id != secret_id ||
|
|
326
|
+
other.version_id != version_id ||
|
|
327
|
+
other.contents != contents
|
|
328
|
+
return false
|
|
329
|
+
end
|
|
330
|
+
true
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
class LoadedProfile < ConfigItem
|
|
336
|
+
extend Forwardable
|
|
337
|
+
|
|
338
|
+
attr_reader :secrets, :loaded_appconfig_profile
|
|
339
|
+
|
|
340
|
+
def_delegators :@loaded_appconfig_profile, :name, :version, :contents
|
|
341
|
+
|
|
342
|
+
def initialize(loaded_appconfig_profile, secrets)
|
|
343
|
+
@loaded_appconfig_profile = loaded_appconfig_profile
|
|
344
|
+
@secrets = secrets || {}
|
|
345
|
+
|
|
346
|
+
@errors = @loaded_appconfig_profile.errors if @loaded_appconfig_profile.errors?
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def validate
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def hash
|
|
353
|
+
@loaded_appconfig_profile.hash ^ @secrets.hash
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def to_h
|
|
357
|
+
contents
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def eql?(other)
|
|
361
|
+
return false if !super(other)
|
|
362
|
+
return false if other.loaded_appconfig_profile != @loaded_appconfig_profile || other.secrets != @secrets
|
|
363
|
+
true
|
|
364
|
+
end
|
|
236
365
|
end
|
|
237
366
|
|
|
238
367
|
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.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex Scarborough
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-02-
|
|
11
|
+
date: 2022-02-16 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
|
|
@@ -144,7 +158,7 @@ files:
|
|
|
144
158
|
- lib/config_o_mat/configurator/op/apply_all_profiles.rb
|
|
145
159
|
- lib/config_o_mat/configurator/op/commit_staged_profile.rb
|
|
146
160
|
- lib/config_o_mat/configurator/op/compile_templates.rb
|
|
147
|
-
- lib/config_o_mat/configurator/op/
|
|
161
|
+
- lib/config_o_mat/configurator/op/connect_to_aws.rb
|
|
148
162
|
- lib/config_o_mat/configurator/op/generate_all_templates.rb
|
|
149
163
|
- lib/config_o_mat/configurator/op/next_tick.rb
|
|
150
164
|
- lib/config_o_mat/configurator/op/notify_systemd_start.rb
|
|
@@ -170,6 +184,15 @@ files:
|
|
|
170
184
|
- lib/config_o_mat/meta_configurator/op.rb
|
|
171
185
|
- lib/config_o_mat/meta_configurator/op/generate_systemd_config.rb
|
|
172
186
|
- lib/config_o_mat/meta_configurator/op/parse_meta_cli.rb
|
|
187
|
+
- lib/config_o_mat/secrets_loader.rb
|
|
188
|
+
- lib/config_o_mat/secrets_loader/cond.rb
|
|
189
|
+
- lib/config_o_mat/secrets_loader/cond/requires_load.rb
|
|
190
|
+
- lib/config_o_mat/secrets_loader/cond/secrets_to_load.rb
|
|
191
|
+
- lib/config_o_mat/secrets_loader/memory.rb
|
|
192
|
+
- lib/config_o_mat/secrets_loader/op.rb
|
|
193
|
+
- lib/config_o_mat/secrets_loader/op/check_cache.rb
|
|
194
|
+
- lib/config_o_mat/secrets_loader/op/load_secret.rb
|
|
195
|
+
- lib/config_o_mat/secrets_loader/op/stage_one_secret.rb
|
|
173
196
|
- lib/config_o_mat/shared/cond.rb
|
|
174
197
|
- lib/config_o_mat/shared/cond/early_exit.rb
|
|
175
198
|
- lib/config_o_mat/shared/op.rb
|