jobmanager 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,10 @@
1
- === 1.0.1 / 2007-08-27
1
+ === 1.1.0 / 2009-06-01
2
+ The following changes were made:
3
+ * jobmanager no longer generates a job log file (nor rotates) in the case that the job output is empty.
4
+ * jobmanager can now be configured to email job results only when the job output is not empty ("on_job_output_or_failure").
5
+ * jobmanager can now be configured to not rotate the job log files
6
+ * jobmanager can now be configured to prepend the date/time to each line of a job log file.
7
+ === 1.0.1 / 2008-08-27
2
8
  Remove the dependency on the sys-host gem.
3
9
  === 1.0.0 / 2008-07-18
4
10
  Initial release of jobmanager. This package is unit tested fully.
@@ -7,21 +7,23 @@ Rakefile
7
7
  bin/jobmanager
8
8
  examples/email_settings.rb
9
9
  examples/example.rb
10
- examples/mysql_backup.rb
11
10
  examples/jobmanager.yaml
12
11
  lib/jobmanager.rb
13
12
  lib/jobmanager/applicationconfig.rb
14
13
  lib/jobmanager/applicationlogger.rb
15
14
  lib/jobmanager/applicationoptionparser.rb
16
15
  lib/jobmanager/application.rb
16
+ lib/jobmanager/openonfirstwritefile.rb
17
17
  lib/jobmanager/system.rb
18
18
  lib/jobmanager/teestream.rb
19
+ lib/jobmanager/timestampedstream.rb
19
20
  lib/jobmanager/util.rb
20
21
  test/helpers.rb
21
22
  test/mock_syslog.rb
22
23
  test/test_applicationlogger.rb
23
24
  test/test_config.rb
24
25
  test/test_jobmanager.rb
26
+ test/test_openonfirstwritefile.rb
25
27
  test/test_system.rb
26
28
  test/test_teestream.rb
27
29
  test/configs/email_settings.rb
@@ -37,6 +39,8 @@ test/configs/jobmanager_3.yaml
37
39
  test/configs/jobmanager_4_bad_central_log_file.yaml
38
40
  test/configs/jobmanager_4_bad_email_settings_file.yaml
39
41
  test/configs/jobmanager_4_bad_job_logs_directory_1.yaml
40
- test/configs/jobmanager_4_bad_job_logs_directory_2.yaml
42
+ test/configs/jobmanager_5_bad_job_log_file.yaml
43
+ test/configs/jobmanager_6.yaml
44
+ test/configs/jobmanager_7_no_log_rotation.yaml
41
45
  test/configs/minimum_plus_email_settings.yaml
42
46
  test/configs/minimum_required.yaml
data/README.txt CHANGED
@@ -25,11 +25,11 @@ added value:
25
25
  Lets walk through an example invocation of +jobmanager+ via cron.
26
26
 
27
27
  Here is an example +crontab+ entry that invokes a simplified database
28
- backup script via +jobmanager+ every day at 22:00.
29
- 00 22 * * * jobmanager --job_name nightly_backup "mysql_backup test"
28
+ backup script via +jobmanager+ every day at 17:16.
29
+ 16 17 * * * jobmanager --job_name nightly_backup "mysql_backup test"
30
30
 
31
31
  Here is the example +mysql_backup+ script referenced above:
32
- ======<tt>examples/mysql_backup.rb</tt>:
32
+ ======<tt>examples/mysql_backup</tt>:
33
33
  #!/usr/bin/env ruby
34
34
 
35
35
  print "=================================================\n"
@@ -50,10 +50,10 @@ Here is the example +mysql_backup+ script referenced above:
50
50
 
51
51
 
52
52
 
53
- Here is the configuration file to be read by +jobmanager+.
53
+ Here is the example configuration file to be read by +jobmanager+.
54
54
  ======<tt>/etc/jobmanager.yaml</tt>:
55
55
  ################################################################################
56
- # The jobmanager configuration file.
56
+ # An example jobmanager configuration file.
57
57
  #
58
58
  # For a full description of each of the fields, and their default values,
59
59
  # see the CONFIGURATION FILE section in README.txt.
@@ -76,14 +76,14 @@ Here is the configuration file to be read by +jobmanager+.
76
76
  # variables: user_name.
