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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 35eaeccb6abdf69afb14c8cbe951c4579734f27af4844673b1761db70dfd1bf1
4
- data.tar.gz: 392a61b3ee55ba8d9351c2b7d69d39de7d591bbde87f3374fac77fc425e628b8
3
+ metadata.gz: bd094e1ed00e6019a324570d47475448e924c6c31abe2c9fb7c31cd37064ddfe
4
+ data.tar.gz: e9712d360ae7da7adf5c15142bf3a2365465832071998790ebd8e5a089429f9a
5
5
  SHA512:
6
- metadata.gz: 31f9277ada3b1a7fc09efe007a3b530fb3f100c480c09ab0af83a9879ab1fa3ea0b0db3252300fe8dbe9bb9877acf2244727a94554a6384bf52641a5a95c685b
7
- data.tar.gz: 4346bae4957800e67e238c0231ddf1b424e1c5afe60a86e2d29e2134f480d56979d320514e24b412557f7f31b239fd7342ffdce459c387ba2b3ce56ce3e3e054
6
+ metadata.gz: 1a63b431f2fe289c0defaadc88a7105910d0198bf8dc3862da8effb908953f2b480687cc7750fc4f282cc410dcc6f3dc4e8d54659523e909c07f5e8e8059a1e3
7
+ data.tar.gz: 8763d1eff5f21f0cb2cfc603595fb8b4e198dad30ccbb8f2727ee930df1438d8b5a5c5075de674e269b99a25313f6648be160b72ed6caa3d921d595af7c8fb83
data/.gitignore CHANGED
@@ -7,3 +7,6 @@ coverage/
7
7
  # YARD doc db and output
8
8
  .yardoc/
9
9
  doc/
10
+
11
+ # Gem builds
12
+ pkg/
data/Gemfile CHANGED
@@ -16,11 +16,4 @@
16
16
 
17
17
  source 'https://rubygems.org'
18
18
 
19
- gem 'aws-sdk-appconfig', '~> 1.18', require: false
20
- gem 'logsformyfamily', '~> 0.2', require: false
21
- gem 'lifecycle_vm', '~> 0.1.1', require: false
22
- gem 'ruby-dbus', '~> 0.16.0', require: false
23
- gem 'sd_notify', '~> 0.1', require: false
24
-
25
- gem 'rspec', '~> 3.10', group: :test, require: false
26
- gem 'simplecov', '~> 0.21', group: :test, require: false
19
+ gemspec
data/Gemfile.lock CHANGED
@@ -1,36 +1,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.525.0)
6
- aws-sdk-appconfig (1.19.0)
7
- aws-sdk-core (~> 3, >= 3.122.0)
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.122.0)
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.4.4)
30
+ diff-lcs (1.5.0)
17
31
  docile (1.4.0)
18
- jmespath (1.4.0)
32
+ jmespath (1.6.0)
19
33
  lifecycle_vm (0.1.2)
20
34
  logsformyfamily (0.2.3)
21
- rspec (3.10.0)
22
- rspec-core (~> 3.10.0)
23
- rspec-expectations (~> 3.10.0)
24
- rspec-mocks (~> 3.10.0)
25
- rspec-core (3.10.1)
26
- rspec-support (~> 3.10.0)
27
- rspec-expectations (3.10.1)
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.10.0)
30
- rspec-mocks (3.10.2)
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.10.0)
33
- rspec-support (3.10.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.3)
55
+ simplecov_json_formatter (0.1.4)
42
56
 
43
57
  PLATFORMS
44
58
  ruby
45
59
 
46
60
  DEPENDENCIES
47
- aws-sdk-appconfig (~> 1.18)
48
- lifecycle_vm (~> 0.1.1)
49
- logsformyfamily (~> 0.2)
61
+ config_o_mat!
50
62
  rspec (~> 3.10)
51
- ruby-dbus (~> 0.16.0)
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
- systemd_interface: nil
59
+ secretsmanager_client: nil,
60
+ systemd_interface: nil,
61
+ secrets_loader_memory: nil
59
62
  )
60
63
  super()
61
64
 
@@ -86,7 +89,9 @@ module ConfigOMat
86
89
  @retry_wait = retry_wait
87
90
  @region = region
88
91
  @appconfig_client = appconfig_client
92
+ @secretsmanager_client = secretsmanager_client
89
93
  @systemd_interface = systemd_interface
94
+ @secrets_loader_memory = secrets_loader_memory
90
95
  end
91
96
  end
92
97
  end
@@ -17,18 +17,20 @@
17
17
  require 'lifecycle_vm/op_base'
18
18
 
19
19
  require 'aws-sdk-appconfig'
20
+ require 'aws-sdk-secretsmanager'
20
21
 
21
22
  module ConfigOMat
22
23
  module Op
23
- class ConnectToAppconfig < LifecycleVM::OpBase
24
+ class ConnectToAws < LifecycleVM::OpBase
24
25
  reads :region
25
- writes :appconfig_client
26
+ writes :appconfig_client, :secretsmanager_client
26
27
 
27
28
  def call
28
29
  client_opts = { logger: logger }
29
30
  client_opts[:region] = region if region
30
31
 
31
32
  self.appconfig_client = Aws::AppConfig::Client.new(client_opts)
33
+ self.secretsmanager_client = Aws::SecretsManager::Client.new(client_opts)
32
34
  end
33
35
  end
34
36
  end
@@ -16,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
- profiles_to_apply << LoadedProfile.new(
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
- self.applying_profile = LoadedProfile.new(
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: :connecting_to_appconfig
44
+ on :compiling_templates, do: Op::CompileTemplates, then: :connecting_to_aws
45
45
 
46
- on :connecting_to_appconfig, do: Op::ConnectToAppconfig, then: :refreshing_profiles
46
+ on :connecting_to_aws, do: Op::ConnectToAws, then: :refreshing_profiles
47
47
 
48
48
  on :refreshing_profiles,
49
49
  do: Op::RefreshAllProfiles,
@@ -116,7 +116,7 @@ module ConfigOMat
116
116
  }
117
117
  }
118
118
 
119
- on :refreshing_profile, do: Op::RefreshProfile, then: :applying_profile
119
+ on :refreshing_profile, do: Op::RefreshProfile, then: :generating_templates
120
120
  end
121
121
  end
122
122
  end
@@ -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 LoadedProfile < ConfigItem
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
@@ -15,5 +15,5 @@
15
15
  # limitations under the License.
16
16
 
17
17
  module ConfigOMat
18
- VERSION = "0.2.1"
18
+ VERSION = "0.3.0"
19
19
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: config_o_mat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
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-15 00:00:00.000000000 Z
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/connect_to_appconfig.rb
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