config_o_mat 0.1.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.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +1 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/Gemfile +26 -0
  7. data/Gemfile.lock +56 -0
  8. data/LICENSE +202 -0
  9. data/NOTICE +2 -0
  10. data/README.md +15 -0
  11. data/Rakefile +35 -0
  12. data/bin/config_o_mat-configurator +49 -0
  13. data/bin/config_o_mat-meta_configurator +49 -0
  14. data/config_o_mat.gemspec +49 -0
  15. data/design/configurator_lifecycle_test.dot +91 -0
  16. data/design/original_design.md +45 -0
  17. data/lib/config_o_mat/configurator/cond/first_run.rb +29 -0
  18. data/lib/config_o_mat/configurator/cond/next_state.rb +29 -0
  19. data/lib/config_o_mat/configurator/cond/profiles_to_apply.rb +29 -0
  20. data/lib/config_o_mat/configurator/cond/retries_left.rb +37 -0
  21. data/lib/config_o_mat/configurator/cond/services_to_reload.rb +29 -0
  22. data/lib/config_o_mat/configurator/cond.rb +17 -0
  23. data/lib/config_o_mat/configurator/memory.rb +93 -0
  24. data/lib/config_o_mat/configurator/op/apply_all_profiles.rb +35 -0
  25. data/lib/config_o_mat/configurator/op/commit_staged_profile.rb +35 -0
  26. data/lib/config_o_mat/configurator/op/compile_templates.rb +41 -0
  27. data/lib/config_o_mat/configurator/op/connect_to_appconfig.rb +35 -0
  28. data/lib/config_o_mat/configurator/op/generate_all_templates.rb +66 -0
  29. data/lib/config_o_mat/configurator/op/next_tick.rb +48 -0
  30. data/lib/config_o_mat/configurator/op/notify_systemd_start.rb +29 -0
  31. data/lib/config_o_mat/configurator/op/parse_cli.rb +93 -0
  32. data/lib/config_o_mat/configurator/op/refresh_all_profiles.rb +63 -0
  33. data/lib/config_o_mat/configurator/op/refresh_profile.rb +65 -0
  34. data/lib/config_o_mat/configurator/op/reload_one_service.rb +73 -0
  35. data/lib/config_o_mat/configurator/op/stage_one_profile.rb +33 -0
  36. data/lib/config_o_mat/configurator/op/wait_retry.rb +41 -0
  37. data/lib/config_o_mat/configurator/op.rb +17 -0
  38. data/lib/config_o_mat/configurator.rb +122 -0
  39. data/lib/config_o_mat/flip_flopper/cond/service_status.rb +29 -0
  40. data/lib/config_o_mat/flip_flopper/cond.rb +17 -0
  41. data/lib/config_o_mat/flip_flopper/memory.rb +51 -0
  42. data/lib/config_o_mat/flip_flopper/op/check_service_status.rb +72 -0
  43. data/lib/config_o_mat/flip_flopper/op/determine_running_instance.rb +56 -0
  44. data/lib/config_o_mat/flip_flopper/op/report_failure.rb +36 -0
  45. data/lib/config_o_mat/flip_flopper/op/start_activating_instance.rb +34 -0
  46. data/lib/config_o_mat/flip_flopper/op/stop_activating_instance.rb +34 -0
  47. data/lib/config_o_mat/flip_flopper/op/stop_initial_instance.rb +34 -0
  48. data/lib/config_o_mat/flip_flopper/op.rb +17 -0
  49. data/lib/config_o_mat/flip_flopper.rb +46 -0
  50. data/lib/config_o_mat/meta_configurator/memory.rb +73 -0
  51. data/lib/config_o_mat/meta_configurator/op/generate_systemd_config.rb +80 -0
  52. data/lib/config_o_mat/meta_configurator/op/parse_meta_cli.rb +103 -0
  53. data/lib/config_o_mat/meta_configurator/op.rb +17 -0
  54. data/lib/config_o_mat/meta_configurator.rb +44 -0
  55. data/lib/config_o_mat/shared/cond/early_exit.rb +29 -0
  56. data/lib/config_o_mat/shared/cond.rb +17 -0
  57. data/lib/config_o_mat/shared/op/load_meta_config.rb +188 -0
  58. data/lib/config_o_mat/shared/op.rb +17 -0
  59. data/lib/config_o_mat/shared/systemd_interface.rb +93 -0
  60. data/lib/config_o_mat/shared/types.rb +248 -0
  61. data/lib/config_o_mat/version.rb +19 -0
  62. data/lib/version.rb +19 -0
  63. data/systemd/teak-configurator-restart-service@.path +27 -0
  64. data/systemd/teak-configurator-restart-service@.service +21 -0
  65. data/systemd/teak-configurator-start-service@.path +27 -0
  66. data/systemd/teak-configurator-start-service@.service +21 -0
  67. data/systemd/teak-configurator-stop-service@.path +27 -0
  68. data/systemd/teak-configurator-stop-service@.service +21 -0
  69. data/systemd/teak-configurator.service +68 -0
  70. data/systemd/teak-metaconfigurator.service +57 -0
  71. metadata +213 -0
