jobmanager 1.0.1 → 1.1.0

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,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