config_skeleton 0.4.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 20798a914f83d1ab8531eb66259f202816e40ade371172102058536cf2fa3af7
4
- data.tar.gz: 5f742b8f777496e322f845eccc9c641eb7bad0815c8853cd1eea26bbddd2080c
3
+ metadata.gz: 114069700875be6dd679f4731609175447186ddcbde862bdfef1d516c0f66601
4
+ data.tar.gz: 424208817f7b1c407272909e8c13e7e9e308dab0ca18e68ce5fda208c9536464
5
5
  SHA512:
6
- metadata.gz: 9873af5655630b61a5833a58d6203836a78d4e11218802608fbcde86ada691e6e38ae2a52dd088037e00be1721aed145c6c057cd3c0175ad4279995d3da7d91e
7
- data.tar.gz: a9e1d788d30246ee436a452076fd157d03864123b3b1eca420c26cb5d6c0684752a5028e4930f81195e2e47e6a8253f468564d525eb3bff8738bf61299d8658a
6
+ metadata.gz: 1da2b1664644625d452e3aa6d7f10435901dd63c1084f1e6d1f9b0ad87e1ffc977efef0e9d64db11ffb072ab495d2a2064ca5b25f1afcc0b27b0ba9750a2d550
7
+ data.tar.gz: a288a8a63fba3019181fe44011c21088136e0c89dc72fb7f870dd96ce2fafc2eef599b9eb1a327149ba0ac973d75cbdda3ef3d164345d6984b6e84e14ed550d0
@@ -43,6 +43,6 @@ jobs:
43
43
  - uses: actions/checkout@v2
44
44
 
45
45
  - name: Release Gem
46
- uses: CvX/publish-rubygems-action@master
46
+ uses: discourse/publish-rubygems-action@main
47
47
  env:
48
48
  RUBYGEMS_API_KEY: ${{secrets.RUBYGEMS_API_KEY}}
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "config_skeleton"
3
3
 
4
- s.version = "0.4.1"
4
+ s.version = "1.0.0"
5
5
 
6
6
  s.platform = Gem::Platform::RUBY
7
7
 
@@ -16,9 +16,8 @@ Gem::Specification.new do |s|
16
16
  s.required_ruby_version = ">= 2.3.0"
17
17
 
18
18
  s.add_runtime_dependency 'diffy', '~> 3.0'
19
- s.add_runtime_dependency 'frankenstein', '~> 1.0'
20
19
  s.add_runtime_dependency 'rb-inotify', '~> 0.9'
21
- s.add_runtime_dependency 'service_skeleton', '> 0.a'
20
+ s.add_runtime_dependency 'service_skeleton', "~> 1.0"
22
21
 
23
22
  s.add_development_dependency 'bundler'
24
23
  s.add_development_dependency 'github-release'
@@ -2,11 +2,17 @@ require 'diffy'
2
2
  require 'fileutils'
3
3
  require 'frankenstein'
4
4
  require 'logger'
5
- require 'rb-inotify'
6
5
  require 'service_skeleton'
7
6
  require 'tempfile'
8
7
  require 'digest/md5'
9
8
 
9
+ begin
10
+ require 'rb-inotify' unless ENV["DISABLE_INOTIFY"]
11
+ rescue FFI::NotFoundError => e
12
+ STDERR.puts "ERROR: Unable to initialize rb-inotify. To disable, set DISABLE_INOTIFY=1"
13
+ raise
14
+ end
15
+
10
16
  # Framework for creating config generation systems.
11
17
  #
12
18
  # There are many systems which require some sort of configuration file to
@@ -29,14 +35,13 @@ require 'digest/md5'
29
35
  #
30
36
  # 1. Setup any file watchers you want with .watch and #watch.
31
37
  #
32
- # 1. Instantiate your new class, passing in an environment hash, and then call
33
- # #start. Something like this should do the trick:
38
+ # 1. Use the ServiceSkeleton Runner to start the service. Something like this should do the trick:
34
39
  #
35
40
  # class MyConfigGenerator < ConfigSkeleton
36
41
  # # Implement all the necessary methods
37
42
  # end
38
43
  #
39
- # MyConfigGenerator.new(ENV).start if __FILE__ == $0
44
+ # ServiceSkeleton::Runner.new(MyConfigGenerator, ENV).run if __FILE__ == $0
40
45
  #
41
46
  # 1. Sit back and relax.
42
47
  #
@@ -136,7 +141,8 @@ require 'digest/md5'
136
141
  # method, passing one or more strings containing the full path to files or
137
142
  # directories to watch.
138
143
  #
139
- class ConfigSkeleton < ServiceSkeleton
144
+ class ConfigSkeleton
145
+ include ServiceSkeleton
140
146
  # All ConfigSkeleton-related errors will be subclasses of this.
141
147
  class Error < StandardError; end
142
148
 
@@ -155,6 +161,19 @@ class ConfigSkeleton < ServiceSkeleton
155
161
  end
156
162
  end
157
163
 
