puma-plugin-systemd 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d95c99f2ab14c60a427044c641e5f234f893ec37e93109e1f397ca1a9c85c963
4
+ data.tar.gz: c07ddd26e9c4e5d696630acce253a40e103365d5f027f873348e91af0eba05f8
5
+ SHA512:
6
+ metadata.gz: 2e60e0544635e3dfe77ba87ce582735b4aeeef686ee1ffcb46cf9f8a00cfd279c4817557c8917a387f457406b762b362450a43017e1a1e12ff3f585e55069344
7
+ data.tar.gz: f072b1fd233dcec72b18265012edb6d48e3d4c52c2e2752ceb2ec0e103151b22a13a8eed28f42f8e914615fae82289f7caf6a4b0e8b491d96a6775e55409c91b
checksums.yaml.gz.sig ADDED
@@ -0,0 +1,2 @@
1
+ #I�Z�Q%'�$[S��F��Fc��T����3 ��Z�|��P�)lݯm ������!�W�R?���QH+s.�?򼷿z(t'�݆�[�T�]F�J�mcG��Z
2
+ d��5|��������^��4Y�C5�Kf2$���k�.� n��@2B$�g4���!H� MZU6�^�����S2�a�;����,
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Samuel Cochran
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,127 @@
1
+ # Puma Systemd Plugin
2
+
3
+ [Puma][puma] integration with [systemd](systemd) for better daemonising under
4
+ modern Linux systemds: notify, status, watchdog.
5
+
6
+ * Notify systemd when puma has booted and is [ready to handle requests][ready]
7
+ * Publish puma stats as systemd [service status][status] for a quick overview
8
+ * Use the [watchdog][watchdog] to make sure your puma processes are healthy
9
+ and haven't locked up or run out of memory
10
+
11
+ Puma already natively supports [socket activation][socket-activation].
12
+
13
+ [puma]: https://github.com/puma/puma
14
+ [systemd]: https://www.freedesktop.org/wiki/Software/systemd/
15
+ [ready]: https://www.freedesktop.org/software/systemd/man/sd_notify.html#READY=1
16
+ [status]: https://www.freedesktop.org/software/systemd/man/sd_notify.html#STATUS=...
17
+ [watchdog]: https://www.freedesktop.org/software/systemd/man/sd_notify.html#WATCHDOG=1
18
+ [socket-activation]: http://github.com/puma/puma/blob/master/docs/systemd.md#socket-activation
19
+
20
+ ## Installation
21
+
22
+ Add this gem to your Gemfile with puma and then bundle:
23
+
24
+ ```ruby
25
+ gem "puma"
26
+ gem "puma-plugin-systemd"
27
+ ```
28
+
29
+ Add it to your puma config:
30
+
31
+ ```ruby
32
+ # config/puma.rb
33
+
34
+ bind "http://127.0.0.1:9292"
35
+
36
+ workers 2
37
+ threads 8, 16
38
+
39
+ plugin :systemd
40
+ ```
41
+
42
+ ## Usage
43
+
44
+ ### Notify
45
+
46
+ Make sure puma is being started using a [systemd service unit][systemd-service]
47
+ with `Type=notify`, something like:
48
+
49
+ ```ini
50
+ # puma.service
51
+ [Service]
52
+ Type=notify
53
+ User=puma
54
+ WorkingDirectory=/app
55
+ ExecStart=/app/bin/puma -C config/puma.rb -e production
56
+ ExecReload=/bin/kill -USR1 $MAINPID
57
+ ExecRestart=/bin/kill -USR2 $MAINPID
58
+ Restart=always
59
+ KillMode=mixed
60
+ ```
61
+
62
+ [systemd-service]: https://www.freedesktop.org/software/systemd/man/systemd.service.html
63
+
64
+ ### Status
65
+
66
+ Running in notify mode as above should just start publishing puma stats as
67
+ systemd status. Running `systemctl status puma.service` or similar should
68
+ result in a Status line in your status output:
69
+
70
+ ```
71
+ app@web:~$ sudo systemctl status puma.service
72
+ ● puma.service - puma
73
+ Loaded: loaded (/etc/systemd/system/puma.service; enabled; vendor preset: enabled)
74
+ Active: active (running) since Mon 2016-10-24 00:26:55 UTC; 5s ago
75
+ Main PID: 32234 (ruby2.3)
76
+ Status: "puma 3.6.0 cluster: 2/2 workers: 16 threads, 0 backlog"
77
+ Tasks: 10
78
+ Memory: 167.9M
79
+ CPU: 7.150s
80
+ CGroup: /system.slice/puma.service
81
+ ├─32234 puma 3.6.0 (unix:///app/tmp/sockets/puma.sock?backlog=1024) [app]
82
+ ├─32251 puma: cluster worker 0: 32234 [app]
83
+ └─32253 puma: cluster worker 1: 32234 [app]
84
+
85
+ Oct 24 00:26:10 web systemd[30762]: puma.service: Executing: /app/bin/puma -C config/puma.rb -e production
86
+ Oct 24 00:54:58 web puma[32234]: [32234] Puma starting in cluster mode...
87
+ Oct 24 00:54:58 web puma[32234]: [32234] * Version 3.6.0 (ruby 2.3.1-p112), codename: Sleepy Sunday Serenity
88
+ Oct 24 00:54:58 web puma[32234]: [32234] * Min threads: 8, max threads: 64
89
+ Oct 24 00:26:55 web puma[32234]: [32234] * Environment: production
90
+ Oct 24 00:26:55 web puma[32234]: [32234] * Process workers: 2
91
+ Oct 24 00:26:55 web puma[32234]: [32234] * Phased restart available
92
+ Oct 24 00:26:55 web puma[32234]: [32234] * Listening on unix:///app/tmp/sockets/puma.sock?backlog=1024
93
+ Oct 24 00:26:55 web puma[32234]: [32234] Use Ctrl-C to stop
94
+ Oct 24 00:26:55 web puma[32234]: [32234] * systemd: notify ready
95
+ Oct 24 00:26:55 web puma[32234]: [32251] + Gemfile in context: /app/Gemfile
96
+ Oct 24 00:26:55 web systemd[1]: Started puma.
97
+ Oct 24 00:26:55 web puma[32234]: [32234] * systemd: watchdog detected (30000000usec)
98
+ Oct 24 00:26:55 web puma[32234]: [32253] + Gemfile in context: /app/Gemfile
99
+ ```
100
+
101
+ ### Watchdog
102
+
103
+ Adding a `WatchdogSec=30` or similar to your systemd service file will tell
104
+ puma systemd to ping systemd at half the specified interval to ensure the
105
+ service is running and healthy.
106
+
107
+ ## Development
108
+
109
+ After checking out the repo, run `script/setup` to install dependencies. Then,
110
+ run `rake test` to run the tests. You can also run `script/console` for an
111
+ interactive prompt that will allow you to experiment.
112
+
113
+ To install this gem onto your local machine, run `bundle exec rake install`. To
114
+ release a new version, update the version number in `version.rb`, and then run
115
+ `bundle exec rake release`, which will create a git tag for the version, push
116
+ git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
117
+
118
+ ## Contributing
119
+
120
+ Bug reports and pull requests are welcome on GitHub at
121
+ https://github.com/sj26/puma-plugin-systemd.
122
+
123
+ ## License
124
+
125
+ The gem is available as open source under the terms of the [MIT License][license].
126
+
127
+ [license]: http://opensource.org/licenses/MIT
@@ -0,0 +1,7 @@
1
+ module Puma
2
+ module Plugin
3
+ module Systemd
4
+ VERSION = "0.1.1"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,270 @@
1
+ # coding: utf-8, frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "puma/plugin"
5
+
6
+ require_relative "systemd/version"
7
+
8
+ # Puma systemd plugin
9
+ #
10
+ # Uses systemd notify to let systemd know a little about what puma is doing, so
11
+ # you know when your system has *actually* started and is ready to take
12
+ # requests.
13
+ #
14
+ Puma::Plugin.create do
15
+ # Give us a way to talk to systemd.
16
+ #
17
+ # It'd be great to use systemd-notify for the whole shebang, but there's a
18
+ # critical error introducing a race condition:
19
+ #
20
+ # https://github.com/systemd/systemd/issues/2739
21
+ #
22
+ # systemd-notify docs:
23
+ #
24
+ # https://www.freedesktop.org/software/systemd/man/systemd-notify.html
25
+ #
26
+ # We could use sd-daemon (sd_notify and friends) but they require a C
27
+ # extension, and are really just fancy wrappers for blatting datagrams at a
28
+ # socket advertised via ENV. See the docs:
29
+ #
30
+ # https://www.freedesktop.org/software/systemd/man/sd-daemon.html
31
+ #
32
+ class Systemd
33
+ # Do we have a systemctl binary? This is a good indicator whether systemd
34
+ # is installed at all.
35
+ def present?
36
+ ENV["PATH"].split(":").any? { |dir| File.exists?(File.join(dir, "systemctl")) }
37
+ end
38
+
39
+ # Is the system currently booted with systemd?
40
+ #
41
+ # We could check for the systemd run directory directly, but we can't be
42
+ # absolutely sure of it's location and breaks encapsulation. We can be sure
43
+ # that systemctl is present on a systemd system and understand whether
44
+ # systemd is running. The systemd-notify binary actually recommends this.
45
+ #
46
+ # An alternate way to check for this state is to call systemctl(1) with
47
+ # the is-system-running command. It will return "offline" if the system
48
+ # was not booted with systemd.
49
+ #
50
+ # See also sd_booted:
51
+ #
52
+ # https://www.freedesktop.org/software/systemd/man/sd_booted.html
53
+ #
54
+ def booted?
55
+ IO.popen(["systemctl", "is-system-running"], &:read).chomp != "offline"
56
+ end
57
+
58
+ # Are we running within a systemd unit that expects us to notify?
59
+ def notify?
60
+ ENV.include?("NOTIFY_SOCKET")
61
+ end
62
+
63
+ # Open a persistent notify socket.
64
+ #
65
+ # Ruby doesn't have a nicer way to open a unix socket as a datagram.
66
+ #
67
+ private def notify_socket
68
+ @notify_socket ||= Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM, 0).tap do |socket|
69
+ socket.connect(Socket.pack_sockaddr_un(ENV["NOTIFY_SOCKET"]))
70
+ socket.close_on_exec = true
71
+ end
72
+ end
73
+
74
+ # Send a raw notify message.
75
+ #
76
+ # https://www.freedesktop.org/software/systemd/man/sd_notify.html
77
+ #
78
+ private def notify(message)
79
+ notify_socket.sendmsg(message, Socket::MSG_NOSIGNAL)
80
+ end
81
+
82
+ # Tell systemd we are now the main pid
83
+ def notify_pid
84
+ notify("MAINPID=#{$$}")
85
+ end
86
+
87
+ # Tell systemd we're fully started and ready to handle requests
88
+ def notify_ready
89
+ notify("READY=1")
90
+ end
91
+
92
+ # Tell systemd our status
93
+ def notify_status(status)
94
+ notify("STATUS=#{status}")
95
+ end
96
+
97
+ # Tell systemd we're restarting
98
+ def notify_reloading
99
+ notify("RELOADING=1")
100
+ end
101
+
102
+ # Tell systemd we're still alive
103
+ def notify_watchdog
104
+ notify("WATCHDOG=1")
105
+ end
106
+
107
+ # Has systemd asked us to watchdog?
108
+ #
109
+ # https://www.freedesktop.org/software/systemd/man/sd_watchdog_enabled.html
110
+ #
111
+ def watchdog?
112
+ ENV.include?("WATCHDOG_USEC") &&
113
+ (!ENV.include?("WATCHDOG_PID") || ENV["WATCHDOG_PID"].to_i == $$)
114
+ end
115
+
116
+ # How long between pings until the watchdog will think we're unhealthy?
117
+ def watchdog_usec
118
+ ENV["WATCHDOG_USEC"].to_i
119
+ end
120
+ end
121
+
122
+ # Take puma's stats and construct a sensible status line for Systemd
123
+ class Status
124
+ def initialize(stats)
125
+ @stats = stats
126
+ end
127
+
128
+ def clustered?
129
+ stats.has_key? "workers"
130
+ end
131
+
132
+ def workers
133
+ stats.fetch("workers", 1)
134
+ end
135
+
136
+ def booted_workers
137
+ stats.fetch("booted_workers", 1)
138
+ end
139
+
140
+ def running
141
+ if clustered?
142
+ stats["worker_status"].map { |s| s["last_status"].fetch("running", 0) }.inject(0, &:+)
143
+ else
144
+ stats.fetch("running", 0)
145
+ end
146
+ end
147
+
148
+ def backlog
149
+ if clustered?
150
+ stats["worker_status"].map { |s| s["last_status"].fetch("backlog", 0) }.inject(0, &:+)
151
+ else
152
+ stats.fetch("backlog", 0)
153
+ end
154
+ end
155
+
156
+ def to_s
157
+ if clustered?
158
+ "puma #{Puma::Const::VERSION} cluster: #{booted_workers}/#{workers} workers: #{running} threads, #{backlog} backlog"
159
+ else
160
+ "puma #{Puma::Const::VERSION}: #{running} threads, #{backlog} backlog"
161
+ end
162
+ end
163
+ end
164
+
165
+ # Puma creates the plugin when encountering `plugin` in the config.
166
+ def initialize(loader)
167
+ # This is a Puma::PluginLoader
168
+ @loader = loader
169
+ end
170
+
171
+ # We can start doing something when we have a launcher:
172
+ def start(launcher)
173
+ @launcher = launcher
174
+
175
+ # Log relevant ENV in debug
176
+ @launcher.events.debug "systemd: NOTIFY_SOCKET=#{ENV["NOTIFY_SOCKET"].inspect}"
177
+ @launcher.events.debug "systemd: WATCHDOG_PID=#{ENV["WATCHDOG_PID"].inspect}"
178
+ @launcher.events.debug "systemd: WATCHDOG_USEC=#{ENV["WATCHDOG_USEC"].inspect}"
179
+
180
+ # Only install hooks if systemd is present, the systemd is booted by
181
+ # systemd, and systemd has asked us to notify it of events.
182
+ @systemd = Systemd.new
183
+ if @systemd.present? && @systemd.booted? && @systemd.notify?
184
+ @launcher.events.debug "systemd: detected running inside systemd, registering hooks"
185
+ register_hooks
186
+ else
187
+ @launcher.events.debug "systemd: not running within systemd, doing nothing"
188
+ end
189
+ end
190
+
191
+ private
192
+
193
+ # Are we a single process worker, or do we have worker processes?
194
+ #
195
+ # Copied from puma, it's private:
196
+ # https://github.com/puma/puma/blob/v3.6.0/lib/puma/launcher.rb#L267-L269
197
+ #
198
+ def clustered?
199
+ (@launcher.options[:workers] || 0) > 0
200
+ end
201
+
202
+ def register_hooks
203
+ (@launcher.config.options[:on_restart] ||= []) << method(:restart)
204
+ @launcher.events.on_booted(&method(:booted))
205
+ in_background(&method(:status_loop))
206
+ in_background(&method(:watchdog_loop)) if @systemd.watchdog?
207
+ end
208
+
209
+ def booted
210
+ @launcher.events.log "* systemd: notify ready"
211
+ begin
212
+ @systemd.notify_ready
213
+ rescue
214
+ @launcher.events.error "! systemd: notify ready failed:\n #{$!.to_s}\n #{$!.backtrace.join("\n ")}"
215
+ end
216
+ end
217
+
218
+ def restart(launcher)
219
+ @launcher.events.log "* systemd: notify reloading"
220
+ begin
221
+ @systemd.notify_reloading
222
+ rescue
223
+ @launcher.events.error "! systemd: notify reloading failed:\n #{$!.to_s}\n #{$!.backtrace.join("\n ")}"
224
+ end
225
+ end
226
+
227
+ def fetch_stats
228
+ JSON.parse(@launcher.stats)
229
+ end
230
+
231
+ def status
232
+ Status.new(fetch_stats)
233
+ end
234
+
235
+ # Update systemd status event second or so
236
+ def status_loop
237
+ loop do
238
+ @launcher.events.debug "systemd: notify status"
239
+ begin
240
+ @systemd.notify_status(status.to_s)
241
+ rescue
242
+ @launcher.events.error "! systemd: notify status failed:\n #{$!.to_s}\n #{$!.backtrace.join("\n ")}"
243
+ ensure
244
+ sleep 1
245
+ end
246
+ end
247
+ end
248
+
249
+ # If watchdog is configured we'll send a ping at about half the timeout
250
+ # configured in systemd as recommended in the docs.
251
+ def watchdog_loop
252
+ @launcher.events.log "* systemd: watchdog detected (#{@systemd.watchdog_usec}usec)"
253
+
254
+ # Ruby wants seconds, and the docs suggest notifying halfway through the
255
+ # timeout.
256
+ sleep_seconds = @systemd.watchdog_usec / 1000.0 / 1000.0 / 2.0
257
+
258
+ loop do
259
+ begin
260
+ @launcher.events.debug "systemd: notify watchdog"
261
+ @systemd.notify_watchdog
262
+ rescue
263
+ @launcher.events.error "! systemd: notify watchdog failed:\n #{$!.to_s}\n #{$!.backtrace.join("\n ")}"
264
+ ensure
265
+ @launcher.events.debug "systemd: sleeping #{sleep_seconds}s"
266
+ sleep sleep_seconds
267
+ end
268
+ end
269
+ end
270
+ end
data.tar.gz.sig ADDED
Binary file
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: puma-plugin-systemd
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Samuel Cochran
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIDKDCCAhCgAwIBAgIBBTANBgkqhkiG9w0BAQUFADA6MQ0wCwYDVQQDDARzajI2
14
+ MRQwEgYKCZImiZPyLGQBGRYEc2oyNjETMBEGCgmSJomT8ixkARkWA2NvbTAeFw0x
15
+ NzA3MzEwNTQ3MDVaFw0xODA3MzEwNTQ3MDVaMDoxDTALBgNVBAMMBHNqMjYxFDAS
16
+ BgoJkiaJk/IsZAEZFgRzajI2MRMwEQYKCZImiZPyLGQBGRYDY29tMIIBIjANBgkq
17
+ hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr60Eo/ttCk8GMTMFiPr3GoYMIMFvLak
18
+ xSmTk9YGCB6UiEePB4THSSA5w6IPyeaCF/nWkDp3/BAam0eZMWG1IzYQB23TqIM0
19
+ 1xzcNRvFsn0aQoQ00k+sj+G83j3T5OOV5OZIlu8xAChMkQmiPd1NXc6uFv+Iacz7
20
+ kj+CMsI9YUFdNoU09QY0b+u+Rb6wDYdpyvN60YC30h0h1MeYbvYZJx/iZK4XY5zu
21
+ 4O/FL2ChjL2CPCpLZW55ShYyrzphWJwLOJe+FJ/ZBl6YXwrzQM9HKnt4titSNvyU
22
+ KzE3L63A3PZvExzLrN9u09kuWLLJfXB2sGOlw3n9t72rJiuBr3/OQQIDAQABozkw
23
+ NzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQU99dfRjEKFyczTeIz
24
+ m3ZsDWrNC80wDQYJKoZIhvcNAQEFBQADggEBADGiXpvK754s0zTFx3y31ZRDdvAI
25
+ lA209JIjUlDyr9ptCRadihyfF2l9/hb+hLemiPEYppzG6vEK1TIyzbAR36yOJ8CX
26
+ 4vPkCXLuwHhs6UIRbwN+IEy41nsIlBxmjLYei8h3t/G2Vm2oOaLdbjDXS+Srl9U8
27
+ shsE8ft81PxSQfzEL7Mr9cC9XvWbHW+SyTpfGm8rAtaqZkNeke4U8a0di4oz2EfA
28
+ P4lSfmXxsd1C71ckIp0cyXkPhyTtpyS/5hq9HhuUNzEHkSDe36/Rd1xYKV5JxMC2
29
+ YAttWFUs06lor2q1wwncPaMtUtbWwW35+1IV6xhs2rFY6DD/I6ZkK3GnHdY=
30
+ -----END CERTIFICATE-----
31
+ date: 2018-05-04 00:00:00.000000000 Z
32
+ dependencies:
33
+ - !ruby/object:Gem::Dependency
34
+ name: puma
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 3.6.0
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: 3.6.0
47
+ - !ruby/object:Gem::Dependency
48
+ name: json
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: bundler
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.13'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '1.13'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rake
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '10.0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '10.0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: minitest
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '5.0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '5.0'
103
+ description:
104
+ email: sj26@sj26.com
105
+ executables: []
106
+ extensions: []
107
+ extra_rdoc_files: []
108
+ files:
109
+ - LICENSE
110
+ - README.md
111
+ - lib/puma/plugin/systemd.rb
112
+ - lib/puma/plugin/systemd/version.rb
113
+ homepage: https://github.com/sj26/puma-plugin-systemd
114
+ licenses:
115
+ - MIT
116
+ metadata: {}
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 2.7.5
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: 'Puma integration with systemd: notify, status, watchdog'
137
+ test_files: []
metadata.gz.sig ADDED
@@ -0,0 +1 @@
1
+ tз��&��Sf��ԈcJ��쌮���Zg+2~���N>MVpnRqJc$��] ML������q�l���b��ݨ^��C�e��:f͗��+���KW~�Q�����s�Ċm_d)D�Ku��Ȼ�!�$�*���9�b/��4E\��]�Jp �o��$��T�� z