immortalize 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/bin/immortalize +103 -18
- data/immortalize.gemspec +2 -2
- metadata +3 -3
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.2
|
data/bin/immortalize
CHANGED
@@ -20,7 +20,7 @@ require 'merb-core'
|
|
20
20
|
require 'merb-mailer'
|
21
21
|
Merb::Mailer.delivery_method = :sendmail
|
22
22
|
|
23
|
-
$log_location = "
|
23
|
+
$log_location = "#{ENV['HOME']}/.immortalize"
|
24
24
|
`mkdir -p "#{$log_location}"` unless File.directory?($log_location)
|
25
25
|
$registry_filename = "#{$log_location}/registry.yaml"
|
26
26
|
File.open($registry_filename, 'w'){|f| f << {}.to_yaml} unless File.exists?($registry_filename)
|
@@ -40,17 +40,22 @@ To remove a command:
|
|
40
40
|
To inspect the current list of immortal commands:
|
41
41
|
#{$0} list
|
42
42
|
|
43
|
+
To install the immortalize cron job (which does the actual work):
|
44
|
+
immortalize setup
|
45
|
+
|
43
46
|
Run this command with no arguments as a cron job, to run every minute:
|
44
47
|
* * * * * immortalize
|
48
|
+
|
49
|
+
Options:
|
45
50
|
ENDBANNER
|
46
51
|
|
47
|
-
|
48
|
-
opts.on( '--
|
52
|
+
$options[:notification_recipient] = nil
|
53
|
+
opts.on( '--notify=EMAIL', "The email address to which failure notifications should be sent." ) do |email|
|
49
54
|
$options[:notification_recipient] = email
|
50
55
|
end
|
51
56
|
|
52
57
|
$options[:max_failures] = 5
|
53
|
-
opts.on('--max_failures=
|
58
|
+
opts.on('--max_failures=NUM', "Notify on NUM or more failures within an hour (default 5)") do |num|
|
54
59
|
$options[:max_failures] = num.to_i
|
55
60
|
end
|
56
61
|
|
@@ -80,6 +85,16 @@ class Time
|
|
80
85
|
end
|
81
86
|
|
82
87
|
class Immortal
|
88
|
+
def self.new(identifier)
|
89
|
+
if $registry[identifier]
|
90
|
+
obj = allocate
|
91
|
+
obj.send :initialize, identifier
|
92
|
+
obj
|
93
|
+
else
|
94
|
+
puts "tried:\n\t#{identifier}commands:\n\t#{$registry.values.map {|r| r[:command]}.join("\n\t")}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
83
98
|
attr_reader :identifier
|
84
99
|
def initialize(identifier)
|
85
100
|
@identifier = identifier
|
@@ -101,13 +116,23 @@ class Immortal
|
|
101
116
|
def start!
|
102
117
|
# Run the command and gather the pid
|
103
118
|
pid = nil
|
104
|
-
open("
|
119
|
+
open("|echo \"#{@reg[:command]}\" | sh & echo $!") do |f|
|
105
120
|
pid = f.sysread(5).chomp.to_i
|
106
121
|
end
|
107
122
|
# Log the pid
|
108
123
|
puts "pid #{pid}"
|
109
124
|
$registry[identifier][:pid] = pid
|
110
125
|
end
|
126
|
+
def stop!
|
127
|
+
if running?
|
128
|
+
pid = $registry[identifier].delete(:pid)
|
129
|
+
`kill -INT #{pid}`
|
130
|
+
puts "attempted to kill pid #{pid}: \"#{@reg[:command]}\""
|
131
|
+
else
|
132
|
+
warn "\"#{@reg[:command]}\" not running!"
|
133
|
+
false
|
134
|
+
end
|
135
|
+
end
|
111
136
|
|
112
137
|
def failed!
|
113
138
|
failures << Time.now
|
@@ -137,25 +162,78 @@ class Immortal
|
|
137
162
|
end
|
138
163
|
end
|
139
164
|
|
165
|
+
# Curate the command string
|
166
|
+
if ARGV[1].to_s.length > 1 && ARGV[1] !~ /^\d+$/
|
167
|
+
puts ARGV.inspect
|
168
|
+
$command_string = ARGV[1]
|
169
|
+
# Complain about the string if it does not have proper output redirections
|
170
|
+
cmds = $command_string.split(/; ?/)
|
171
|
+
last_cmd = cmds.pop
|
172
|
+
lst,out_s = last_cmd.split(/(?: \d)? ?>/,2)
|
173
|
+
cmds << lst
|
174
|
+
outs = last_cmd.scan(/(?: \d)? ?> ?\S+/)
|
175
|
+
outs = outs.map {|o| o.sub(/ ?> ?/,">").sub(/^>/,"1>") }
|
176
|
+
unless outs.any? {|o| o =~ /^2>/}
|
177
|
+
warn "Appending default STDERR redirection: 2>&1 (STDERR > STDOUT)"
|
178
|
+
outs << "2>&1"
|
179
|
+
end
|
180
|
+
unless outs.any? {|o| o =~ /^1?>/}
|
181
|
+
warn "#{$command_string}\nInvalid command: You need to add proper STDOUT redirection, ex: > /dev/null or 1>log/run.log or 1 > log/run.log"
|
182
|
+
exit
|
183
|
+
end
|
184
|
+
$command_string = cmds.join('; ') + ' ' + outs.join(' ')
|
185
|
+
end
|
186
|
+
|
187
|
+
|
140
188
|
# Main logic
|
141
189
|
unless ::Object.const_defined?(:IRB)
|
142
190
|
case $action
|
191
|
+
when 'setup'
|
192
|
+
crons = `crontab -l 2>/dev/null`.split(/\n/)
|
193
|
+
immortalize_cmd = `which immortalize`.chomp
|
194
|
+
if immortalize_cmd.empty?
|
195
|
+
warn "Couldn't find installed version of the 'immortalize' command! (Try `which immortalize`)"
|
196
|
+
exit
|
197
|
+
end
|
198
|
+
crons.reject! {|c| c =~ /immortalize > #{$log_location}\/cron.log$/}
|
199
|
+
crons << "* * * * * #{immortalize_cmd} > #{$log_location}/cron.log\n"
|
200
|
+
puts "Installing crons:\n\t#{crons.join("\n\t")}"
|
201
|
+
f = IO.popen("crontab -", 'w')
|
202
|
+
f << crons.join("\n")
|
203
|
+
f.close
|
204
|
+
crons = `crontab -l 2>/dev/null`.split(/\n/)
|
205
|
+
puts "Installed crons:\n\t#{crons.join("\n\t")}"
|
143
206
|
when 'list'
|
144
|
-
puts $registry.inspect
|
207
|
+
# puts $registry.inspect
|
145
208
|
puts "Immortalized:"
|
146
|
-
$registry.
|
147
|
-
|
209
|
+
keys = $registry.keys.sort
|
210
|
+
keys.each_with_index do |identifier,i|
|
211
|
+
puts "\t#{i+1}) " + Immortal.new(identifier).inspect
|
148
212
|
end
|
213
|
+
puts "\nTo remove jobs, for example job #1, from this list, run `immortalize remove 1`"
|
149
214
|
exit
|
150
215
|
|
216
|
+
when 'stop'
|
217
|
+
if ARGV[1] =~ /^\d+$/
|
218
|
+
identifier = $registry.keys.sort[ARGV[1].to_i-1]
|
219
|
+
else
|
220
|
+
identifier = SHA1.hexdigest($command_string)
|
221
|
+
end
|
222
|
+
immortal = Immortal.new(identifier)
|
223
|
+
immortal.stop!
|
224
|
+
|
151
225
|
when 'run'
|
226
|
+
if $options[:notification_recipient].nil?
|
227
|
+
warn "Must include --notify EMAIL_ADDRESS when adding a command!"
|
228
|
+
exit
|
229
|
+
end
|
230
|
+
|
152
231
|
# Running with a given command.
|
153
|
-
|
154
|
-
identifier = SHA1.hexdigest(command_string)
|
232
|
+
identifier = SHA1.hexdigest($command_string)
|
155
233
|
|
156
234
|
# Create the command
|
157
235
|
$registry[identifier] ||= {
|
158
|
-
:command => command_string
|
236
|
+
:command => $command_string
|
159
237
|
}
|
160
238
|
$registry[identifier].merge!($options)
|
161
239
|
|
@@ -169,32 +247,39 @@ unless ::Object.const_defined?(:IRB)
|
|
169
247
|
end
|
170
248
|
|
171
249
|
when 'remove'
|
172
|
-
|
173
|
-
|
174
|
-
|
250
|
+
if ARGV[1] =~ /^\d+$/
|
251
|
+
identifier = $registry.keys.sort[ARGV[1].to_i-1]
|
252
|
+
else
|
253
|
+
identifier = SHA1.hexdigest($command_string)
|
254
|
+
end
|
255
|
+
reg = $registry.delete(identifier)
|
256
|
+
puts "Deleted #{identifier}: \"#{reg[:command]}\""
|
175
257
|
|
176
258
|
when nil
|
177
259
|
# Running bare from cron.
|
178
260
|
# Check all logged commands with pids.
|
179
|
-
$registry.
|
261
|
+
puts "[#{Time.now.strftime("%Y-%m-%d %H:%M:%S")}] #{$registry.length} jobs"
|
262
|
+
$registry.keys.sort.each_with_index do |identifier,i|
|
180
263
|
immortal = Immortal.new(identifier)
|
181
264
|
|
182
265
|
# Check if running
|
183
266
|
if immortal.running?
|
184
|
-
puts "`#{immortal[:command]}' is running fine..."
|
267
|
+
# puts " #{i+1}) `#{immortal[:command]}' is running fine..."
|
185
268
|
else
|
186
|
-
puts "`#{immortal[:command]}' HAS DIED! Reviving..."
|
269
|
+
puts " #{i+1}) `#{immortal[:command]}' HAS DIED! Reviving..."
|
187
270
|
# Mark the failure
|
188
271
|
immortal.failed!
|
189
272
|
# Notify if failures have been frequent
|
190
273
|
if immortal.frequent_failures?
|
191
|
-
puts "
|
274
|
+
puts " #{i+1}) FREQUENT FAILURE ON #{identifier} (`#{immortal[:command]}')"
|
192
275
|
notify(immortal, "ImmortalCommand failure!\n\nCommand `#{immortal[:command]}' failed, threshold is #{immortal[:max_failures]} / hour.\n\n#{immortal.failures_today.size} failures so far today, #{immortal.failures_this_hour.size} in the past hour.")
|
193
276
|
end
|
194
277
|
# Start it
|
195
278
|
immortal.start!
|
196
279
|
end
|
197
280
|
end
|
281
|
+
else
|
282
|
+
puts optparse
|
198
283
|
end
|
199
284
|
end
|
200
285
|
|
data/immortalize.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{immortalize}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["BehindLogic"]
|
12
|
-
s.date = %q{2010-03-
|
12
|
+
s.date = %q{2010-03-17}
|
13
13
|
s.default_executable = %q{immortalize}
|
14
14
|
s.description = %q{Watch a specific process, restart it if it dies.}
|
15
15
|
s.email = %q{gems@behindlogic.com}
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 2
|
9
|
+
version: 0.1.2
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- BehindLogic
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-03-
|
17
|
+
date: 2010-03-17 00:00:00 -04:00
|
18
18
|
default_executable: immortalize
|
19
19
|
dependencies: []
|
20
20
|
|