|
10 | 10 | # you know when your system has *actually* started and is ready to take |
11 | 11 | # requests. |
12 | 12 | # |
13 | | -Puma::Plugin.create do |
| 13 | +class Puma::Plugin::Systemd |
| 14 | + Puma::Plugins.register("systemd", self) |
| 15 | + |
| 16 | + # Puma creates the plugin when encountering `plugin` in the config. |
| 17 | + def initialize(loader) |
| 18 | + # This is a Puma::PluginLoader |
| 19 | + @loader = loader |
| 20 | + end |
| 21 | + |
| 22 | + # We can start doing something when we have a launcher: |
| 23 | + def start(launcher) |
| 24 | + @launcher = launcher |
| 25 | + |
| 26 | + # Log relevant ENV in debug |
| 27 | + @launcher.events.debug "systemd: NOTIFY_SOCKET=#{ENV["NOTIFY_SOCKET"].inspect}" |
| 28 | + @launcher.events.debug "systemd: WATCHDOG_PID=#{ENV["WATCHDOG_PID"].inspect}" |
| 29 | + @launcher.events.debug "systemd: WATCHDOG_USEC=#{ENV["WATCHDOG_USEC"].inspect}" |
| 30 | + |
| 31 | + # Only install hooks if systemd is present, the systemd is booted by |
| 32 | + # systemd, and systemd has asked us to notify it of events. |
| 33 | + @systemd = Systemd.new |
| 34 | + if @systemd.present? && @systemd.booted? && @systemd.notify? |
| 35 | + @launcher.events.debug "systemd: detected running inside systemd, registering hooks" |
| 36 | + register_hooks |
| 37 | + else |
| 38 | + @launcher.events.debug "systemd: not running within systemd, doing nothing" |
| 39 | + end |
| 40 | + end |
| 41 | + |
| 42 | + private |
| 43 | + |
| 44 | + # Are we a single process worker, or do we have worker processes? |
| 45 | + # |
| 46 | + # Copied from puma, it's private: |
| 47 | + # https://github.com/puma/puma/blob/v3.6.0/lib/puma/launcher.rb#L267-L269 |
| 48 | + # |
| 49 | + def clustered? |
| 50 | + (@launcher.options[:workers] || 0) > 0 |
| 51 | + end |
| 52 | + |
| 53 | + def register_hooks |
| 54 | + (@launcher.config.options[:on_restart] ||= []) << method(:restart) |
| 55 | + @launcher.events.on_booted(&method(:booted)) |
| 56 | + in_background(&method(:status_loop)) |
| 57 | + in_background(&method(:watchdog_loop)) if @systemd.watchdog? |
| 58 | + end |
| 59 | + |
| 60 | + def booted |
| 61 | + @launcher.events.log "* systemd: notify ready" |
| 62 | + begin |
| 63 | + @systemd.notify_ready |
| 64 | + rescue |
| 65 | + @launcher.events.error "! systemd: notify ready failed:\n #{$!.to_s}\n #{$!.backtrace.join("\n ")}" |
| 66 | + end |
| 67 | + end |
| 68 | + |
| 69 | + def restart(launcher) |
| 70 | + @launcher.events.log "* systemd: notify reloading" |
| 71 | + begin |
| 72 | + @systemd.notify_reloading |
| 73 | + rescue |
| 74 | + @launcher.events.error "! systemd: notify reloading failed:\n #{$!.to_s}\n #{$!.backtrace.join("\n ")}" |
| 75 | + end |
| 76 | + end |
| 77 | + |
| 78 | + def fetch_stats |
| 79 | + JSON.parse(@launcher.stats) |
| 80 | + end |
| 81 | + |
| 82 | + def status |
| 83 | + Status.new(fetch_stats) |
| 84 | + end |
| 85 | + |
| 86 | + # Update systemd status event second or so |
| 87 | + def status_loop |
| 88 | + loop do |
| 89 | + @launcher.events.debug "systemd: notify status" |
| 90 | + begin |
| 91 | + @systemd.notify_status(status.to_s) |
| 92 | + rescue |
| 93 | + @launcher.events.error "! systemd: notify status failed:\n #{$!.to_s}\n #{$!.backtrace.join("\n ")}" |
| 94 | + ensure |
| 95 | + sleep 1 |
| 96 | + end |
| 97 | + end |
| 98 | + end |
| 99 | + |
| 100 | + # If watchdog is configured we'll send a ping at about half the timeout |
| 101 | + # configured in systemd as recommended in the docs. |
| 102 | + def watchdog_loop |
| 103 | + @launcher.events.log "* systemd: watchdog detected (#{@systemd.watchdog_usec}usec)" |
| 104 | + |
| 105 | + # Ruby wants seconds, and the docs suggest notifying halfway through the |
| 106 | + # timeout. |
| 107 | + sleep_seconds = @systemd.watchdog_usec / 1000.0 / 1000.0 / 2.0 |
| 108 | + |
| 109 | + loop do |
| 110 | + begin |
| 111 | + @launcher.events.debug "systemd: notify watchdog" |
| 112 | + @systemd.notify_watchdog |
| 113 | + rescue |
| 114 | + @launcher.events.error "! systemd: notify watchdog failed:\n #{$!.to_s}\n #{$!.backtrace.join("\n ")}" |
| 115 | + ensure |
| 116 | + @launcher.events.debug "systemd: sleeping #{sleep_seconds}s" |
| 117 | + sleep sleep_seconds |
| 118 | + end |
| 119 | + end |
| 120 | + end |
| 121 | + |
14 | 122 | # Give us a way to talk to systemd. |
15 | 123 | # |
16 | 124 | # It'd be great to use systemd-notify for the whole shebang, but there's a |
@@ -160,110 +268,4 @@ def to_s |
160 | 268 | end |
161 | 269 | end |
162 | 270 | end |
163 | | - |
164 | | - # Puma creates the plugin when encountering `plugin` in the config. |
165 | | - def initialize(loader) |
166 | | - # This is a Puma::PluginLoader |
167 | | - @loader = loader |
168 | | - end |
169 | | - |
170 | | - # We can start doing something when we have a launcher: |
171 | | - def start(launcher) |
172 | | - @launcher = launcher |
173 | | - |
174 | | - # Log relevant ENV in debug |
175 | | - @launcher.events.debug "systemd: NOTIFY_SOCKET=#{ENV["NOTIFY_SOCKET"].inspect}" |
176 | | - @launcher.events.debug "systemd: WATCHDOG_PID=#{ENV["WATCHDOG_PID"].inspect}" |
177 | | - @launcher.events.debug "systemd: WATCHDOG_USEC=#{ENV["WATCHDOG_USEC"].inspect}" |
178 | | - |
179 | | - # Only install hooks if systemd is present, the systemd is booted by |
180 | | - # systemd, and systemd has asked us to notify it of events. |
181 | | - @systemd = Systemd.new |
182 | | - if @systemd.present? && @systemd.booted? && @systemd.notify? |
183 | | - @launcher.events.debug "systemd: detected running inside systemd, registering hooks" |
184 | | - register_hooks |
185 | | - else |
186 | | - @launcher.events.debug "systemd: not running within systemd, doing nothing" |
187 | | - end |
188 | | - end |
189 | | - |
190 | | - private |
191 | | - |
192 | | - # Are we a single process worker, or do we have worker processes? |
193 | | - # |
194 | | - # Copied from puma, it's private: |
195 | | - # https://github.com/puma/puma/blob/v3.6.0/lib/puma/launcher.rb#L267-L269 |
196 | | - # |
197 | | - def clustered? |
198 | | - (@launcher.options[:workers] || 0) > 0 |
199 | | - end |
200 | | - |
201 | | - def register_hooks |
202 | | - (@launcher.config.options[:on_restart] ||= []) << method(:restart) |
203 | | - @launcher.events.on_booted(&method(:booted)) |
204 | | - in_background(&method(:status_loop)) |
205 | | - in_background(&method(:watchdog_loop)) if @systemd.watchdog? |
206 | | - end |
207 | | - |
208 | | - def booted |
209 | | - @launcher.events.log "* systemd: notify ready" |
210 | | - begin |
211 | | - @systemd.notify_ready |
212 | | - rescue |
213 | | - @launcher.events.error "! systemd: notify ready failed:\n #{$!.to_s}\n #{$!.backtrace.join("\n ")}" |
214 | | - end |
215 | | - end |
216 | | - |
217 | | - def restart(launcher) |
218 | | - @launcher.events.log "* systemd: notify reloading" |
219 | | - begin |
220 | | - @systemd.notify_reloading |
221 | | - rescue |
222 | | - @launcher.events.error "! systemd: notify reloading failed:\n #{$!.to_s}\n #{$!.backtrace.join("\n ")}" |
223 | | - end |
224 | | - end |
225 | | - |
226 | | - def fetch_stats |
227 | | - JSON.parse(@launcher.stats) |
228 | | - end |
229 | | - |
230 | | - def status |
231 | | - Status.new(fetch_stats) |
232 | | - end |
233 | | - |
234 | | - # Update systemd status event second or so |
235 | | - def status_loop |
236 | | - loop do |
237 | | - @launcher.events.debug "systemd: notify status" |
238 | | - begin |
239 | | - @systemd.notify_status(status.to_s) |
240 | | - rescue |
241 | | - @launcher.events.error "! systemd: notify status failed:\n #{$!.to_s}\n #{$!.backtrace.join("\n ")}" |
242 | | - ensure |
243 | | - sleep 1 |
244 | | - end |
245 | | - end |
246 | | - end |
247 | | - |
248 | | - # If watchdog is configured we'll send a ping at about half the timeout |
249 | | - # configured in systemd as recommended in the docs. |
250 | | - def watchdog_loop |
251 | | - @launcher.events.log "* systemd: watchdog detected (#{@systemd.watchdog_usec}usec)" |
252 | | - |
253 | | - # Ruby wants seconds, and the docs suggest notifying halfway through the |
254 | | - # timeout. |
255 | | - sleep_seconds = @systemd.watchdog_usec / 1000.0 / 1000.0 / 2.0 |
256 | | - |
257 | | - loop do |
258 | | - begin |
259 | | - @launcher.events.debug "systemd: notify watchdog" |
260 | | - @systemd.notify_watchdog |
261 | | - rescue |
262 | | - @launcher.events.error "! systemd: notify watchdog failed:\n #{$!.to_s}\n #{$!.backtrace.join("\n ")}" |
263 | | - ensure |
264 | | - @launcher.events.debug "systemd: sleeping #{sleep_seconds}s" |
265 | | - sleep sleep_seconds |
266 | | - end |
267 | | - end |
268 | | - end |
269 | 271 | end |
0 commit comments