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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d6c6c6c34675d90e10f42b3edd377a4c7201df27
4
- data.tar.gz: 043e1bbcdaf37e1eb17772969a2d8a5c394d20c9
3
+ metadata.gz: 467ba192f3c2102dbc67f8fa957081576ca2e1a0
4
+ data.tar.gz: 0e3fc78a240edc9c0c0982d60dc2579ecffff3a6
5
5
  SHA512:
6
- metadata.gz: 717e78358e2ba9d8d6a75f1848423a6ee7265712f900a1c7a05edc3baf88de9c24835200378f672cfab8ec7ddccbcb4eaeee430f39de852b726805e17fcb60fd
7
- data.tar.gz: 8c8358d531f80106ddba1461b044c8f91ffade248f79bfc459989a21803583593cbe6a85f259163ab9668278149f19e9cb0e57707f1d8165def51bbeafc13c15
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.
@@ -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 :unicorn_pid
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
- def start
16
- trap_signals(:HUP) { restart_unicorn }
17
- trap_signals(:QUIT, :INT, :TERM) do |signal|
18
- kill_unicorn_after_delay
19
- exit
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
- if old_unicorn = old_pidfile_contents && process_running?(old_unicorn)
23
- signal :QUIT, old_unicorn
25
+ def running?
26
+ Process.getpgid(pid)
27
+ rescue Errno::ESRCH
28
+ false
24
29
  end
25
30
 
26
- self.unicorn_pid = pidfile_contents
31
+ def signal(msg)
32
+ puts "Sending signal #{msg} to #{pid}"
33
+ Process.kill msg, pid
34
+ end
27
35
 
28
- if unicorn_pid && process_running?(unicorn_pid)
29
- debug "running unicorn found at #{unicorn_pid}"
30
- restart_unicorn
31
- else
32
- debug "no running unicorn found, starting new instance"
33
- self.unicorn_pid = Process.spawn(command, pgroup: true)
34
- wait_for { process_running?(unicorn_pid) }
35
- debug "new instance started with PID #{unicorn_pid}"
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
- loop do
39
- unless process_running?(unicorn_pid)
40
- debug "unicorn(#{unicorn_pid}) no longer running, quitting."
41
- exit
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
- private
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
- def trap_signals(*signals, &block)
50
- signals.map(&:to_s).each do |signal|
51
- trap(signal) do
52
- debug "received #{signal} (managing #{unicorn_pid})"
53
- block.call signal
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 restart_unicorn
59
- debug "restarting unicorn process at #{unicorn_pid}"
60
- signal "USR2", unicorn_pid
61
- wait_for { pidfile_contents && unicorn_pid != pidfile_contents }
62
- debug "new master process started at #{pidfile_contents}"
63
- sleep grace_period
64
- signal "TERM", unicorn_pid
65
- self.unicorn_pid = pidfile_contents
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 pidfile_contents
69
- File.exist?(pidfile) && File.read(pidfile).to_i
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 old_pidfile_contents
73
- File.exist?(pidfile + ".oldbin") && File.read(pidfile + ".oldbin").to_i
138
+ def reload_unicorn
139
+ self.unicorn = unicorn.reload(grace_period) if unicorn
74
140
  end
75
141
 
76
- def signal(name, pid)
77
- debug "signalling #{pid} with #{name}"
78
- Process.kill name, pid
79
- rescue Errno::ESRCH
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 process_running?(pid)
83
- pid && Process.getpgid(pid)
84
- rescue Errno::ESRCH
85
- false
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 process_running?(unicorn_pid)
96
- unless fork
97
- debug "preparing to kill unicorn #{unicorn_pid} in #{grace_period} seconds"
98
- Process.setsid
99
- sleep grace_period
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 debug(message)
175
+ def self.verbose(message)
106
176
  puts message if @verbose
107
177
  end
108
178
  end
@@ -1,5 +1,5 @@
1
1
  module Unicorn
2
2
  class Wrangler
3
- VERSION = "0.0.6"
3
+ VERSION = "0.0.7"
4
4
  end
5
5
  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.6
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-14 00:00:00.000000000 Z
11
+ date: 2013-06-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler