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,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