service_skeleton 0.0.0.1.ENOTAG → 0.0.0.2.g46c1e0e

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 (69) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +0 -2
  3. data/.rubocop.yml +114 -9
  4. data/.travis.yml +11 -0
  5. data/README.md +153 -279
  6. data/lib/service_skeleton/background_worker.rb +80 -0
  7. data/lib/service_skeleton/config.rb +18 -78
  8. data/lib/service_skeleton/config_variable.rb +8 -29
  9. data/lib/service_skeleton/config_variables.rb +68 -54
  10. data/lib/service_skeleton/error.rb +3 -5
  11. data/lib/service_skeleton/filtering_logger.rb +0 -2
  12. data/lib/service_skeleton/logging_helpers.rb +3 -10
  13. data/lib/service_skeleton/metrics_methods.rb +13 -28
  14. data/lib/service_skeleton/signal_handler.rb +183 -0
  15. data/lib/service_skeleton.rb +145 -22
  16. data/service_skeleton.gemspec +9 -10
  17. metadata +19 -102
  18. data/.editorconfig +0 -7
  19. data/.git-blame-ignore-revs +0 -2
  20. data/.github/workflows/ci.yml +0 -50
  21. data/lib/service_skeleton/config_class.rb +0 -16
  22. data/lib/service_skeleton/config_variable/boolean.rb +0 -21
  23. data/lib/service_skeleton/config_variable/enum.rb +0 -27
  24. data/lib/service_skeleton/config_variable/float.rb +0 -25
  25. data/lib/service_skeleton/config_variable/integer.rb +0 -25
  26. data/lib/service_skeleton/config_variable/kv_list.rb +0 -26
  27. data/lib/service_skeleton/config_variable/path_list.rb +0 -13
  28. data/lib/service_skeleton/config_variable/string.rb +0 -18
  29. data/lib/service_skeleton/config_variable/url.rb +0 -36
  30. data/lib/service_skeleton/config_variable/yaml_file.rb +0 -42
  31. data/lib/service_skeleton/generator.rb +0 -165
  32. data/lib/service_skeleton/metric_method_name.rb +0 -9
  33. data/lib/service_skeleton/runner.rb +0 -46
  34. data/lib/service_skeleton/service_name.rb +0 -20
  35. data/lib/service_skeleton/signal_manager.rb +0 -202
  36. data/lib/service_skeleton/signals_methods.rb +0 -15
  37. data/lib/service_skeleton/ultravisor_children.rb +0 -20
  38. data/lib/service_skeleton/ultravisor_loggerstash.rb +0 -11
  39. data/ultravisor/.yardopts +0 -1
  40. data/ultravisor/Guardfile +0 -9
  41. data/ultravisor/README.md +0 -404
  42. data/ultravisor/lib/ultravisor/child/call.rb +0 -21
  43. data/ultravisor/lib/ultravisor/child/call_receiver.rb +0 -14
  44. data/ultravisor/lib/ultravisor/child/cast.rb +0 -16
  45. data/ultravisor/lib/ultravisor/child/cast_receiver.rb +0 -11
  46. data/ultravisor/lib/ultravisor/child/process_cast_call.rb +0 -39
  47. data/ultravisor/lib/ultravisor/child.rb +0 -481
  48. data/ultravisor/lib/ultravisor/error.rb +0 -25
  49. data/ultravisor/lib/ultravisor/logging_helpers.rb +0 -32
  50. data/ultravisor/lib/ultravisor.rb +0 -216
  51. data/ultravisor/spec/example_group_methods.rb +0 -19
  52. data/ultravisor/spec/example_methods.rb +0 -8
  53. data/ultravisor/spec/spec_helper.rb +0 -52
  54. data/ultravisor/spec/ultravisor/add_child_spec.rb +0 -79
  55. data/ultravisor/spec/ultravisor/child/call_spec.rb +0 -121
  56. data/ultravisor/spec/ultravisor/child/cast_spec.rb +0 -111
  57. data/ultravisor/spec/ultravisor/child/id_spec.rb +0 -21
  58. data/ultravisor/spec/ultravisor/child/new_spec.rb +0 -152
  59. data/ultravisor/spec/ultravisor/child/restart_delay_spec.rb +0 -40
  60. data/ultravisor/spec/ultravisor/child/restart_spec.rb +0 -70
  61. data/ultravisor/spec/ultravisor/child/run_spec.rb +0 -95
  62. data/ultravisor/spec/ultravisor/child/shutdown_spec.rb +0 -124
  63. data/ultravisor/spec/ultravisor/child/spawn_spec.rb +0 -107
  64. data/ultravisor/spec/ultravisor/child/unsafe_instance_spec.rb +0 -55
  65. data/ultravisor/spec/ultravisor/child/wait_spec.rb +0 -32
  66. data/ultravisor/spec/ultravisor/new_spec.rb +0 -71
  67. data/ultravisor/spec/ultravisor/remove_child_spec.rb +0 -49
  68. data/ultravisor/spec/ultravisor/run_spec.rb +0 -334
  69. data/ultravisor/spec/ultravisor/shutdown_spec.rb +0 -106
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ServiceSkeleton
4
- module ConfigClass
5
- Undefined = Module.new
6
- private_constant :Undefined
7
-
8
- def config_class(klass = Undefined)
9
- unless klass == Undefined
10
- @config_class = klass
11
- end
12
-
13
- @config_class
14
- end
15
- end
16
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "service_skeleton/config_variable"
4
-
5
- class ServiceSkeleton::ConfigVariable::Boolean < ServiceSkeleton::ConfigVariable
6
- private
7
-
8
- def pluck_value(env)
9
- maybe_default(env) do
10
- case env[@name.to_s]
11
- when /\A(no|n|off|0|false)\z/i
12
- false
13
- when /\A(yes|y|on|1|true)\z/i
14
- true
15
- else
16
- raise ServiceSkeleton::Error::InvalidEnvironmentError,
17
- "Value #{env[@name.to_s].inspect} for environment variable #{@name} is not a valid boolean value"
18
- end
19
- end
20
- end
21
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "service_skeleton/config_variable"
4
-
5
- class ServiceSkeleton::ConfigVariable::Enum < ServiceSkeleton::ConfigVariable
6
- private
7
-
8
- def pluck_value(env)
9
- maybe_default(env) do
10
- v = env[@name.to_s]
11
-
12
- if @opts[:values].is_a?(Array)
13
- unless @opts[:values].include?(v)
14
- raise ServiceSkeleton::Error::InvalidEnvironmentError,
15
- "Invalid value for #{@name}; must be one of #{@opts[:values].join(", ")}"
16
- end
17
- v
18
- elsif @opts[:values].is_a?(Hash)
19
- unless @opts[:values].keys.include?(v)
20
- raise ServiceSkeleton::Error::InvalidEnvironmentError,
21
- "Invalid value for #{@name}; must be one of #{@opts[:values].keys.join(", ")}"
22
- end
23
- @opts[:values][v]
24
- end
25
- end
26
- end
27
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "service_skeleton/config_variable"
4
-
5
- class ServiceSkeleton::ConfigVariable::Float < ServiceSkeleton::ConfigVariable
6
- private
7
-
8
- def pluck_value(env)
9
- maybe_default(env) do
10
- value = env[@name.to_s]
11
-
12
- if value =~ /\A-?\d+.?\d*\z/
13
- value.to_f.tap do |f|
14
- unless @opts[:range].include?(f)
15
- raise ServiceSkeleton::Error::InvalidEnvironmentError,
16
- "Value #{f} for environment variable #{@name} is out of the valid range (must be between #{@opts[:range].first} and #{@opts[:range].last} inclusive)"
17
- end
18
- end
19
- else
20
- raise ServiceSkeleton::Error::InvalidEnvironmentError,
21
- "Value #{value.inspect} for environment variable #{@name} is not a valid numeric value"
22
- end
23
- end
24
- end
25
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "service_skeleton/config_variable"
4
-
5
- class ServiceSkeleton::ConfigVariable::Integer < ServiceSkeleton::ConfigVariable
6
- private
7
-
8
- def pluck_value(env)
9
- maybe_default(env) do
10
- value = env[@name.to_s]
11
-
12
- if value =~ /\A-?\d+\z/
13
- value.to_i.tap do |i|
14
- unless @opts[:range].include?(i)
15
- raise ServiceSkeleton::Error::InvalidEnvironmentError,
16
- "Value #{i} for environment variable #{@name} is out of the valid range (must be between #{@opts[:range].first} and #{@opts[:range].last} inclusive)"
17
- end
18
- end
19
- else
20
- raise ServiceSkeleton::Error::InvalidEnvironmentError,
21
- "Value #{value.inspect} for environment variable #{@name} is not a valid integer value"
22
- end
23
- end
24
- end
25
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "service_skeleton/config_variable"
4
-
5
- class ServiceSkeleton::ConfigVariable::KVList < ServiceSkeleton::ConfigVariable
6
- def redact!(env)
7
- env.keys.each { |k| env[k] = "*SENSITIVE*" if k =~ @opts[:key_pattern] }
8
- end
9
-
10
- private
11
-
12
- def pluck_value(env)
13
- matches = env.select { |k, _| k.to_s =~ @opts[:key_pattern] }
14
-
15
- if matches.empty?
16
- if @opts.has_key?(:default)
17
- @opts[:default]
18
- else
19
- raise ServiceSkeleton::Error::InvalidEnvironmentError,
20
- "no keys for key-value list #{@name} specified"
21
- end
22
- else
23
- matches.transform_keys { |k| @opts[:key_pattern].match(k.to_s)[1].to_sym }
24
- end
25
- end
26
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "service_skeleton/config_variable"
4
-
5
- class ServiceSkeleton::ConfigVariable::PathList < ServiceSkeleton::ConfigVariable
6
- private
7
-
8
- def pluck_value(env)
9
- maybe_default(env) do
10
- env[@name.to_s].split(":")
11
- end
12
- end
13
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "service_skeleton/config_variable"
4
-
5
- class ServiceSkeleton::ConfigVariable::String < ServiceSkeleton::ConfigVariable
6
- private
7
-
8
- def pluck_value(env)
9
- maybe_default(env) do
10
- env[@name.to_s].tap do |s|
11
- if @opts[:match] && s !~ @opts[:match]
12
- raise ServiceSkeleton::Error::InvalidEnvironmentError,
13
- "Value for #{@name} must match #{@opts[:match]}"
14
- end
15
- end
16
- end
17
- end
18
- end
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "service_skeleton/config_variable"
4
-
5
- class ServiceSkeleton::ConfigVariable::URL < ServiceSkeleton::ConfigVariable
6
- def redact?(env)
7
- !!(env.has_key?(@name.to_s) && (@opts[:sensitive] || URI(env[@name.to_s] || "").password))
8
- end
9
-
10
- def redact!(env)
11
- if env.has_key?(@name.to_s)
12
- super
13
- uri = URI(env[@name.to_s])
14
- if uri.password
15
- uri.password = "*REDACTED*"
16
- env[@name.to_s] = uri.to_s
17
- end
18
- end
19
- end
20
-
21
- private
22
-
23
- def pluck_value(env)
24
- maybe_default(env) do
25
- begin
26
- v = env[@name.to_s]
27
- URI(v)
28
- rescue URI::InvalidURIError
29
- raise ServiceSkeleton::Error::InvalidEnvironmentError,
30
- "Value for #{@name} (#{v}) does not appear to be a valid URL"
31
- end
32
-
33
- v
34
- end
35
- end
36
- end
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "yaml"
4
-
5
- require "service_skeleton/config_variable"
6
-
7
- class ServiceSkeleton::ConfigVariable::YamlFile < ServiceSkeleton::ConfigVariable
8
- def redact!(env)
9
- if env.has_key?(@name.to_s)
10
- if File.world_readable?(env[@name.to_s])
11
- raise ServiceSkeleton::Error::InvalidEnvironmentError,
12
- "Sensitive file #{env[@name.to_s]} is world-readable!"
13
- end
14
-
15
- super
16
- end
17
- end
18
-
19
- private
20
-
21
- def pluck_value(env)
22
- maybe_default(env) do
23
- begin
24
- val = YAML.safe_load(File.read(env[@name.to_s]))
25
- if @opts[:klass]
26
- val = @opts[:klass].new(val)
27
- end
28
-
29
- val
30
- rescue Errno::ENOENT
31
- raise ServiceSkeleton::Error::InvalidEnvironmentError,
32
- "YAML file #{env[@name.to_s]} does not exist"
33
- rescue Errno::EPERM
34
- raise ServiceSkeleton::Error::InvalidEnvironmentError,
35
- "Do not have permission to read YAML file #{env[@name.to_s]}"
36
- rescue Psych::SyntaxError => ex
37
- raise ServiceSkeleton::Error::InvalidEnvironmentError,
38
- "Invalid YAML syntax: #{ex.message}"
39
- end
40
- end
41
- end
42
- end
@@ -1,165 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "config"
4
- require_relative "signal_manager"
5
- require_relative "ultravisor_loggerstash"
6
-
7
- require "frankenstein/ruby_gc_metrics"
8
- require "frankenstein/ruby_vm_metrics"
9
- require "frankenstein/process_metrics"
10
- require "frankenstein/server"
11
- require "prometheus/client/registry"
12
- require "sigdump"
13
- require_relative "../../ultravisor/lib/ultravisor"
14
-
15
- module ServiceSkeleton
16
- module Generator
17
- def generate(config:, metrics_registry:, service_metrics:, service_signal_handlers:)
18
- Ultravisor.new(logger: config.logger).tap do |ultravisor|
19
- initialize_metrics(ultravisor, config, metrics_registry, service_metrics)
20
- initialize_loggerstash(ultravisor, config, metrics_registry)
21
- initialize_signals(ultravisor, config, service_signal_handlers, metrics_registry)
22
- end
23
- end
24
-
25
- private
26
-
27
- def initialize_metrics(ultravisor, config, registry, metrics)
28
- Frankenstein::RubyGCMetrics.register(registry)
29
- Frankenstein::RubyVMMetrics.register(registry)
30
- Frankenstein::ProcessMetrics.register(registry)
31
-
32
- metrics.each do |m|
33
- registry.register(m)
34
-
35
- method_name = m.method_name(config.service_name)
36
-
37
- if registry.singleton_class.method_defined?(method_name)
38
- raise ServiceSkeleton::Error::InvalidMetricNameError,
39
- "Metric method #{method_name} is already defined"
40
- end
41
-
42
- registry.define_singleton_method(method_name) do
43
- m
44
- end
45
- end
46
-
47
- if config.metrics_port
48
- config.pre_run_logger.info(config.service_name) { "Starting metrics server on port #{config.metrics_port}" }
49
- ultravisor.add_child(
50
- id: :metrics_server,
51
- klass: Frankenstein::Server,
52
- method: :run,
53
- args: [
54
- port: config.metrics_port,
55
- logger: config.logger,
56
- metrics_prefix: :"#{config.service_name}_metrics_server",
57
- registry: registry,
58
- ]
59
- )
60
- end
61
- end
62
-
63
- def initialize_loggerstash(ultravisor, config, registry)
64
- if config.logstash_server && !config.logstash_server.empty?
65
- config.pre_run_logger.info(config.service_name) { "Configuring loggerstash to send to #{config.logstash_server}" }
66
-
67
- ultravisor.add_child(
68
- id: :logstash_writer,
69
- klass: LogstashWriter,
70
- method: :run,
71
- args: [
72
- server_name: config.logstash_server,
73
- metrics_registry: registry,
74
- logger: config.logger,
75
- ],
76
- access: :unsafe
77
- )
78
-
79
- config.logger.singleton_class.prepend(Loggerstash::Mixin)
80
-
81
- config.logger.instance_variable_set(:@ultravisor, ultravisor)
82
- config.logger.singleton_class.prepend(ServiceSkeleton::UltravisorLoggerstash)
83
- end
84
- end
85
-
86
- def initialize_signals(ultravisor, config, service_signals, metrics_registry)
87
- counter = metrics_registry.counter(:"#{config.service_name}_signals_handled_total", docstring: "How many of each signal have been handled", labels: %i{signal})
88
-
89
- ultravisor.add_child(
90
- id: :signal_manager,
91
- klass: ServiceSkeleton::SignalManager,
92
- method: :run,
93
- args: [
94
- logger: config.logger,
95
- counter: counter,
96
- signals: global_signals(ultravisor, config.logger) + wrap_service_signals(service_signals, ultravisor),
97
- ],
98
- shutdown: {
99
- method: :shutdown,
100
- timeout: 1,
101
- }
102
- )
103
- end
104
-
105
- def global_signals(ultravisor, logger)
106
- # For mysterious reasons of mystery, simplecov doesn't recognise these
107
- # procs as being called, even though there are definitely tests for
108
- # them. So...
109
- #:nocov:
110
- [
111
- [
112
- "USR1",
113
- ->() {
114
- logger.level -= 1 unless logger.level == Logger::DEBUG
115
- logger.info($0) { "Received SIGUSR1; log level is now #{Logger::SEV_LABEL[logger.level]}." }
116
- }
117
- ],
118
- [
119
- "USR2",
120
- ->() {
121
- logger.level += 1 unless logger.level == Logger::ERROR
122
- logger.info($0) { "Received SIGUSR2; log level is now #{Logger::SEV_LABEL[logger.level]}." }
123
- }
124
- ],
125
- [
126
- "HUP",
127
- ->() {
128
- logger.reopen
129
- logger.info($0) { "Received SIGHUP; log file handle reopened" }
130
- }
131
- ],
132
- [
133
- "QUIT",
134
- ->() { Sigdump.dump("+") }
135
- ],
136
- [
137
- "INT",
138
- ->() {
139
- ultravisor.shutdown(wait: false, force: !!@shutting_down)
140
- @shutting_down = true
141
- }
142
- ],
143
- [
144
- "TERM",
145
- ->() {
146
- ultravisor.shutdown(wait: false, force: !!@shutting_down)
147
- @shutting_down = true
148
- }
149
- ]
150
- ]
151
- #:nocov:
152
- end
153
-
154
- def wrap_service_signals(signals, ultravisor)
155
- [].tap do |signal_list|
156
- signals.each do |service_name, sigs|
157
- sigs.each do |sig, proc|
158
- wrapped_proc = ->() { ultravisor[service_name.to_sym].unsafe_instance.instance_eval(&proc) }
159
- signal_list << [sig, wrapped_proc]
160
- end
161
- end
162
- end
163
- end
164
- end
165
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ServiceSkeleton
4
- module MetricMethodName
5
- def method_name(svc_name)
6
- @name.to_s.gsub(/\A#{Regexp.quote(svc_name)}_/i, '').downcase
7
- end
8
- end
9
- end
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "config"
4
- require_relative "logging_helpers"
5
- require_relative "signal_manager"
6
-
7
- require "frankenstein/ruby_gc_metrics"
8
- require "frankenstein/ruby_vm_metrics"
9
- require "frankenstein/process_metrics"
10
- require "frankenstein/server"
11
- require "prometheus/client/registry"
12
- require "sigdump"
13
- require_relative "../../ultravisor/lib/ultravisor"
14
-
15
- module ServiceSkeleton
16
- class Runner
17
- include ServiceSkeleton::LoggingHelpers
18
-
19
- def initialize(klass, env)
20
- @config = (klass.config_class || ServiceSkeleton::Config).new(env, klass.service_name, klass.registered_variables)
21
- @logger = @config.logger
22
-
23
- @metrics_registry = Prometheus::Client::Registry.new
24
-
25
- @ultravisor = ServiceSkeleton.generate(
26
- config: @config,
27
- metrics_registry: @metrics_registry,
28
- service_metrics: klass.registered_metrics,
29
- service_signal_handlers: { klass.service_name.to_sym => klass.registered_signal_handlers }
30
- )
31
-
32
- klass.register_ultravisor_children(@ultravisor, config: @config, metrics_registry: @metrics_registry)
33
- end
34
-
35
- def run
36
- @config.pre_run_logger.info(logloc) { "Starting service #{@config.service_name}" }
37
- @config.pre_run_logger.info(logloc) { (["Environment:"] + @config.env.map { |k, v| "#{k}=#{v.inspect}" }).join("\n ") }
38
-
39
- @ultravisor.run
40
- end
41
-
42
- private
43
-
44
- attr_reader :logger
45
- end
46
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ServiceSkeleton
4
- module ServiceName
5
- def service_name
6
- service_name_from_class(self)
7
- end
8
-
9
- private
10
-
11
- def service_name_from_class(klass)
12
- klass.to_s
13
- .gsub("::", "_")
14
- .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
15
- .gsub(/([a-z\d])([A-Z])/, '\1_\2')
16
- .downcase
17
- .gsub(/[^a-zA-Z0-9_]/, "_")
18
- end
19
- end
20
- end