@@ -0,0 +1,63 @@
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 RefreshAllProfiles < LifecycleVM::OpBase
22
+ reads :profile_defs, :applied_profiles, :client_id, :appconfig_client
23
+ writes :profiles_to_apply, :last_refresh_time
24
+
25
+ def call
26
+ self.profiles_to_apply = []
27
+ self.last_refresh_time = Time.now.to_i
28
+
29
+ profile_defs.each do |(profile_name, definition)|
30
+ request = {
31
+ application: definition.application, environment: definition.environment,
32
+ configuration: definition.profile, client_id: client_id
33
+ }
34
+
35
+ current_version = applied_profiles&.fetch(profile_name, nil)&.version
36
+ request[:client_configuration_version] = current_version if current_version
37
+
38
+ response =
39
+ begin
40
+ appconfig_client.get_configuration(request)
41
+ rescue StandardError => e
42
+ error profile_name, e
43
+ nil
44
+ end
45
+
46
+ next if response.nil?
47
+
48
+ loaded_version = response.configuration_version
49
+ next if current_version && loaded_version == current_version
50
+
51
+ logger&.notice(
52
+ :updated_profile,
53
+ name: profile_name, previous_version: current_version, new_version: loaded_version
54
+ )
55
+
56
+ profiles_to_apply << LoadedProfile.new(
57
+ profile_name, loaded_version, response.content.read, response.content_type
58
+ )
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,65 @@
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 RefreshProfile < LifecycleVM::OpBase
22
+ reads :profile_defs, :client_id, :applying_profile, :appconfig_client
23
+ writes :applying_profile
24
+
25
+ def call
26
+ profile_name = applying_profile.name
27
+ profile_version = applying_profile.version
28
+ definition = profile_defs[profile_name]
29
+ request = {
30
+ application: definition.application, environment: definition.environment,
31
+ configuration: definition.profile, client_id: client_id,
32
+ client_configuration_version: profile_version
33
+ }
34
+
35
+ response =
36
+ begin
37
+ appconfig_client.get_configuration(request)
38
+ rescue StandardError => e
39
+ error profile_name, e
40
+ nil
41
+ end
42
+
43
+ return if response.nil? || errors?
44
+ loaded_version = response.configuration_version
45
+
46
+ if loaded_version == profile_version
47
+ logger&.warning(
48
+ :no_update,
49
+ name: profile_name, version: profile_version
50
+ )
51
+ return
52
+ end
53
+
54
+ logger&.notice(
55
+ :updated_profile,
56
+ name: profile_name, previous_version: profile_version, new_version: loaded_version
57
+ )
58
+
59
+ self.applying_profile = LoadedProfile.new(
60
+ profile_name, loaded_version, response.content.read, response.content_type
61
+ )
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,73 @@
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/flip_flopper'
20
+
21
+ require 'dbus'
22
+
23
+ module ConfigOMat
24
+ module Op
25
+ class ReloadOneService < LifecycleVM::OpBase
26
+ reads :services_to_reload, :runtime_directory, :service_defs, :systemd_interface
27
+ writes :services_to_reload
28
+
29
+ def call
30
+ service = services_to_reload.pop
31
+ service_def = service_defs[service]
32
+
33
+ if service_def.restart_mode == :restart
34
+ do_restart(service, service_def)
35
+ else
36
+ do_flip_flop(service, service_def)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def do_restart(service, service_def)
43
+ unit = service_def.systemd_unit
44
+
45
+ file_path = File.join(runtime_directory, unit + '.restart')
46
+
47
+ logger&.notice(
48
+ :service_restart,
49
+ name: service, systemd_unit: unit, touched_path: file_path
50
+ )
51
+
52
+ FileUtils.touch(file_path)
53
+ end
54
+
55
+ def do_flip_flop(service, service_def)
56
+ mem = ConfigOMat::FlipFlopper::Memory.new(
57
+ runtime_directory: runtime_directory,
58
+ service: service_def.systemd_unit,
59
+ systemd_interface: systemd_interface,
60
+ logger: logger
61
+ )
62
+ vm = ConfigOMat::FlipFlopper::VM.new(mem)
63
+ vm.call
64
+
65
+ if vm.errors?
66
+ vm.errors.each do |(key, value)|
67
+ value.each { |v| error key, v }
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,33 @@
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 StageOneProfile < LifecycleVM::OpBase
22
+ reads :profiles_to_apply
23
+ writes :applying_profile, :profiles_to_apply
24
+
25
+ def call
26
+ self.applying_profile = profiles_to_apply.pop
27
+
28
+ # We defer error checking to GenerateAllTemplates so that even if errored the profile gets set as
29
+ # the applying_profile, which simplifies retry logic.
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,41 @@
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 WaitRetry < LifecycleVM::OpBase
22
+ reads :retry_wait, :retry_count, :retries_left
23
+ writes :retries_left
24
+
25
+ def call
26
+ # Exponential backoff
27
+ wait = retry_wait * (2**(retry_count - retries_left))
28
+
29
+ # With jitter
30
+ wait += rand(retry_wait + 1) - (retry_wait / 2.0)
31
+
32
+ logger&.notice(:retry_wait, wait: wait)
33
+
34
+ # Calling Kernel.sleep directly for easy test stubbing
35
+ Kernel.sleep wait
36
+
37
+ self.retries_left -= 1
38
+ end
39
+ end
40
+ end
41
+ 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,122 @@
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/shared/cond'
20
+ require 'config_o_mat/shared/op'
21
+ require 'config_o_mat/configurator/op'
22
+ require 'config_o_mat/configurator/cond'
23
+ require 'config_o_mat/configurator/memory'
24
+
25
+ module ConfigOMat
26
+ module Configurator
27
+ class VM < LifecycleVM::VM
28
+ VERSION = "0.0.1"
29
+
30
+ memory_class ConfigOMat::Configurator::Memory
31
+ on_op_failure :op_failure
32
+ terminal :fail
33
+
34
+ on :start, do: Op::ParseCli, then: {
35
+ case: Cond::EarlyExit,
36
+ when: {
37
+ true => :exit,
38
+ false => :reading_meta_config
39
+ }
40
+ }
41
+
42
+ on :reading_meta_config, do: Op::LoadMetaConfig, then: :compiling_templates
43
+
44
+ on :compiling_templates, do: Op::CompileTemplates, then: :connecting_to_appconfig
45
+
46
+ on :connecting_to_appconfig, do: Op::ConnectToAppconfig, then: :refreshing_profiles
47
+
48
+ on :refreshing_profiles,
49
+ do: Op::RefreshAllProfiles,
50
+ then: {
51
+ case: Cond::FirstRun,
52
+ when: {
53
+ true => :initial_applying_profiles,
54
+ false => :applying_profiles
55
+ }
56
+ }
57
+
58
+ on :initial_applying_profiles, do: Op::ApplyAllProfiles, then: :generating_templates
59
+
60
+ on :applying_profiles,
61
+ then: {
62
+ case: Cond::ProfilesToApply,
63
+ when: {
64
+ true => :applying_profile,
65
+ false => :running
66
+ }
67
+ }
68
+
69
+ on :applying_profile, do: Op::StageOneProfile, then: :generating_templates
70
+
71
+ on :generating_templates, do: Op::GenerateAllTemplates, then: {
72
+ case: Cond::FirstRun,
73
+ when: {
74
+ true => :notifying_systemd,
75
+ false => :reloading_services
76
+ }
77
+ }
78
+
79
+ on :notifying_systemd, do: Op::NotifySystemdStart, then: :running
80
+
81
+ on :reloading_services,
82
+ then: {
83
+ case: Cond::ServicesToReload,
84
+ when: {
85
+ true => :reloading_service,
86
+ false => { do: Op::CommitStagedProfile, then: :applying_profiles }
87
+ }
88
+ }
89
+
90
+ on :reloading_service, do: Op::ReloadOneService, then: :reloading_services
91
+
92
+ on :running,
93
+ do: Op::NextTick,
94
+ then: {
95
+ case: Cond::NextState,
96
+ when: {
97
+ refreshing_profiles: :refreshing_profiles,
98
+ graceful_shutdown: :exit,
99
+ fail: :fail,
100
+ running: :running
101
+ }
102
+ }
103
+
104
+ on :op_failure,
105
+ then: {
106
+ case: Cond::FirstRun,
107
+ when: {
108
+ true => :fail,
109
+ false => {
110
+ case: Cond::RetriesLeft,
111
+ when: {
112
+ true => { do: Op::WaitRetry, then: :refreshing_profile },
113
+ false => :fail
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ on :refreshing_profile, do: Op::RefreshProfile, then: :applying_profile
120
+ end
121
+ end
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 ServiceStatus < LifecycleVM::CondBase
22
+ reads :activation_status
23
+
24
+ def call
25
+ activation_status
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,51 @@
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 FlipFlopper
21
+ class Memory < LifecycleVM::Memory
22
+ attr_reader :systemd_interface, :service, :runtime_directory
23
+ attr_accessor :running_instance, :activating_instance, :activation_status, :activating_interface,
24
+ :min_wait, :max_wait
25
+
26
+ def initialize(
27
+ systemd_interface: nil,
28
+ service: nil,
29
+ min_wait: 5,
30
+ max_wait: 30,
31
+ runtime_directory: nil,
32
+ running_instance: nil,
33
+ activating_instance: nil,
34
+ activation_status: nil,
35
+ activating_interface: nil,
36
+ logger: nil
37
+ )
38
+ @systemd_interface = systemd_interface
39
+ @service = service
40
+ @min_wait = min_wait
41
+ @max_wait = max_wait
42
+ @runtime_directory = runtime_directory
43
+ @running_instance = running_instance
44
+ @activating_instance = activating_instance
45
+ @activation_status = activation_status
46
+ @activating_interface = activating_interface
47
+ @logger = logger
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,72 @@
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 CheckServiceStatus < LifecycleVM::OpBase
22
+ reads :service, :activating_instance, :activating_interface, :systemd_interface,
23
+ :min_wait, :max_wait
24
+ writes :activating_interface, :activation_status, :min_wait, :max_wait
25
+
26
+ def call
27
+ if min_wait > 0
28
+ do_sleep(min_wait)
29
+ else
30
+ do_sleep(1)
31
+ end
32
+
33
+ instance_name = "#{service}#{activating_instance}"
34
+
35
+ if !activating_interface
36
+ self.activating_interface = systemd_interface.service_interface(instance_name)
37
+ end
38
+
39
+ if !activating_interface
40
+ logger&.error(:ipc_failure, name: instance_name)
41
+ self.activation_status = :failed
42
+ return
43
+ end
44
+
45
+ reported_status = activating_interface['ActiveState']
46
+
47
+ logger&.info(:service_status, name: instance_name, status: reported_status)
48
+
49
+ if reported_status == 'active'
50
+ self.activation_status = :started
51
+ elsif reported_status == 'activating'
52
+ if max_wait > 0
53
+ self.activation_status = :starting
54
+ else
55
+ self.activation_status = :timed_out
56
+ end
57
+ else
58
+ self.activation_status = :failed
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def do_sleep(secs)
65
+ # I'm calling Kernel.sleep directly so we can easily stub it out.
66
+ ::Kernel.sleep secs
67
+ self.min_wait -= secs
68
+ self.max_wait -= secs
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,56 @@
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 DetermineRunningInstance < LifecycleVM::OpBase
22
+ reads :systemd_interface, :service
23
+ writes :running_instance, :activating_instance
24
+
25
+ RUNNING_STATES = %w[active activating reloading].freeze
26
+
27
+ def call
28
+ i1_name = "#{service}1"
29
+ i1_status = systemd_interface.service_status(i1_name)
30
+ logger&.info(:service_status, name: i1_name, status: i1_status)
31
+
32
+ i2_name = "#{service}2"
33
+ i2_status = systemd_interface.service_status(i2_name)
34
+ logger&.info(:service_status, name: i2_name, status: i2_status)
35
+
36
+ i1_running = RUNNING_STATES.include?(i1_status)
37
+ i2_running = RUNNING_STATES.include?(i2_status)
38
+
39
+ if i1_running && i2_running
40
+ error :service, 'both instances are currently running!'
41
+ elsif i1_running
42
+ self.activating_instance = 2
43
+ self.running_instance = 1
44
+ elsif i2_running
45
+ self.activating_instance = 1
46
+ self.running_instance = 2
47
+ else
48
+ # If neither version is up, attempt to bring up instance 1. Our attempt to stop
49
+ # instance 2 will be a noop.
50
+ self.activating_instance = 1
51
+ self.running_instance = 2
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,36 @@
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 ReportFailure < LifecycleVM::OpBase
22
+ reads :activation_status
23
+
24
+ def call
25
+ case activation_status
26
+ when :failed
27
+ error :service, 'failed to start service instance'
28
+ when :timed_out
29
+ error :service, 'service instance did not start within timeout'
30
+ else
31
+ error :service, "service instance failed due to an unknown error (#{activation_status})"
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end