config_o_mat 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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