164
+ def self.inherited(klass)
165
+ klass.gauge :"#{klass.service_name}_last_generation_timestamp", docstring: "When the last config generation run was made"
166
+ klass.gauge :"#{klass.service_name}_last_change_timestamp", docstring: "When the config file was last written to"
167
+ klass.counter :"#{klass.service_name}_reload_total", docstring: "How many times we've asked the server to reload", labels: [:status]
168
+ klass.counter :"#{klass.service_name}_signals_total", docstring: "How many signals have been received (and handled)"
169
+ klass.gauge :"#{klass.service_name}_config_ok", docstring: "Whether the last config change was accepted by the server"
170
+
171
+ klass.hook_signal("HUP") do
172
+ logger.info("SIGHUP") { "received SIGHUP, triggering config regeneration" }
173
+ @trigger_regen_w << "."
174
+ end
175
+ end
176
+
158
177
  # Declare a file watch on all instances of the config generator.
159
178
  #
160
179
  # When you're looking to watch a file whose path is well-known and never-changing, you
@@ -186,20 +205,8 @@ class ConfigSkeleton < ServiceSkeleton
186
205
  @watches || []
187
206
  end
188
207
 
189
- # Create a new config generator.
190
- #
191
- # @param env [Hash<String, String>] the environment in which this config
192
- # generator runs. Typically you'll just pass `ENV` in here, but you can
193
- # pass in any hash you like, for testing purposes.
194
- #
195
- def initialize(env)
208
+ def initialize(*_)
196
209
  super
197
-
198
- hook_signal(:HUP) do
199
- logger.info("SIGHUP") { "received SIGHUP, triggering config regeneration" }
200
- @trigger_regen_w << "."
201
- end
202
-
203
210
  initialize_config_skeleton_metrics
204
211
  @trigger_regen_r, @trigger_regen_w = IO.pipe
205
212
  @terminate_r, @terminate_w = IO.pipe
@@ -294,6 +301,7 @@ class ConfigSkeleton < ServiceSkeleton
294
301
  # @see .watch for watching files and directories whose path never changes.
295
302
  #
296
303
  def watch(*files)
304
+ return if ENV["DISABLE_INOTIFY"]
297
305
  files.each do |f|
298
306
  if File.directory?(f)
299
307
  notifier.watch(f, :recursive, :create, :modify, :delete, :move) { |ev| logger.info("#{logloc} watcher") { "detected #{ev.flags.join(", ")} on #{ev.watcher.path}/#{ev.name}; regenerating config" } }
@@ -305,22 +313,11 @@ class ConfigSkeleton < ServiceSkeleton
305
313
 
306
314
  private
307
315
 
308
- # Register metrics in the ServiceSkeleton metrics registry
309
- #
310
- # @return [void]
311
- #
312
316
  def initialize_config_skeleton_metrics
313
- @config_generation = Frankenstein::Request.new("#{service_name}_generation", outgoing: false, description: "config generation", registry: metrics)
314
-
315
- metrics.gauge(:"#{service_name}_last_generation_timestamp", "When the last config generation run was made")
316
- metrics.gauge(:"#{service_name}_last_change_timestamp", "When the config file was last written to")
317
- metrics.counter(:"#{service_name}_reload_total", "How many times we've asked the server to reload")
318
- metrics.counter(:"#{service_name}_signals_total", "How many signals have been received (and handled)")
319
- metrics.gauge(:"#{service_name}_config_ok", "Whether the last config change was accepted by the server")
320
-
321
- metrics.last_generation_timestamp.set({}, 0)
322
- metrics.last_change_timestamp.set({}, 0)
323
- metrics.config_ok.set({}, 0)
317
+ @config_generation = Frankenstein::Request.new("#{self.class.service_name}_generation", outgoing: false, description: "config generation", registry: metrics)
318
+ metrics.last_generation_timestamp.set(0)
319
+ metrics.last_change_timestamp.set(0)
320
+ metrics.config_ok.set(0)
324
321
  end
325
322
 
326
323
  # Write out a config file if one doesn't exist, or do an initial regen run
@@ -335,7 +332,7 @@ class ConfigSkeleton < ServiceSkeleton
335
332
  else
336
333
  logger.info(logloc) { "No existing config file #{config_file} found; writing one" }
337
334
  File.write(config_file, instrumented_config_data)
338
- metrics.last_change_timestamp.set({}, Time.now.to_f)
335
+ metrics.last_change_timestamp.set(Time.now.to_f)
339
336
  end
340
337
  end
341
338
 
@@ -423,7 +420,7 @@ class ConfigSkeleton < ServiceSkeleton
423
420
  #
424
421
  def instrumented_config_data
425
422
  begin
426
- @config_generation.measure { config_data.tap { metrics.last_generation_timestamp.set({}, Time.now.to_f) } }
423
+ @config_generation.measure { config_data.tap { metrics.last_generation_timestamp.set(Time.now.to_f) } }
427
424
  rescue => ex
428
425
  log_exception(ex, logloc) { "Call to config_data raised exception" }
429
426
  nil
@@ -453,6 +450,9 @@ class ConfigSkeleton < ServiceSkeleton
453
450
  #
454
451
  def notifier
455
452
  @notifier ||= INotify::Notifier.new