77
77
  central_log_file: /tmp/logs/jobmanager.log
78
78
 
79
- # The path to search for the command that jobmanager is invoked with.
79
+ # The path to search for the command that jobmanager is invoked with.
80
80
  #command_path: /usr/bin:/usr/designingpatterns/bin
81
81
 
82
82
  # Whether jobmanager should print debug trace to the central log file.
83
- debug: false
83
+ #debug: true
84
84
 
85
85
  # The condition upon which condition results should be emailed.
86
- # Possible values are: always, never, on_failure.
86
+ # Possible values are: always, never, on_failure, on_job_output_or_failure.
87
87
  email_condition: always
88
88
 
89
89
  # The configuration file for the simpleemail gem. This parameter will
@@ -96,21 +96,31 @@ Here is the configuration file to be read by +jobmanager+.
96
96
  # Allowed variables: job_name, command, host_name, results, user_name.
97
97
  email_subject: "jobmanager results for <%=job_name%> on <%=host_name%> : <%=result%>"
98
98
 
99
- # The extension of the rotated job log file. If true, the extension
100
- # is a date_time. Otherwise, it is a simple count (.1, .2, etc.).
101
- date_time_extension: true
99
+ # Whether the job log file should be rotated.
100
+ #rotate_job_log: false
101
+
102
+ # The extension of the rotated job log file.
103
+ # True -> The extension is a date_time.
104
+ # False -> It is a simple count (.1, .2, etc.).
105
+ #date_time_extension: true
102
106
 
103
107
  # If date_time_extension is true, this field will be used as the
104
108
  # format for the date/time extension of the rotated job log file
105
109
  # name.
106
- date_time_extension_format: '%F_%T'
110
+ #date_time_extension_format: '%F_%T'
107
111
 
108
112
  # Whether the rotated job log file should be zipped.
109
- zip_rotated_log_file: false
113
+ #zip_rotated_log_file: true
110
114
 
111
115
  # The timeout (in seconds). If not present, no timeout will be used.
112
116
  timeout: 1800
113
117
 
118
+ # Whether each line in the job log file will be prepended with the date/time.
119
+ #job_log_prepend_date_time: true
120
+
121
+ # The date/time format of the prepended date/time.
122
+ #job_log_prepend_date_time: '%F_%T'
123
+
114
124
  # The sender of emails sent from jobmanager. This can also be
115
125
  # specified in the email_settings_file (attribute default_from).
116
126
  #email_from_address: "Sender <sender@gmail.com>"
@@ -141,15 +151,21 @@ sendmail is also an option. This file is documented further in the
141
151
  SimpleEmail::Email.default_to = "Receiver Address <receiver_address@gmail.com>"
142
152
 
143
153
 
154
+ Note: In order to run this example oneself, either
155
+ * +mysql_backup+ script must live in a directory that is in one's path, or
156
+ * the +command_path+ field in +jobmanager.yaml+ must be set to include the path
157
+ where +mysql_backup+ lives.
158
+
144
159
  Here is the central log file that +jobmanager+ updated.
145
160
  ======<tt>/tmp/logs/jobmanager.log</tt>:
