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.
@@ -1,6 +1,6 @@
1
1
  h1. tellmewhen
2
2
 
3
- Tell me when another program has finshed. Show me when it started, when it finished and how long it took. Show me the output and if it errored.
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, by grep pattern over 'ps aux')
13
- * release as a gem so it's easy to use/install
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. Usage: ./tellmewhen command args...
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. ./tellmewhen 'sleep 3; ls'
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
 
@@ -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 - @start_time.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
- #{args.to_s}
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 #{args.split.first}...", body
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('from_addr', @settings["email"]["to"].split(',')) do |f|
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
- raise "Implement sending via /usr/bin/mail"
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
@@ -3,7 +3,7 @@ require 'rubygems'
3
3
 
4
4
  SPEC = Gem::Specification.new do |s|
5
5
  s.name = "tellmewhen"
6
- s.version = "1.0.1"
6
+ s.version = "1.0.2"
7
7
  s.author = "Kyle Burton"
8
8
  s.email = "kyle.burton@gmail.com"
9
9
  s.platform = Gem::Platform::RUBY
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: 21
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 0
9
- - 1
10
- version: 1.0.1
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-25 00:00:00 -05:00
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