tellmewhen 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.textile +95 -6
- data/bin/tellmewhen +56 -22
- data/pkg/tellmewhen-1.0.1.gem +0 -0
- data/tellmewhen.gemspec +1 -1
- metadata +5 -4
data/README.textile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
h1. tellmewhen
|
2
2
|
|
3
|
-
Tell me when another program has finshed.
|
3
|
+
Tell me when another program has finshed. Tell me when it started, tell when it finished. Tell me how long it took. Tell me the output and if it failed.
|
4
4
|
|
5
5
|
I created this utility because I had long running database scripts that I wanted a completion notification for - including a summary of the time and exit code. I had done this various ways in the past by using bash and other shell utilities. This program captures all of the features and behavior I wanted without having to script it from scratch again every time I needed it.
|
6
6
|
|
@@ -9,18 +9,31 @@ I created this utility because I had long running database scripts that I wanted
|
|
9
9
|
* sends email
|
10
10
|
* includes start/stop/elapsed timing
|
11
11
|
* success/fail based on exit code
|
12
|
-
* watch an already running process (by pid
|
13
|
-
*
|
12
|
+
* watch an already running process (by pid)
|
13
|
+
* released as a gem so it's easy to use/install
|
14
|
+
|
15
|
+
h1. Installation
|
16
|
+
|
17
|
+
gem install tellmewhen
|
14
18
|
|
15
19
|
h1. Configuration
|
16
20
|
|
17
21
|
Configuration Defaults:
|
18
22
|
|
23
|
+
<pre>
|
24
|
+
---
|
25
|
+
notify-via: email
|
26
|
+
email:
|
27
|
+
from: `echo $LOGNAME`@`hostname
|
28
|
+
to: `echo $LOGNAME`@`hostname
|
29
|
+
</pre>
|
30
|
+
|
19
31
|
Configuration is then merged from @$HOME/.tellmewhenrc@ if the file exists in @$HOME@. Configuration is also merged from @./.tellmewhenrc@ in the current directory, if the file exists. This allows you some flexibility in overriding settings.
|
20
32
|
|
21
33
|
h1. Usage
|
22
34
|
|
23
|
-
pre
|
35
|
+
<pre>
|
36
|
+
Usage: ./tellmewhen command args...
|
24
37
|
-v, --[no-]verbose Run Verbosely.
|
25
38
|
-c, --config=file Use alternate configuration file.
|
26
39
|
-p, --pid=pid Wait for <pid> to terminate.
|
@@ -28,14 +41,90 @@ pre. Usage: ./tellmewhen command args...
|
|
28
41
|
-m, --modified=file Wait for <file> to be modified.
|
29
42
|
-t, --timeout=seconds Wait for up to <seconds> seconds for the command before sendin a 'pending' notification.
|
30
43
|
-w, --write-config=file Write [fully merged] configuration to <file> (NB: will not be clobber).
|
31
|
-
|
44
|
+
</pre>
|
32
45
|
|
33
46
|
h1. Examples
|
34
47
|
|
35
|
-
pre
|
48
|
+
<pre>
|
49
|
+
./tellmewhen 'sleep 3; ls'
|
36
50
|
./tellmewhen -p 12345
|
37
51
|
./tellmewhen -e some-file.txt # await existance
|
38
52
|
./tellmewhen -m some-file.txt # await update
|
53
|
+
</pre>
|
54
|
+
|
55
|
+
You can also have 'pending' notifications so that you know things are still going:
|
56
|
+
|
57
|
+
<pre>
|
58
|
+
./tellmewhen -t 2 'sleep 5; ls'
|
59
|
+
</pre>
|
60
|
+
|
61
|
+
Produces 2 emails. The pending mail is:
|
62
|
+
|
63
|
+
<pre>
|
64
|
+
Subject: When! [NOT] I'm _still_ waiting for sleep...
|
65
|
+
Body:
|
66
|
+
|
67
|
+
Just wanted to let you know that:
|
68
|
+
|
69
|
+
sleep 5; ls
|
70
|
+
|
71
|
+
Is _STILL_ running on your-host.com, it has not exited. You did want me to let you know didn't you?
|
72
|
+
|
73
|
+
I started the darn thing at Tue Jan 25 23:25:01 -0500 2011 (1296015901) and it has taken a total of 3 seconds so far.
|
74
|
+
|
75
|
+
Just thought you'd like to know. I'll continue to keep watching what you asked me to. (boor-ring!)
|
76
|
+
|
77
|
+
Cordially,
|
78
|
+
|
79
|
+
Tellmewhen
|
80
|
+
|
81
|
+
P.S. stderr says:
|
82
|
+
--
|
83
|
+
|
84
|
+
--
|
85
|
+
|
86
|
+
P.S. stdout says:
|
87
|
+
--
|
88
|
+
|
89
|
+
--
|
90
|
+
</pre>
|
91
|
+
|
92
|
+
|
93
|
+
And a final mail when it completed:
|
94
|
+
|
95
|
+
<pre>
|
96
|
+
Subject: When! SUCCESS for sleep...
|
97
|
+
Body:
|
98
|
+
|
99
|
+
Just wanted to let you know that:
|
100
|
+
|
101
|
+
sleep 5; ls
|
102
|
+
|
103
|
+
completed on your-host.com, with an exit code of: 0
|
104
|
+
|
105
|
+
It started at Tue Jan 25 23:25:01 -0500 2011 (1296015901), finished at Tue Jan 25 23:25:06 -0500 2011 (1296015906) and took a total of 5 seconds.
|
106
|
+
|
107
|
+
May your day continue to be full of win.
|
108
|
+
|
109
|
+
Sincerely,
|
110
|
+
|
111
|
+
Tellmewhen
|
112
|
+
|
113
|
+
P.S. stderr says:
|
114
|
+
--
|
115
|
+
|
116
|
+
--
|
117
|
+
|
118
|
+
P.S. stdout says:
|
119
|
+
--
|
120
|
+
README.textile
|
121
|
+
Rakefile
|
122
|
+
bin
|
123
|
+
foo
|
124
|
+
tellmewhen.gemspec
|
125
|
+
|
126
|
+
--
|
127
|
+
</pre>
|
39
128
|
|
40
129
|
h1. Authors
|
41
130
|
|
data/bin/tellmewhen
CHANGED
@@ -15,6 +15,11 @@ class TellMeWhen
|
|
15
15
|
@stderr_file = Tempfile.new('tellmewhen.stdout').path
|
16
16
|
end
|
17
17
|
|
18
|
+
|
19
|
+
def max_bytes_to_read_from_file
|
20
|
+
8 * 1024
|
21
|
+
end
|
22
|
+
|
18
23
|
def hostname
|
19
24
|
`hostname`.chomp
|
20
25
|
end
|
@@ -102,8 +107,8 @@ class TellMeWhen
|
|
102
107
|
exit app.run args
|
103
108
|
end
|
104
109
|
|
105
|
-
def elapsed_time
|
106
|
-
Time.now.to_i -
|
110
|
+
def elapsed_time since
|
111
|
+
Time.now.to_i - since.to_i
|
107
112
|
end
|
108
113
|
|
109
114
|
def wait_timeout
|
@@ -112,10 +117,12 @@ class TellMeWhen
|
|
112
117
|
|
113
118
|
def wait_on_command args
|
114
119
|
puts "Do run: #{args}"
|
120
|
+
@current_command = args.to_s
|
115
121
|
if args.to_s.empty?
|
116
122
|
raise "Error: you must supply a command to execute"
|
117
123
|
end
|
118
124
|
child_pid = Kernel.fork
|
125
|
+
last_check_time = Time.now
|
119
126
|
if child_pid.nil?
|
120
127
|
# in child
|
121
128
|
STDOUT.reopen(File.open(@stdout_file, 'w+'))
|
@@ -132,7 +139,8 @@ class TellMeWhen
|
|
132
139
|
child_exited = true
|
133
140
|
end
|
134
141
|
sleep(0.250)
|
135
|
-
if elapsed_time > wait_timeout
|
142
|
+
if elapsed_time(last_check_time) > wait_timeout.to_i
|
143
|
+
last_check_time = Time.now
|
136
144
|
puts "Exceeded timeout #{wait_timeout}, sending 'pending' notificaiton"
|
137
145
|
send_pending_notification
|
138
146
|
end
|
@@ -148,7 +156,7 @@ Just wanted to let you know that:
|
|
148
156
|
|
149
157
|
completed on #{hostname}, with an exit code of: #{@exit_status}
|
150
158
|
|
151
|
-
It started at #{@start_time.to_s} (#{@start_time.to_i}), finished at #{@end_time.to_s} (#{@end_time.to_i}) and took a total of #{elapsed_time} seconds.
|
159
|
+
It started at #{@start_time.to_s} (#{@start_time.to_i}), finished at #{@end_time.to_s} (#{@end_time.to_i}) and took a total of #{elapsed_time(@start_time)} seconds.
|
152
160
|
|
153
161
|
May your day continue to be full of win.
|
154
162
|
|
@@ -168,7 +176,7 @@ Just wanted to let you know that:
|
|
168
176
|
|
169
177
|
FAILED! on #{hostname}, with an exit code of: #{@exit_status}
|
170
178
|
|
171
|
-
It started at #{@start_time.to_s} (#{@start_time.to_i}), finished at #{@end_time.to_s} (#{@end_time.to_i}) and took a total of #{elapsed_time} seconds to collapse in a steaming heap of failure.
|
179
|
+
It started at #{@start_time.to_s} (#{@start_time.to_i}), finished at #{@end_time.to_s} (#{@end_time.to_i}) and took a total of #{elapsed_time(@start_time)} seconds to collapse in a steaming heap of failure.
|
172
180
|
|
173
181
|
Have a nice day.
|
174
182
|
|
@@ -187,11 +195,11 @@ BODY
|
|
187
195
|
body = <<-BODY
|
188
196
|
Just wanted to let you know that:
|
189
197
|
|
190
|
-
#{
|
198
|
+
#{@current_command}
|
191
199
|
|
192
200
|
Is _STILL_ running on #{hostname}, it has not exited. You did want me to let you know didn't you?
|
193
201
|
|
194
|
-
I started the darn thing at #{@start_time.to_s} (#{@start_time.to_i}) and it has taken a total of #{elapsed_time} seconds so far.
|
202
|
+
I started the darn thing at #{@start_time.to_s} (#{@start_time.to_i}) and it has taken a total of #{elapsed_time(@start_time)} seconds so far.
|
195
203
|
|
196
204
|
Just thought you'd like to know. I'll continue to keep watching what you asked me to. (boor-ring!)
|
197
205
|
|
@@ -202,19 +210,19 @@ Tellmewhen
|
|
202
210
|
#{email_footer}
|
203
211
|
BODY
|
204
212
|
|
205
|
-
send_email_notification "When! [NOT] I'm _still_ waiting for #{
|
213
|
+
send_email_notification "When! [NOT] I'm _still_ waiting for #{@current_command.split.first}...", body
|
206
214
|
end
|
207
215
|
|
208
216
|
def email_footer
|
209
217
|
return <<-END
|
210
218
|
P.S. stderr says:
|
211
219
|
--
|
212
|
-
#{File.read(@stderr_file)}
|
220
|
+
#{File.read(@stderr_file,max_bytes_to_read_from_file)}
|
213
221
|
--
|
214
222
|
|
215
223
|
P.S. stdout says:
|
216
224
|
--
|
217
|
-
#{File.read(@stdout_file)}
|
225
|
+
#{File.read(@stdout_file,max_bytes_to_read_from_file)}
|
218
226
|
--
|
219
227
|
END
|
220
228
|
end
|
@@ -226,13 +234,16 @@ P.S. stdout says:
|
|
226
234
|
|
227
235
|
def wait_on_pid args
|
228
236
|
# wait until pid exits
|
237
|
+
@current_command = "wait on pid: #{@options[:pid]}"
|
238
|
+
last_check_time = Time.now
|
229
239
|
while pid_running? @options[:pid]
|
230
240
|
sleep 0.250
|
231
|
-
if elapsed_time > wait_timeout
|
241
|
+
if elapsed_time(last_check_time) > wait_timeout
|
242
|
+
last_check_time = Time.now
|
232
243
|
puts "Exceeded timeout #{wait_timeout}, sending 'pending' notificaiton"
|
233
244
|
send_email_notification "When! [NOT] still awaiting pid:#{pid} to exit", <<-END
|
234
245
|
|
235
|
-
I started watching #{pid} on #{hostname} at #{@start_time.to_s} (#{@start_time.to_i}), I've been watching 'em for #{elapsed_time} seconds so far.
|
246
|
+
I started watching #{pid} on #{hostname} at #{@start_time.to_s} (#{@start_time.to_i}), I've been watching 'em for #{elapsed_time(@start_time)} seconds so far.
|
236
247
|
|
237
248
|
Awaiting it's demise,
|
238
249
|
|
@@ -245,7 +256,7 @@ TellMeWhen
|
|
245
256
|
|
246
257
|
send_email_notification "When! pid:#{@options[:pid]} has come to its end", <<-END
|
247
258
|
|
248
|
-
I started watching #{@options[:pid]} on #{hostname} at #{@start_time.to_s} (#{@start_time.to_i}), and now, after #{elapsed_time} seconds it has finally gone bellly up. #{@options[:pid]} will rest in peace as of #{@end_time.to_s} (#{@end_time.to_i})
|
259
|
+
I started watching #{@options[:pid]} on #{hostname} at #{@start_time.to_s} (#{@start_time.to_i}), and now, after #{elapsed_time(@start_time)} seconds it has finally gone bellly up. #{@options[:pid]} will rest in peace as of #{@end_time.to_s} (#{@end_time.to_i})
|
249
260
|
|
250
261
|
#{@options[:pid]} will be missed, it was a good little process. :,)
|
251
262
|
|
@@ -255,13 +266,16 @@ TellMeWhen
|
|
255
266
|
end
|
256
267
|
|
257
268
|
def wait_on_file_exists args
|
269
|
+
@current_command = "wait on file exists: #{@options[:trigger_file]}"
|
270
|
+
last_check_time = Time.now
|
258
271
|
while !File.exist? @options[:trigger_file]
|
259
272
|
sleep 0.250
|
260
|
-
if elapsed_time > wait_timeout
|
273
|
+
if elapsed_time(last_check_time) > wait_timeout
|
274
|
+
last_check_time = Time.now
|
261
275
|
puts "Exceeded timeout #{wait_timeout}, sending 'pending' notificaiton"
|
262
276
|
send_email_notification "When! [NOT] still awaiting #{@options[:trigger_file]} to exist", <<-END
|
263
277
|
|
264
|
-
I started watching for #{@options[:trigger_file]} on #{hostname} at #{@start_time.to_s} (#{@start_time.to_i}), I've been watching for it #{elapsed_time} seconds so far.
|
278
|
+
I started watching for #{@options[:trigger_file]} on #{hostname} at #{@start_time.to_s} (#{@start_time.to_i}), I've been watching for it #{elapsed_time(@start_time)} seconds so far.
|
265
279
|
|
266
280
|
Awaiting it's arrival,
|
267
281
|
|
@@ -273,7 +287,7 @@ TellMeWhen
|
|
273
287
|
|
274
288
|
send_email_notification "When! #{@options[:trigger_file]} now exists.", <<-END
|
275
289
|
|
276
|
-
I started watching for #{@options[:trigger_file]} on #{hostname} at #{@start_time.to_s} (#{@start_time.to_i}), and now, after #{elapsed_time} seconds it has finally shown up as of #{@end_time.to_s} (#{@end_time.to_i})
|
290
|
+
I started watching for #{@options[:trigger_file]} on #{hostname} at #{@start_time.to_s} (#{@start_time.to_i}), and now, after #{elapsed_time(@start_time)} seconds it has finally shown up as of #{@end_time.to_s} (#{@end_time.to_i})
|
277
291
|
|
278
292
|
What is thy next bidding my master?
|
279
293
|
|
@@ -282,16 +296,19 @@ TellMeWhen
|
|
282
296
|
end
|
283
297
|
|
284
298
|
def wait_on_file_modified args
|
299
|
+
@current_command = "wait on file modified: #{@options[:trigger_file]}"
|
285
300
|
trigger_file = @options[:trigger_file]
|
286
301
|
initial_mtime = File.mtime trigger_file
|
287
302
|
|
303
|
+
last_check_time = Time.now
|
288
304
|
while initial_mtime == File.mtime(trigger_file)
|
289
305
|
sleep 0.250
|
290
|
-
if elapsed_time > wait_timeout
|
306
|
+
if elapsed_time(last_check_time) > wait_timeout
|
307
|
+
last_check_time = Time.now
|
291
308
|
puts "Exceeded timeout #{wait_timeout}, sending 'pending' notificaiton"
|
292
309
|
send_email_notification "When! [NOT] still awaiting #{@options[:trigger_file]} to change", <<-END
|
293
310
|
|
294
|
-
I started watching for #{@options[:trigger_file]} to be updated on #{hostname} at #{@start_time.to_s} (#{@start_time.to_i}), I've been watching for it #{elapsed_time} seconds so far.
|
311
|
+
I started watching for #{@options[:trigger_file]} to be updated on #{hostname} at #{@start_time.to_s} (#{@start_time.to_i}), I've been watching for it #{elapsed_time(@start_time)} seconds so far.
|
295
312
|
|
296
313
|
Awaiting it's update,
|
297
314
|
|
@@ -304,7 +321,7 @@ TellMeWhen
|
|
304
321
|
|
305
322
|
send_email_notification "When! #{@options[:trigger_file]} was updated.", <<-END
|
306
323
|
|
307
|
-
I started watching for #{@options[:trigger_file]} to be updated on #{hostname} at #{@start_time.to_s} (#{@start_time.to_i}), and now, after #{elapsed_time} seconds it has finally been modified as of #{@end_time.to_s} (#{@end_time.to_i})
|
324
|
+
I started watching for #{@options[:trigger_file]} to be updated on #{hostname} at #{@start_time.to_s} (#{@start_time.to_i}), and now, after #{elapsed_time(@start_time)} seconds it has finally been modified as of #{@end_time.to_s} (#{@end_time.to_i})
|
308
325
|
|
309
326
|
POSIX is my zen,
|
310
327
|
|
@@ -321,11 +338,21 @@ TellMeWhen
|
|
321
338
|
end
|
322
339
|
|
323
340
|
def send_email_notification subject, body
|
341
|
+
if @settings["email"]["method"] == "smtp"
|
342
|
+
send_email_notification_via_smtp subject, body
|
343
|
+
elsif @settings["email"]["method"] == "mailtools"
|
344
|
+
send_email_notification_via_mailtools subject, body
|
345
|
+
else
|
346
|
+
send_email_notification_via_smtp subject, body
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
def send_email_notification_via_smtp subject, body
|
324
351
|
# optionally send via /usr/bin/mail or sendmail binary if it exists...
|
325
|
-
puts "Sending email: from:#{@settings["email"]["from"]} to:#{@settings["email"]["to"]}"
|
352
|
+
puts "[SMTP] Sending email: from:#{@settings["email"]["from"]} to:#{@settings["email"]["to"]}"
|
326
353
|
begin
|
327
354
|
Net::SMTP.start(smtp_host, smtp_port) do |smtp|
|
328
|
-
smtp.open_message_stream(
|
355
|
+
smtp.open_message_stream(@settings["email"]["from"], @settings["email"]["to"].split(',')) do |f|
|
329
356
|
f.puts "From: #{@settings["email"]["from"]}"
|
330
357
|
f.puts "To: #{@settings["email"]["to"]}"
|
331
358
|
f.puts "Subject: #{subject}"
|
@@ -335,7 +362,12 @@ TellMeWhen
|
|
335
362
|
end
|
336
363
|
end
|
337
364
|
rescue Errno::ECONNREFUSED => e
|
365
|
+
send_email_notification_via_mailtools subject, body
|
366
|
+
end
|
367
|
+
|
368
|
+
def send_email_notification_via_mailtools subject, body
|
338
369
|
if File.exist? "/usr/sbin/sendmail"
|
370
|
+
puts "[mailtools/sendmail] Sending email: from:#{@settings["email"]["from"]} to:#{@settings["email"]["to"]}"
|
339
371
|
body_file = Tempfile.new("tellmewhen.mail.body")
|
340
372
|
File.open(body_file.path,"w") do |f|
|
341
373
|
f.puts "From: #{@settings["email"]["from"]}"
|
@@ -344,9 +376,11 @@ TellMeWhen
|
|
344
376
|
f.puts ""
|
345
377
|
f.write body
|
346
378
|
end
|
379
|
+
puts "[sendmail] /usr/sbin/sendmail -f #{@settings["email"]["from"]} '#{@settings["email"]["to"]}' < #{body_file.path}"
|
380
|
+
#require 'ruby-debug'; debugger; 1;
|
347
381
|
system "/usr/sbin/sendmail -f #{@settings["email"]["from"]} '#{@settings["email"]["to"]}' < #{body_file.path}"
|
348
382
|
elsif File.exist? "/usr/bin/mail"
|
349
|
-
|
383
|
+
puts "[mailtools/mail] Sending email: from:#{@settings["email"]["from"]} to:#{@settings["email"]["to"]}"
|
350
384
|
body_file = Tempfile.new("tellmewhen.mail.body")
|
351
385
|
File.open(body_file.path,"w") do |f|
|
352
386
|
f.puts "From: #{@settings["email"]["from"]}"
|
Binary file
|
data/tellmewhen.gemspec
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tellmewhen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 1.0.
|
9
|
+
- 2
|
10
|
+
version: 1.0.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Kyle Burton
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-01-
|
18
|
+
date: 2011-01-26 00:00:00 -05:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|
@@ -39,6 +39,7 @@ extra_rdoc_files: []
|
|
39
39
|
files:
|
40
40
|
- bin/tellmewhen
|
41
41
|
- pkg/tellmewhen-1.0.0.gem
|
42
|
+
- pkg/tellmewhen-1.0.1.gem
|
42
43
|
- Rakefile
|
43
44
|
- README.textile
|
44
45
|
- tellmewhen.gemspec
|