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,188 @@
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
+ require 'config_o_mat/shared/types'
19
+ require 'config_o_mat/shared/systemd_interface'
20
+
21
+ require 'logsformyfamily'
22
+
23
+ require 'securerandom'
24
+ require 'yaml'
25
+
26
+ module ConfigOMat
27
+ module Op
28
+ class LoadMetaConfig < LifecycleVM::OpBase
29
+ module DeepMerge
30
+ refine Hash do
31
+ def deep_merge(other)
32
+ dup.deep_merge!(other)
33
+ end
34
+
35
+ def deep_merge!(other)
36
+ merge!(other) do |key, this_val, other_val|
37
+ if this_val.respond_to?(:deep_merge) &&
38
+ this_val.is_a?(other_val.class)
39
+ this_val.deep_merge(other_val)
40
+ else
41
+ other_val
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ refine Array do
48
+ def deep_merge(other)
49
+ dup.deep_merge!(other)
50
+ end
51
+
52
+ def deep_merge!(other)
53
+ concat(other)
54
+ end
55
+ end
56
+ end
57
+
58
+ using DeepMerge
59
+
60
+ LOG_TYPES = %i[stdout file].freeze
61
+ LOG_CONFIG_KEYS = %i[log_level log_type log_file].freeze
62
+
63
+ reads :configuration_directory, :logs_directory, :env
64
+ writes :profile_defs, :template_defs, :service_defs, :dependencies,
65
+ :refresh_interval, :client_id, :logger, :retry_count, :retries_left,
66
+ :retry_wait, :region, :systemd_interface
67
+
68
+ def call
69
+ default_config = {
70
+ refresh_interval: 5,
71
+ client_id: env.fetch('INVOCATION_ID') { SecureRandom.uuid },
72
+ retry_count: 3,
73
+ retry_wait: 2,
74
+ services: [],
75
+ templates: [],
76
+ profiles: [],
77
+ region: nil
78
+ }
79
+
80
+ # TODO: I would like to make this configurable. I think the trick
81
+ # Sequel uses for its model classes (< Sequel::Model(source_dataset))
82
+ # might be appropriate for how this system works?
83
+ # do: Op::LoadMetaConfig(parser: Yaml.method(&:safe_load))
84
+ parser = proc { |file| YAML.safe_load(file, symbolize_names: true) }
85
+ file_ending = '.conf'
86
+
87
+ files =
88
+ Dir.children(configuration_directory)
89
+ .lazy
90
+ .select { |f| f.end_with?(file_ending) }
91
+ .map { |f| File.join(configuration_directory, f) }
92
+ .select { |f| File.file?(f) }
93
+ .to_a
94
+ .sort!
95
+
96
+ loaded_files =
97
+ files.each_with_object({}) do |file, hash|
98
+ hash[file] = parser.call(File.read(file))
99
+ rescue StandardError => e
100
+ error file, e
101
+ end
102
+
103
+ # If we couldn't load a configuration file then it's probably not worth
104
+ # exploding on any objects we fail to initialize -- that's an obvious
105
+ # consequence and the additional noise might mask the root cause.
106
+ return if errors?
107
+
108
+ merged_config =
109
+ loaded_files.each_with_object(default_config) do |(_filename, config), memo|
110
+ memo.deep_merge!(config)
111
+ end
112
+
113
+ instantiate = proc do |key, klass|
114
+ merged_config[key].each_with_object({}) do |(name, obj), defs|
115
+ definition = klass.new(obj)
116
+ definition.validate!
117
+ defs[name] = definition
118
+ rescue StandardError => e
119
+ error key, { name => e }
120
+ end
121
+ end
122
+
123
+ logger&.info(:log_config, configuration: merged_config.slice(*LOG_CONFIG_KEYS))
124
+
125
+ self.service_defs = instantiate.call(:services, Service)
126
+ self.template_defs = instantiate.call(:templates, Template)
127
+ self.profile_defs = instantiate.call(:profiles, Profile)
128
+
129
+ self.logger = LogsForMyFamily::Logger.new if !logger
130
+
131
+ log_type = merged_config[:log_type]&.to_sym || :stdout
132
+ error :log_type, "must be one of #{LOG_TYPES.map(&:to_s)}" if log_type && !LOG_TYPES.include?(log_type)
133
+
134
+ log_level = merged_config[:log_level]&.to_sym
135
+ if log_level && !LogsForMyFamily::Logger::LEVELS.include?(log_level)
136
+ error :log_level, "must be one of #{LogsForMyFamily::Logger::LEVELS}"
137
+ end
138
+
139
+ backend =
140
+ if log_type == :file
141
+ log_file = merged_config[:log_file] || 'configurator.log'
142
+ if logs_directory.nil?
143
+ error :log_type, 'must set logs directory with -l or $LOGS_DIRECTORY to set log_type to file'
144
+ else
145
+ FileLogWriter.new(File.join(logs_directory, log_file))
146
+ end
147
+ else
148
+ StdoutLogWriter.new
149
+ end
150
+
151
+ # If we couldn't initialize our logger (or anything else) then bail here before
152
+ # we try to use it.
153
+ return if errors?
154
+
155
+ logger.filter_level(log_level) if log_level
156
+ logger.backends = [backend]
157
+
158
+ # Re-log our merged config with our configured logger.
159
+ logger.info(:parsed_config, configuration: merged_config)
160
+
161
+ self.refresh_interval = merged_config[:refresh_interval]
162
+ self.client_id = merged_config[:client_id]
163
+ self.retry_count = merged_config[:retry_count]
164
+ self.retries_left = retry_count
165
+ self.retry_wait = merged_config[:retry_wait]
166
+ self.region = merged_config[:region]
167
+
168
+ self.dependencies = service_defs.each_with_object({}) do |(name, service), template_to_services|
169
+ service.templates.each do |template|
170
+ template = template.to_sym
171
+ if !template_defs.key?(template)
172
+ error :services, { name => "references undefined template #{template}" }
173
+ else
174
+ # Listing the same template multiple times is acceptable. Since we allow
175
+ # merging config files, and this deep merges the service dependency list,
176
+ # it's quite possible that a service could inadvertently declare the same
177
+ # dependency twice in a way that's not easy to untangle.
178
+ template_to_services[template] ||= Set.new
179
+ template_to_services[template] << name
180
+ end
181
+ end
182
+ end
183
+
184
+ self.systemd_interface = SystemdInterface.new(DBus.system_bus)
185
+ end
186
+ end
187
+ end
188
+ 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,93 @@
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 'dbus'
18
+
19
+ module ConfigOMat
20
+ class SystemdInterface
21
+ SERVICE_NAME = 'org.freedesktop.systemd1'
22
+ OBJECT_PATH = '/org/freedesktop/systemd1'
23
+ MANAGER_INTERFACE = 'org.freedesktop.systemd1.Manager'
24
+
25
+ UNIT_INTERFACE = 'org.freedesktop.systemd1.Unit'
26
+ UNIT_STATE = 'ActiveState'
27
+
28
+ def initialize(sysbus = nil)
29
+ if sysbus
30
+ @sysd_service = sysbus[SERVICE_NAME]
31
+ obj = @sysd_service[OBJECT_PATH]
32
+ @sysd_iface = obj[MANAGER_INTERFACE]
33
+ end
34
+ end
35
+
36
+ def service_interface(unit_name)
37
+ unit_path = @sysd_iface.GetUnit("#{unit_name}.service")
38
+ unit_obj = @sysd_service[unit_path]
39
+ unit_obj[UNIT_INTERFACE]
40
+ rescue DBus::Error
41
+ nil
42
+ end
43
+
44
+ def service_status(unit_name)
45
+ iface = service_interface(unit_name)
46
+ if iface
47
+ iface[UNIT_STATE]
48
+ else
49
+ 'inactive'
50
+ end
51
+ end
52
+
53
+ def enable_restart_paths(units)
54
+ path_units = units.map { |unit| "teak-configurator-restart-service@#{unit}.path" }
55
+ enable_and_start(path_units)
56
+ end
57
+
58
+ def enable_start_stop_paths(units)
59
+ path_units = units.flat_map do |unit|
60
+ ["teak-configurator-start-service@#{unit}.path", "teak-configurator-stop-service@#{unit}.path"]
61
+ end
62
+ enable_and_start(path_units)
63
+ end
64
+
65
+ def daemon_reload
66
+ @sysd_iface.Reload()
67
+ end
68
+
69
+ def ==(other)
70
+ eql?(other)
71
+ end
72
+
73
+ def eql?(other)
74
+ return false if !other.is_a?(self.class)
75
+ return false if other.instance_variable_get(:@sysd_service) != @sysd_service
76
+ true
77
+ end
78
+
79
+ private
80
+
81
+ def enable_and_start(units)
82
+ @sysd_iface.EnableUnitFiles(
83
+ units,
84
+ true, # Runtime
85
+ false # force
86
+ )
87
+ daemon_reload
88
+ units.each do |unit|
89
+ @sysd_iface.StartUnit(unit, 'replace')
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,248 @@
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 'json'
18
+ require 'yaml'
19
+ require 'digest'
20
+
21
+ module ConfigOMat
22
+ class LogWriter
23
+ def call(level_name, event_type, merged_data)
24
+ merged_data[:level] = level_name
25
+ merged_data[:event_type] = event_type
26
+
27
+ write { "#{JSON.generate(merged_data)}\n" }
28
+ end
29
+ end
30
+
31
+ class StdoutLogWriter < LogWriter
32
+ def write
33
+ $stdout.write(yield)
34
+ $stdout.flush
35
+ end
36
+ end
37
+
38
+ class FileLogWriter < LogWriter
39
+ attr_reader :file_path
40
+
41
+ def initialize(file_path)
42
+ @file_path = file_path
43
+ end
44
+
45
+ def write
46
+ File.open(@file_path, 'a') { |f| f.write(yield) }
47
+ end
48
+ end
49
+
50
+ class ConfigItem
51
+ class ValidationError < RuntimeError
52
+ attr_reader :errors
53
+
54
+ def initialize(errors)
55
+ @errors = errors
56
+ super(errors.to_s)
57
+ end
58
+ end
59
+
60
+ attr_reader :errors
61
+
62
+ def errors?
63
+ instance_variable_defined?(:@errors) && !(@errors.nil? || @errors.empty?)
64
+ end
65
+
66
+ def error(field, message)
67
+ @errors ||= {}
68
+ @errors[field] ||= []
69
+ @errors[field] << message
70
+ end
71
+
72
+ def validate!
73
+ validate
74
+ raise ValidationError.new(errors) if errors?
75
+ end
76
+
77
+ def eql?(other)
78
+ return false if !other.is_a?(self.class)
79
+ true
80
+ end
81
+
82
+ def ==(other)
83
+ eql?(other)
84
+ end
85
+ end
86
+
87
+ class Service < ConfigItem
88
+ RESTART_MODES = %i[restart flip_flop].freeze
89
+
90
+ attr_reader :systemd_unit, :restart_mode, :templates
91
+
92
+ def initialize(opts)
93
+ @systemd_unit = (opts[:systemd_unit] || '')
94
+ @restart_mode = opts[:restart_mode]&.to_sym
95
+ @templates = opts[:templates]
96
+
97
+ if @restart_mode == :flip_flop && !@systemd_unit.include?('@')
98
+ @systemd_unit = "#{@systemd_unit}@"
99
+ end
100
+ end
101
+
102
+ def validate
103
+ error :templates, 'must be present' if @templates.nil? || @templates.empty?
104
+ unless @templates.is_a?(Array) && @templates.all? { |v| v.is_a?(String) }
105
+ error :templates, 'must be an array of strings'
106
+ end
107
+ error :systemd_unit, 'must be present' if @systemd_unit.nil? || @systemd_unit.empty? || @systemd_unit == '@'
108
+ error :restart_mode, "must be one of #{RESTART_MODES}" unless RESTART_MODES.include?(@restart_mode)
109
+
110
+ if @restart_mode == :flip_flop && !@systemd_unit.end_with?('@')
111
+ error :systemd_unit, 'must not contain an instance (anything after a @)'
112
+ end
113
+
114
+ if restart_mode == :restart && @systemd_unit.end_with?('@')
115
+ error :systemd_unit, 'must not be a naked instantiated unit when restart_mode=restart'
116
+ end
117
+ end
118
+
119
+ def hash
120
+ systemd_unit.hash ^ restart_mode.hash ^ templates.hash
121
+ end
122
+
123
+ def eql?(other)
124
+ return false if !super(other)
125
+ return false if other.systemd_unit != systemd_unit || other.restart_mode != restart_mode || other.templates != templates
126
+ true
127
+ end
128
+ end
129
+
130
+ class Template < ConfigItem
131
+ attr_reader :src, :dst
132
+
133
+ def initialize(opts)
134
+ @src = opts[:src]
135
+ @dst = opts[:dst]
136
+ end
137
+
138
+ def validate
139
+ error :src, 'must be present' if @src.nil? || @src.empty?
140
+ error :dst, 'must be present' if @dst.nil? || @dst.empty?
141
+ end
142
+
143
+ def hash
144
+ src.hash ^ dst.hash
145
+ end
146
+
147
+ def eql?(other)
148
+ return false if !super(other)
149
+ return false if other.src != src || other.dst != dst
150
+ true
151
+ end
152
+ end
153
+
154
+ class Profile < ConfigItem
155
+ attr_reader :application, :environment, :profile
156
+
157
+ def initialize(opts)
158
+ @application = opts[:application]
159
+ @environment = opts[:environment]
160
+ @profile = opts[:profile]
161
+ end
162
+
163
+ def validate
164
+ error :application, 'must be present' if @application.nil? || @application.empty?
165
+ error :environment, 'must be present' if @environment.nil? || @environment.empty?
166
+ error :profile, 'must be present' if @profile.nil? || @profile.empty?
167
+ end
168
+
169
+ def hash
170
+ application.hash ^ environment.hash ^ profile.hash
171
+ end
172
+
173
+ def eql?(other)
174
+ return false if !super(other)
175
+ return false if other.application != application || other.environment != environment || other.profile != profile
176
+ true
177
+ end
178
+ end
179
+
180
+ class LoadedProfile < ConfigItem
181
+ attr_reader :name, :version, :contents
182
+
183
+ PARSERS = {
184
+ 'text/plain' => proc { |str| str },
185
+ 'application/json' => proc { |str| JSON.parse(str, symbolize_names: true) },
186
+ 'application/x-yaml' => proc { |str| YAML.safe_load(str, symbolize_names: true) }
187
+ }.freeze
188
+
189
+ def initialize(name, version, contents, content_type)
190
+ @name = name
191
+ @version = version
192
+
193
+ parser = PARSERS[content_type]
194
+
195
+ if parser
196
+ begin
197
+ @contents = parser.call(contents)
198
+ rescue StandardError => e
199
+ error :contents, e
200
+ end
201
+ else
202
+ error :content_type, "must be one of #{PARSERS.keys}"
203
+ end
204
+ end
205
+
206
+ def validate
207
+ error :name, 'must be present' if @name.nil? || @name.empty?
208
+ error :name, 'must be a Symbol' unless @name.is_a?(Symbol)
209
+ error :version, 'must be present' if @version.nil? || @version.empty?
210
+ error :contents, 'must be present' if @contents.nil? || @contents.empty?
211
+ end
212
+
213
+ def hash
214
+ @name.hash ^ @version.hash ^ @contents.hash
215
+ end
216
+
217
+ def to_h
218
+ @contents
219
+ end
220
+
221
+ def eql?(other)
222
+ return false if !super(other)
223
+ return false if other.version != version || other.contents != contents || other.name != name
224
+ true
225
+ end
226
+ end
227
+
228
+ class GeneratedTemplate < ConfigItem
229
+ attr_reader :digest
230
+
231
+ def initialize(contents)
232
+ @digest = Digest::SHA256.hexdigest(contents)
233
+ end
234
+
235
+ def validate
236
+ end
237
+
238
+ def hash
239
+ @digest.hash
240
+ end
241
+
242
+ def eql?(other)
243
+ return false if !super(other)
244
+ return false if other.digest != digest
245
+ true
246
+ end
247
+ end
248
+ end
@@ -0,0 +1,19 @@
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
+ module ConfigOMat
18
+ VERSION = "0.1.0"
19
+ end
data/lib/version.rb ADDED
@@ -0,0 +1,19 @@
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
+ module ConfigOMat
18
+ VERSION = "0.1.0"
19
+ end
@@ -0,0 +1,27 @@
1
+ # Copyright 2021 Teak.io, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # By enabling this unit for a service, we ensure that after the service starts systemd
16
+ # listens for touches on the configurator's restart file, and will run teak-configurator-restart-service@%i.service
17
+ # when the file is touched.
18
+ #
19
+ # This allows the configurator to restart systemd services without privileged access.
20
+ [Unit]
21
+ Description=Touch a file to restart %i.service
22
+
23
+ [Path]
24
+ PathModified=/run/teak-configurator/%i.restart
25
+
26
+ [Install]
27
+ WantedBy=%i.service
@@ -0,0 +1,21 @@
1
+ # Copyright 2021 Teak.io, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ [Unit]
16
+ Description=Restart %i.service
17
+
18
+ [Service]
19
+ Type=oneshot
20
+ ExecStart=/usr/bin/systemctl restart %i.service
21
+ RemainAfterExit=no
@@ -0,0 +1,27 @@
1
+ # Copyright 2021 Teak.io, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # By enabling this unit for a service, we ensure that after the service starts systemd
16
+ # listens for touches on the configurator's start file, and will run teak-configurator-start-service@%i.service
17
+ # when the file is touched.
18
+ #
19
+ # This allows the configurator to start systemd services without privileged access.
20
+ [Unit]
21
+ Description=Touch a file to start %i.service
22
+
23
+ [Path]
24
+ PathModified=/run/teak-configurator/%i.start
25
+
26
+ [Install]
27
+ WantedBy=%i.service
@@ -0,0 +1,21 @@
1
+ # Copyright 2021 Teak.io, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ [Unit]
16
+ Description=Start %i.service
17
+
18
+ [Service]
19
+ Type=oneshot
20
+ ExecStart=/usr/bin/systemctl start %i.service
21
+ RemainAfterExit=no