Skip to content

Commit 516985d

Browse files
committed
Don't pollute global namespace
Puma::Plugin.create does a class_eval which means we were leaking top-level Systemd and Status constants. Instead, construct a class manually, namespace the constants inside, and register the plugin directly in the same manner. Fixes #9
1 parent 12015ad commit 516985d

File tree

1 file changed

+109
-107
lines changed

1 file changed

+109
-107
lines changed

lib/puma/plugin/systemd.rb

Lines changed: 109 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,115 @@
1010
# you know when your system has *actually* started and is ready to take
1111
# requests.
1212
#
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+
14122
# Give us a way to talk to systemd.
15123
#
16124
# It'd be great to use systemd-notify for the whole shebang, but there's a
@@ -160,110 +268,4 @@ def to_s
160268
end
161269
end
162270
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
269271
end

0 commit comments

Comments
 (0)