unicorn-wrangler 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -0
- data/lib/unicorn/wrangler.rb +128 -58
- data/lib/unicorn/wrangler/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 467ba192f3c2102dbc67f8fa957081576ca2e1a0
|
4
|
+
data.tar.gz: 0e3fc78a240edc9c0c0982d60dc2579ecffff3a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5e7eebda41d84eb9a914a93ad9d8914591963877d8cc5d51c9d42b0e838f026e763317b8db0deccc9b316a5c5cbb3176ea9a2c72137ea5a9af02e7ee808d7c24
|
7
|
+
data.tar.gz: 428f1680fea401c017cc88f6371457bcdf24cbd8dc2a14ad6035faa5adfd561cdfe7dc1317af53ea178f47864854325f7213421b97fceeebb1a5eb93a2c2e304
|
data/README.md
CHANGED
@@ -5,3 +5,13 @@ A simple process to launch and relaunch unicorn. When launched, it either start
|
|
5
5
|
Usage:
|
6
6
|
|
7
7
|
unicorn-wrangler --pidfile /path/to/unicorn.pid --startup-time 60 -- /path/to/unicorn <unicorn-options>
|
8
|
+
|
9
|
+
When the wrangler starts, it does the following:
|
10
|
+
|
11
|
+
If unicorn is not running (determined by looking for the unicorn pidfile, and if it exists, checking the named process is running), it starts unicorn with the given command.
|
12
|
+
|
13
|
+
If unicorn is running, it reloads the running instance, then terminates the old instance after the grace period has passed.
|
14
|
+
|
15
|
+
The wrangler will continue to run while the monitored unicorn process runs. It will stop running shortly after unicorn stops.
|
16
|
+
|
17
|
+
If the wrangler is manually stopped (by sending INT or TERM signals to its process), it forks a new 'assassin' process that will terminate unicorn after the grace period has passed. This is so that quickly stopping and starting the wrangler will have the effect of reloading unicorn, not terminating and restarting it.
|
data/lib/unicorn/wrangler.rb
CHANGED
@@ -3,86 +3,159 @@ require "unicorn/wrangler/version"
|
|
3
3
|
module Unicorn
|
4
4
|
class Wrangler
|
5
5
|
attr_reader :command, :pidfile, :grace_period
|
6
|
-
attr_accessor :
|
6
|
+
attr_accessor :unicorn
|
7
7
|
|
8
8
|
def initialize(command, options = {})
|
9
9
|
@command = command
|
10
10
|
@pidfile = File.expand_path(options[:pidfile] || 'unicorn.pid')
|
11
11
|
@grace_period = options[:grace_period] || 60
|
12
12
|
@verbose = options[:verbose]
|
13
|
+
@unicorn = UnicornProcess.from_pidfile(@pidfile)
|
13
14
|
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
class UnicornProcess
|
17
|
+
attr_reader :pid, :pidfile
|
18
|
+
|
19
|
+
def initialize(pidfile)
|
20
|
+
@pidfile = pidfile
|
21
|
+
@pid = File.read(pidfile).to_i
|
22
|
+
recall_assassin
|
20
23
|
end
|
21
24
|
|
22
|
-
|
23
|
-
|
25
|
+
def running?
|
26
|
+
Process.getpgid(pid)
|
27
|
+
rescue Errno::ESRCH
|
28
|
+
false
|
24
29
|
end
|
25
30
|
|
26
|
-
|
31
|
+
def signal(msg)
|
32
|
+
puts "Sending signal #{msg} to #{pid}"
|
33
|
+
Process.kill msg, pid
|
34
|
+
end
|
27
35
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
+
def reload(grace_period)
|
37
|
+
signal "USR2"
|
38
|
+
sleep grace_period
|
39
|
+
reloaded_unicorn = UnicornProcess.from_pidfile(pidfile)
|
40
|
+
if reloaded_unicorn && pid != reloaded_unicorn.pid
|
41
|
+
terminate
|
42
|
+
reloaded_unicorn
|
43
|
+
else
|
44
|
+
raise "unicorn didn't reload correctly within grace period (was pid #{pid})"
|
45
|
+
end
|
36
46
|
end
|
37
47
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
48
|
+
def launch_assassin(grace_period)
|
49
|
+
if running? && !@assassin_launched
|
50
|
+
@assassin_launched = true
|
51
|
+
puts "preparing to kill unicorn #{pid} in #{grace_period} seconds"
|
52
|
+
unless fork
|
53
|
+
File.write(assassin_pidfile, Process.pid.to_s)
|
54
|
+
|
55
|
+
trap 'TERM' do
|
56
|
+
exit
|
57
|
+
end
|
58
|
+
|
59
|
+
Process.setsid
|
60
|
+
sleep grace_period
|
61
|
+
terminate
|
62
|
+
File.delete(assassin_pidfile)
|
63
|
+
end
|
42
64
|
end
|
43
|
-
sleep 1
|
44
65
|
end
|
45
|
-
end
|
46
66
|
|
47
|
-
|
67
|
+
def recall_assassin
|
68
|
+
if File.exist?(assassin_pidfile)
|
69
|
+
assassin_pid = File.read(assassin_pidfile).to_i
|
70
|
+
Process.kill 'TERM', assassin_pid
|
71
|
+
end
|
72
|
+
rescue Errno::ESRCH
|
73
|
+
end
|
48
74
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
75
|
+
def assassin_pidfile
|
76
|
+
pidfile + ".assassin"
|
77
|
+
end
|
78
|
+
|
79
|
+
def terminate
|
80
|
+
if running?
|
81
|
+
signal 'TERM'
|
82
|
+
else
|
83
|
+
"Attempt to terminate #{pid} failed as process not running"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def verbose(message)
|
88
|
+
self.class.verbose(message)
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.verbose(message)
|
92
|
+
Unicorn::Wrangler.verbose(message)
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.wait_for(&block)
|
96
|
+
until block.call
|
97
|
+
sleep 0.1
|
54
98
|
end
|
55
99
|
end
|
100
|
+
|
101
|
+
def self.start(pidfile, command)
|
102
|
+
Process.spawn(command, pgroup: true)
|
103
|
+
wait_for { File.exist?(pidfile) }
|
104
|
+
new(pidfile)
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.from_pidfile(pidfile)
|
108
|
+
File.exist?(pidfile) && new(pidfile)
|
109
|
+
end
|
56
110
|
end
|
57
111
|
|
58
|
-
def
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
112
|
+
def start
|
113
|
+
setup_signal_handlers
|
114
|
+
|
115
|
+
if unicorn && unicorn.running?
|
116
|
+
self.unicorn = reload_unicorn
|
117
|
+
else
|
118
|
+
self.unicorn = UnicornProcess.start(pidfile, command)
|
119
|
+
sleep grace_period
|
120
|
+
end
|
121
|
+
|
122
|
+
if unicorn.running?
|
123
|
+
verbose "Unicorn running on #{unicorn.pid}"
|
124
|
+
loop_while_unicorn_runs
|
125
|
+
else
|
126
|
+
verbose "Unable to start unicorn. Exiting."
|
127
|
+
end
|
66
128
|
end
|
67
129
|
|
68
|
-
def
|
69
|
-
|
130
|
+
def setup_signal_handlers
|
131
|
+
trap_signals(:HUP) { reload_unicorn }
|
132
|
+
trap_signals(:QUIT, :INT, :TERM) do |signal|
|
133
|
+
kill_unicorn_after_delay
|
134
|
+
exit
|
135
|
+
end
|
70
136
|
end
|
71
137
|
|
72
|
-
def
|
73
|
-
|
138
|
+
def reload_unicorn
|
139
|
+
self.unicorn = unicorn.reload(grace_period) if unicorn
|
74
140
|
end
|
75
141
|
|
76
|
-
def
|
77
|
-
|
78
|
-
|
79
|
-
|
142
|
+
def loop_while_unicorn_runs
|
143
|
+
loop do
|
144
|
+
if unicorn.running?
|
145
|
+
sleep 0.1
|
146
|
+
else
|
147
|
+
exit
|
148
|
+
end
|
149
|
+
end
|
80
150
|
end
|
81
151
|
|
82
|
-
def
|
83
|
-
|
84
|
-
|
85
|
-
|
152
|
+
def trap_signals(*signals, &block)
|
153
|
+
signals.map(&:to_s).each do |signal|
|
154
|
+
trap(signal) do
|
155
|
+
verbose "received #{signal} (managing #{unicorn.pid})"
|
156
|
+
block.call signal
|
157
|
+
end
|
158
|
+
end
|
86
159
|
end
|
87
160
|
|
88
161
|
def wait_for(&block)
|
@@ -92,17 +165,14 @@ module Unicorn
|
|
92
165
|
end
|
93
166
|
|
94
167
|
def kill_unicorn_after_delay
|
95
|
-
if
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
signal :TERM, unicorn_pid if process_running?(unicorn_pid)
|
101
|
-
end
|
102
|
-
end
|
168
|
+
unicorn.launch_assassin(grace_period) if unicorn
|
169
|
+
end
|
170
|
+
|
171
|
+
def verbose(message)
|
172
|
+
self.class.verbose(message)
|
103
173
|
end
|
104
174
|
|
105
|
-
def
|
175
|
+
def self.verbose(message)
|
106
176
|
puts message if @verbose
|
107
177
|
end
|
108
178
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unicorn-wrangler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Ward
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-06-
|
11
|
+
date: 2013-06-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|