146
- I, [2008-07-30T22:00:00 #2301] INFO -- : [janet, nightly_backup] Command (/usr/bin/mysql_backup test) launched, pid = 2302.
147
- I, [2008-07-30T22:00:00 #2301] INFO -- : [janet, nightly_backup] Exited successfully
148
- I, [2008-07-30T22:00:00 #2301] INFO -- : [janet, nightly_backup] Job log rotated to /tmp/logs/nightly_backup.log.2008-07-30_22:00:00.
161
+ I, [2009-06-01T17:16:32 #32235] INFO -- : [janet, nightly_backup] Command (/usr/bin/mysql_backup test) launched, pid = 32237.
162
+ I, [2009-06-01T17:16:33 #32235] INFO -- : [janet, nightly_backup] Job exited successfully
163
+ I, [2009-06-01T17:16:33 #32235] INFO -- : [janet, nightly_backup] Job log rotated to /tmp/logs/nightly_backup.log.2009-06-01_17:16:33.
164
+ I, [2009-06-01T17:16:33 #32235] INFO -- : [janet, nightly_backup] Sent mail to Receiver Address <receiver_address@gmail.com>
149
165
 
150
166
 
151
167
  Here is the rotated job log file.
152
- ======<tt>/tmp/logs/nightly_backup.log.2008-07-30_22:00:00</tt>:
168
+ ======<tt>/tmp/logs/nightly_backup.log.2009-06-01_17:16:33</tt>:
153
169
  =================================================
154
170
  Backing up Database
155
171
  =================================================
@@ -157,6 +173,7 @@ Here is the rotated job log file.
157
173
 
158
174
  Success!
159
175
 
176
+
160
177
  Here is the subject and body of the email that was sent upon job completion.
161
178
 
162
179
  ======<tt>Email Subject</tt>:
@@ -166,9 +183,9 @@ Here is the subject and body of the email that was sent upon job completion.
166
183
  ---------------------------------------------------------
167
184
  jobmanager Output:
168
185
  ---------------------------------------------------------
169
- I, [2008-07-30T22:00:00 #2301] INFO -- : [janet, nightly_backup] Command (/usr/bin/mysql_backup test) launched, pid = 2302.
170
- I, [2008-07-30T22:00:00 #2301] INFO -- : [janet, nightly_backup] Exited successfully
171
- I, [2008-07-30T22:00:00 #2301] INFO -- : [janet, nightly_backup] Job log rotated to /tmp/logs/nightly_backup.log.2008-08-01_11:29:47.
186
+ I, [2009-06-01T17:16:23 #32219] INFO -- : [janet, nightly_backup] Command (/usr/bin/mysql_backup test) launched, pid = 32222.
187
+ I, [2009-06-01T17:16:24 #32219] INFO -- : [janet, nightly_backup] Job exited successfully
188
+ I, [2009-06-01T17:16:24 #32219] INFO -- : [janet, nightly_backup] Job log rotated to /tmp/logs/nightly_backup.log.2009-06-01_17:16:24.
172
189
 
173
190
  ---------------------------------------------------------
174
191
  Job Output:
@@ -216,6 +233,15 @@ The configuration file provides the following parameters:
216
233
  [+number_of_job_logs+: optional, default: +3+]
217
234
  The number of logs to be kept per job.
218
235
 
236
+ [+job_log_prepend_date_time+: optional, default: +false+]
237
+ Whether the current date/time should be prepended to each line of the
238
+ job log file.
239
+
240
+ [+job_log_prepend_date_time_format+: optional, default: <tt>'%F_%T'</tt> ]
241
+ if +job_log_prepend_date_time+ is +true+, this parameter will be used as
242
+ the format for the date/time that is prepended to each line of the job
243
+ log file.
244
+
219
245
  [+command_path+: optional, default: <tt>ENV['PATH']</tt> ]
220
246
  The path to be searched for the command that +jobmanager+ is
221
247
  invoked with. If unspecified, the environment path will be used.
@@ -225,7 +251,9 @@ The configuration file provides the following parameters:
225
251
 
226
252
  [+email_condition+: optional, default: +on_failure+]
227
253
  The condition upon which results should be emailed. Possible
228
- values are: <tt>always, never, on_failure</tt>.
254
+ values are: <tt>always, never, on_failure, on_job_output_or_failure</tt>.
255
+ The condition +on_job_output_or_failure+ denotes that an email will be
256
+ sent if the job failed and/or the job output was non-empty.
229
257
 
230
258
  [+email_settings_file+: optional, default: +email_settings.rb+]
231
259
  The configuration file for the
@@ -243,34 +271,38 @@ The configuration file provides the following parameters:
243
271
  separate email settings files.
244
272
 
245
273
 
246
- [+email_subject+, optional, default: <tt>"jobmanager results for <%=job_name%> on <%=host_name%> : <%=result%>"</tt>]
274
+ [+email_subject+: optional, default: <tt>"jobmanager results for <%=job_name%> on <%=host_name%> : <%=result%>"</tt>]
247
275
  The email subject, to be interpreted by ERB. Allowed variables
248
276
  inside the string are: <tt>job_name, command, host_name, results, user_name</tt>.
249
277
 
250
- [+date_time_extension+, optional, default: +true+]
278
+ [+rotate_job_log+: optional, default: +true+]
279
+ Whether the job log file should be rotated. If the job log file is not
280
+ rotated, it will simply be appended to during each run.
281
+
282
+ [+date_time_extension+: optional, default: +true+]
251
283
  This refers to the extension of the rotated job log file. If
252
284
  +date_time_extension+ is present and set to +true+, the extension
253
285
  represents a date and time. Otherwise, it is a simple count (.1, .2, etc.).
254
286
 
255
- [+date_time_extension_format+, optional, default: <tt>'%F_%T'</tt>]
287
+ [+date_time_extension_format+: optional, default: <tt>'%F_%T'</tt>]
256
288
  If +date_time_extension+ is +true+, this parameter will be used as the
257
289
  format for the date and time extension of the rotated job log file.
258
290
 
259
- [+zip_rotated_log_file+, optional, default: +false+]
291
+ [+zip_rotated_log_file+: optional, default: +false+]
260
292
  Whether the rotated job log file should be gzip'ed.
261
293
 
262
- [+timeout+, optional]
294
+ [+timeout+: optional]
263
295
  The timeout (in seconds). If the job spawned by +jobmanager+ does not
264
296
  exit after +timeout+ seconds, +jobmanager+ will kill the process, and
265
297
  report failure. If +timeout+ is unspecifed, +jobmanager+ will let the
266
298
  process run for an unlimited amount of time (+jobmanager+ will simply
267
299
  wait til it returns).
268
300
 
269
- [+email_from_address+, optional]
301
+ [+email_from_address+: optional]
270
302
  The sender of emails sent from +jobmanager+. This can also be
271
303
  specified in the +email_settings_file+ (attribute +default_from+).
272
304
 
273
- [+email_to_address+, optional]
305
+ [+email_to_address+: optional]
274
306
  The receiver of emails sent from +jobmanager+. This can also be
275
307
  specified in the +email_settings_file+ (attribute +default_to+).
276
308
 
@@ -321,7 +353,8 @@ listed above are required for the proper functioning of the
321
353
 
322
354
  2. Set up the +jobmanager+ configuration file (+jobmanager.yaml+).
323
355
  For more detailed instructions, see the CONFIGURATION FILE section
324
- above.
356
+ above. There is an example file provided in this distribution:
357
+ +examples/jobmanager.yaml+.
325
358
 
326
359
  3. Set up the simpleemail configuration file (+email_settings.rb+).
327
360
  Alternately, if you want to disable email, set the configuration
@@ -329,9 +362,10 @@ listed above are required for the proper functioning of the
329
362
  the {+simpleemail+}[http://simpleemail.rubyforge.org] gem rdoc for
330
363
  more details.
331
364
 
332
- 4. Confirm that the configuration files are valid by invoking
333
- +jobmanager+ from the command line with one of your cron jobs or a
334
- test command.
365
+ 4. Confirm that the configuration files are valid by invoking +jobmanager+
366
+ from the command line with one of your cron jobs or a test command. If
367
+ +jobmanager+ is invoked via the command line, the central log will be
368
+ redirected to screen (instead of the central log file).
335
369
 
336
370
  5. Edit your +crontab+ to run new and/or existing cron jobs through
337
371
  +jobmanager+.
data/Rakefile CHANGED
@@ -2,14 +2,14 @@
2
2
  require 'rubygems'
3
3
  require 'hoe'
4
4
 
5
- Hoe.new('jobmanager', "1.0.1") do |p|
5
+ Hoe.new('jobmanager', "1.1.0") do |p|
6
6
  p.remote_rdoc_dir = ''
7
7
  p.developer('DesigningPatterns', 'technical.inquiries@designingpatterns.com')
8
8
 
9
9
  p.extra_deps << ['simpleemail']
10
10
  p.extra_deps << ['configtoolkit', '>= 2.2']
11
11
  p.extra_deps << ['relative']
12
- p.extra_deps << ['logrotate']
12
+ p.extra_deps << ['logrotate', '>= 1.1']
13
13
  p.extra_deps << ['SyslogLogger']
14
14
  end
15
15
 
@@ -1,5 +1,5 @@
1
1
  ################################################################################
2
- # The jobmanager configuration file.
2
+ # An example jobmanager configuration file.
3
3
  #
4
4
  # For a full description of each of the fields, and their default values,
5
5
  # see the CONFIGURATION FILE section in README.txt.
@@ -22,14 +22,14 @@ central_log_mode: file
22
22
  # variables: user_name.
23
23
  central_log_file: /tmp/logs/jobmanager.log
24
24
 
25
- # The path to search for the command that jobmanager is invoked with.
25
+ # The path to search for the command that jobmanager is invoked with.
26
26
  #command_path: /usr/bin:/usr/designingpatterns/bin
27
27
 
28
28
  # Whether jobmanager should print debug trace to the central log file.
29
- debug: false
29
+ #debug: true
30
30
 
31
31
  # The condition upon which condition results should be emailed.
32
- # Possible values are: always, never, on_failure.
32
+ # Possible values are: always, never, on_failure, on_job_output_or_failure.
33
33
  email_condition: always
34
34
 
35
35
  # The configuration file for the simpleemail gem. This parameter will
@@ -42,21 +42,31 @@ email_settings_file: email_settings.rb
42
42
  # Allowed variables: job_name, command, host_name, results, user_name.
43
43
  email_subject: "jobmanager results for <%=job_name%> on <%=host_name%> : <%=result%>"
44
44
 
45
- # The extension of the rotated job log file. If true, the extension
46
- # is a date_time. Otherwise, it is a simple count (.1, .2, etc.).
47
- date_time_extension: true
45
+ # Whether the job log file should be rotated.
46
+ #rotate_job_log: false
47
+
48
+ # The extension of the rotated job log file.
49
+ # True -> The extension is a date_time.
50
+ # False -> It is a simple count (.1, .2, etc.).
51
+ #date_time_extension: true
48
52
 
49
53
  # If date_time_extension is true, this field will be used as the
50
54
  # format for the date/time extension of the rotated job log file
51
55
  # name.
52
- date_time_extension_format: '%F_%T'
56
+ #date_time_extension_format: '%F_%T'
53
57
 
54
58
  # Whether the rotated job log file should be zipped.
55
- zip_rotated_log_file: false
59
+ #zip_rotated_log_file: true
56
60
 
57
61
  # The timeout (in seconds). If not present, no timeout will be used.
58
62
  timeout: 1800
59
63
 
64
+ # Whether each line in the job log file will be prepended with the date/time.
65
+ #job_log_prepend_date_time: true
66
+
67
+ # The date/time format of the prepended date/time.
68
+ #job_log_prepend_date_time: '%F_%T'
69
+
60
70
  # The sender of emails sent from jobmanager. This can also be
61
71
  # specified in the email_settings_file (attribute default_from).
62
72
  #email_from_address: "Sender <sender@gmail.com>"
@@ -22,7 +22,9 @@ require 'jobmanager/applicationconfig'
22
22
  require 'jobmanager/applicationoptionparser'
23
23
  require 'jobmanager/system'
24
24
  require 'jobmanager/teestream'
25
+ require 'jobmanager/timestampedstream'
25
26
  require 'jobmanager/applicationlogger'
27
+ require 'jobmanager/openonfirstwritefile'
26
28
 
27
29
  module JobManager
28
30
 
@@ -182,20 +184,15 @@ module JobManager
182
184
  def run()
183
185
  success = false
184
186
 
185
- begin
186
- FileUtils.mkdir_p(@config.job_logs_directory)
187
- rescue => e
188
- raise ComposedError.new("Error creating job_logs_directory", e)
189
- end
190
-
191
187
  job_log_file = File.join(@config.job_logs_directory, "#{@config.job_name}.log")
192
- @mgr_logger.debug("Opening job log file #{job_log_file}.")
188
+ @mgr_logger.debug("Job log file: #{job_log_file}.")
193
189
 
194
- begin
195
- job_log_file_stream = File.open(job_log_file, "w")
196
- rescue => e
197
- raise ComposedError.new("Error opening job log file", e)
198
- end
190
+ #
191
+ # Create a stream that only opens the specified file when the
192
+ # first write is called. This is to prevent empty files from
193
+ # being created.
194
+ #
195
+ job_log_file_stream = OpenOnFirstWriteFile.new(job_log_file, "a")
199
196
 
200
197
  #
201
198
  # The output of the job that will be invoked will be sent back
@@ -216,9 +213,13 @@ module JobManager
216
213
  # TeeStream to forward strings to the job log file stream and a
217
214
  # string stream (which will be later used for emailing results).
218
215
  #
216
+ job_output = ""
219
217
  begin
220
- job_tee_stream = TeeStream.new(:file => job_log_file_stream,
221
- :stringio => StringIO.new())
218
+ job_stream = TeeStream.new(:file => job_log_file_stream,
219
+ :stringio => StringIO.new())
220
+ if (@config.job_log_prepend_date_time)
221
+ job_stream = TimestampedStream.new(job_stream, @config.job_log_prepend_date_time_format)
222
+ end
222
223
 
223
224
  optional_args = {}
224
225
  if @config.timeout then optional_args[:timeout] = @config.timeout end
@@ -226,23 +227,23 @@ module JobManager
226
227
 
227
228
  # invoke the command - fork and exec the process, and wait til it finishes to reap the status.
228
229
  success = System::invoke_command(@config.command,
229
- job_tee_stream,
230
+ job_stream,
230
231
  @mgr_logger,
231
232
  optional_args)
232
233
 
233
- job_output = job_tee_stream.get_stream(:stringio).string()
234
- job_tee_stream.close()
234
+ if (job_stream.is_a?(TimestampedStream))
235
+ job_stream = job_stream.stream()
236
+ end
237
+ job_output = job_stream.stream(:stringio).string()
238
+ job_stream.close()
235
239
 
236
240
  rescue => e
237
241
  @mgr_logger.record_tagged_exception("Error running job", e)
238
242
  end
239
243
 
240
244
  # Rotate the job log file.
241
- begin
242
- if (File.file?(job_log_file))
243
-
244
- before_entries = Dir.glob("#{@config.job_logs_directory}/#{@config.job_name}*")
245
-
245
+ if (@config.rotate_job_log && File.file?(job_log_file))
246
+ begin
246
247
  logrotate_options = {
247
248
  :count => @config.number_of_job_logs,
248
249
  :date_time_ext => @config.date_time_extension,
@@ -250,20 +251,20 @@ module JobManager
250
251
  :gzip => @config.zip_rotated_log_file
251
252
  }
252
253
 
253
- LogRotate.rotate_file(job_log_file, logrotate_options)
254
+ result = LogRotate.rotate_file(job_log_file, logrotate_options)
254
255
 
255
- rotated_file = (Dir.glob("#{@config.job_logs_directory}/#{@config.job_name}*") - before_entries)[0]
256
- @mgr_logger.info("Job log rotated to #{rotated_file}.")
256
+ @mgr_logger.info("Job log rotated to #{result.new_rotated_file}.")
257
+ rescue => e
258
+ success = false
259
+ @mgr_logger.record_tagged_exception("Error rotating file #{job_log_file}", e)
257
260
  end
258
- rescue => e
259
- success = false
260
- @mgr_logger.record_tagged_exception("Error rotating file #{job_log_file}", e)
261
261
  end
262
262
 
263
263
  # Email the results!
264
264
  begin
265
265
  if (@config.email_condition == :always ||
266
- (@config.email_condition == :on_failure && !success))
266
+ (@config.email_condition == :on_failure && !success) ||
267
+ (@config.email_condition == :on_job_output_or_failure && (!success || job_output.length > 0)))
267
268
 
268
269
  email_results(success, job_output)
269
270
  end
@@ -340,7 +341,7 @@ module JobManager
340
341
 
341
342
  @config.email_settings_file = value
342
343
  end
343
-
344
+
344
345
  # Note: From/To email addresses can be specified in either the
345
346
  # jobmanager configuration file or the simpleemail gem
346
347
  # configuration file.
@@ -372,7 +373,7 @@ module JobManager
372
373
  begin
373
374
  new_logger = ApplicationFileLogger.new(@config.central_log_file,
374
375
  @user_name)
375
-
376
+
376
377
  old_logger = @mgr_logger
377
378
  @mgr_logger = new_logger
378
379
  old_logger.close