453
+ rescue NameError
454
+ raise if !ENV["DISABLE_INOTIFY"]
455
+ @notifier ||= Struct.new(:to_io).new(IO.pipe[1]) # Stub for macOS development
456
456
  end
457
457
 
458
458
  # Do the hard yards of actually regenerating the config and performing the reload.
@@ -474,7 +474,7 @@ class ConfigSkeleton < ServiceSkeleton
474
474
  )
475
475
 
476
476
  logger.debug(logloc) { "force? #{force_reload.inspect}" }
477
- tmpfile = Tempfile.new(service_name, File.dirname(config_file))
477
+ tmpfile = Tempfile.new(self.class.service_name, File.dirname(config_file))
478
478
  logger.debug(logloc) { "Tempfile is #{tmpfile.path}" }
479
479
  unless (new_config = instrumented_config_data).nil?
480
480
  File.write(tmpfile.path, new_config)
@@ -512,7 +512,7 @@ class ConfigSkeleton < ServiceSkeleton
512
512
  new_config_hash: new_config_hash
513
513
  )
514
514
  ensure
515
- metrics.last_change_timestamp.set({}, File.stat(config_file).mtime.to_f)
515
+ metrics.last_change_timestamp.set(File.stat(config_file).mtime.to_f)
516
516
  tmpfile.close rescue nil
517
517
  tmpfile.unlink rescue nil
518
518
  end
@@ -562,7 +562,7 @@ class ConfigSkeleton < ServiceSkeleton
562
562
  logger.debug(logloc) { "Restored previous config file" }
563
563
  File.rename(old_copy, config_file)
564
564
  end
565
- metrics.reload_total.increment(status: "failure")
565
+ metrics.reload_total.increment(labels: { status: "failure" })
566
566
 
567
567
  return
568
568
  end
@@ -570,21 +570,21 @@ class ConfigSkeleton < ServiceSkeleton
570
570
  logger.debug(logloc) { "Server reloaded successfully" }
571
571
 
572
572
  if config_ok?
573
- metrics.config_ok.set({}, 1)
573
+ metrics.config_ok.set(1)
574
574
  logger.debug(logloc) { "Configuration successfully updated." }
575
- metrics.reload_total.increment(status: "success")
576
- metrics.last_change_timestamp.set({}, Time.now.to_f)
575
+ metrics.reload_total.increment(labels: { status: "success" })
576
+ metrics.last_change_timestamp.set(Time.now.to_f)
577
577
  else
578
- metrics.config_ok.set({}, 0)
578
+ metrics.config_ok.set(0)
579
579
  if config_was_ok
580
580
  logger.warn(logloc) { "New config file failed config_ok? test; rolling back to previous known-good config" }
581
581
  File.rename(old_copy, config_file)
582
582
  reload_server
583
- metrics.reload_total.increment(status: "bad-config")
583
+ metrics.reload_total.increment(labels: { status: "bad-config" })
584
584
  else
585
585
  logger.warn(logloc) { "New config file failed config_ok? test; leaving new config in place because old config is broken too" }
586
- metrics.reload_total.increment(status: "everything-is-awful")
587
- metrics.last_change_timestamp.set({}, Time.now.to_f)
586
+ metrics.reload_total.increment(labels: { status: "everything-is-awful" })
587
+ metrics.last_change_timestamp.set(Time.now.to_f)
588
588
  end
589
589
  end
590
590
  ensure
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: config_skeleton
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Palmer
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-12-01 00:00:00.000000000 Z
12
+ date: 2021-03-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: diffy
@@ -25,20 +25,6 @@ dependencies:
25
25
  - - "~>"
26
26
  - !ruby/object:Gem::Version
27
27
  version: '3.0'
28
- - !ruby/object:Gem::Dependency
29
- name: frankenstein
30
- requirement: !ruby/object:Gem::Requirement
31
- requirements:
32
- - - "~>"
33
- - !ruby/object:Gem::Version
34
- version: '1.0'
35
- type: :runtime
36
- prerelease: false
37
- version_requirements: !ruby/object:Gem::Requirement
38
- requirements:
39
- - - "~>"
40
- - !ruby/object:Gem::Version
41
- version: '1.0'
42
28
  - !ruby/object:Gem::Dependency
43
29
  name: rb-inotify
44
30
  requirement: !ruby/object:Gem::Requirement
@@ -57,16 +43,16 @@ dependencies:
57
43
  name: service_skeleton
58
44
  requirement: !ruby/object:Gem::Requirement
59
45
  requirements:
60
- - - ">"
46
+ - - "~>"
61
47
  - !ruby/object:Gem::Version
62
- version: 0.a
48
+ version: '1.0'
63
49
  type: :runtime
64
50
  prerelease: false
65
51
  version_requirements: !ruby/object:Gem::Requirement
66
52
  requirements:
67
- - - ">"
53
+ - - "~>"
68
54
  - !ruby/object:Gem::Version
69
- version: 0.a
55
+ version: '1.0'
70
56
  - !ruby/object:Gem::Dependency
71
57
  name: bundler
72
58
  requirement: !ruby/object:Gem::Requirement