config_o_mat 0.2.1 → 0.3.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/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
|