Skip to content
Browse files

Add --pid-file option

Improve --help output, failed run output

[git-p4: depot-paths = "//src/ar_mailer/dev/": change = 5488]
  • Loading branch information...
1 parent 8fc5d5d commit 5cecfc809e24a1dc25691cd0da8335e8750d1180 @drbrain drbrain committed Oct 15, 2009
Showing with 161 additions and 11 deletions.
  1. +6 −0 .autotest
  2. +5 −0 History.txt
  3. +63 −10 lib/action_mailer/ar_sendmail.rb
  4. +87 −1 test/test_arsendmail.rb
View
6 .autotest
@@ -0,0 +1,6 @@
+require 'autotest/restart'
+
+Autotest.add_hook :initialize do |at|
+ at.testlib = 'minitest/autorun'
+end
+
View
5 History.txt
@@ -1,3 +1,8 @@
+=== 1.5.0
+
+* Added --pid-file option. See --help for details.
+* Minor improvements to --help, argument error output
+
=== 1.4.0
* 1.8.7 and 1.9 STARTTLS compatibility, now uses smtp_tls gem for STARTTLS on
View
73 lib/action_mailer/ar_sendmail.rb
@@ -54,7 +54,7 @@ class ActionMailer::ARSendmail
##
# The version of ActionMailer::ARSendmail you are running.
- VERSION = '1.4.0'
+ VERSION = '1.5.0'
##
# Maximum number of times authentication will be consecutively retried
@@ -97,6 +97,26 @@ class ActionMailer::ARSendmail
attr_accessor :failed_auth_count
##
+ # Checks and writes +pid_file+, aborting if it already exists or this
+ # process loses the pid-writing race.
+
+ def self.check_pid(pid_file)
+ if File.exist? pid_file then
+ abort "pid file exists at #{pid_file}, exiting"
+ else
+ open pid_file, 'w', 0644 do |io|
+ io.write $$
+ end
+
+ written_pid = File.read pid_file
+
+ if written_pid.to_i != $$ then
+ abort "pid #{written_pid} from #{pid_file} doesn't match $$ #{$$}, exiting"
+ end
+ end
+ end
+
+ ##
# Creates a new migration using +table_name+ and prints it on stdout.
def self.create_migration(table_name)
@@ -193,13 +213,21 @@ def self.process_args(args)
options[:TableName] = 'Email'
op = OptionParser.new do |opts|
- opts.banner = "Usage: #{name} [options]"
- opts.separator ''
+ opts.program_name = name
+ opts.version = VERSION
- opts.separator "#{name} scans the email table for new messages and sends them to the"
- opts.separator "website's configured SMTP host."
- opts.separator ''
- opts.separator "#{name} must be run from a Rails application's root."
+ opts.banner = <<-BANNER
+Usage: #{name} [options]
+
+#{name} scans the email table for new messages and sends them to the
+website's configured SMTP host.
+
+#{name} must be run from a Rails application's root or have it specified
+with --chdir.
+
+If #{name} is started with --pid-file, it will fail to start if the PID
+file already exists or the contents don't match it's PID.
+ BANNER
opts.separator ''
opts.separator 'Sendmail options:'
@@ -231,6 +259,20 @@ def self.process_args(args)
options[:Once] = once
end
+ opts.on("-p", "--pid-file [PATH]",
+ "File to store the pid in.",
+ "Defaults to /var/run/ar_sendmail.pid",
+ "when no path is given") do |pid_file|
+ pid_file ||= '/var/run/ar_sendmail/ar_sendmail.pid'
+
+ pid_dir = File.dirname pid_file
+ raise OptionParser::InvalidArgument,
+ "directory #{pid_dir} does not exist" unless
+ File.directory? pid_dir
+
+ options[:PidFile] = pid_file
+ end
+
opts.on("-d", "--daemonize",
"Run as a daemon process",
"Default: #{options[:Daemon]}") do |daemon|
@@ -308,6 +350,7 @@ def self.process_args(args)
rescue LoadError
usage op, <<-EOF
#{name} must be run from a Rails application's root to deliver email.
+
#{Dir.pwd} does not appear to be a Rails application root.
EOF
end
@@ -338,7 +381,16 @@ def self.run(args = ARGV)
WEBrick::Daemon.start
end
- new(options).run
+ sendmail = new options
+
+ check_pid options[:PidFile] if options.key? :PidFile
+
+ begin
+ sendmail.run
+ ensure
+ File.unlink options[:PidFile] if
+ options.key? :PidFile and $PID == File.read(options[:PidFile]).to_i
+ end
rescue SystemExit
raise
@@ -354,12 +406,13 @@ def self.run(args = ARGV)
# Prints a usage message to $stderr using +opts+ and exits
def self.usage(opts, message = nil)
+ $stderr.puts opts
+
if message then
- $stderr.puts message
$stderr.puts
+ $stderr.puts message
end
- $stderr.puts opts
exit 1
end
View
88 test/test_arsendmail.rb
@@ -1,3 +1,4 @@
+require 'tmpdir'
require 'action_mailer'
require 'action_mailer/ar_sendmail'
require 'rubygems'
@@ -23,10 +24,64 @@ def setup
@include_c_e = ! $".grep(/config\/environment.rb/).empty?
$" << 'config/environment.rb' unless @include_c_e
+
+ @pid_file = File.join Dir.tmpdir, "test_#{$$}_ar_sendmail.pid"
end
def teardown
$".delete 'config/environment.rb' unless @include_c_e
+
+ File.unlink @pid_file if File.exist? @pid_file
+ end
+
+ def test_class_check_pid
+ ActionMailer::ARSendmail.check_pid @pid_file
+
+ assert File.exist?(@pid_file)
+
+ assert_equal $$, File.read(@pid_file).to_i
+ rescue SystemExit
+ flunk 'pid file teardown failed'
+ end
+
+ def test_class_check_pid_exists
+ ActionMailer::ARSendmail.check_pid @pid_file
+
+ out, err = capture_io do
+ assert_raises SystemExit do
+ ActionMailer::ARSendmail.check_pid @pid_file
+ end
+ end
+
+ assert_equal '', out
+ assert_equal "pid file exists at #{@pid_file}, exiting\n", err
+ end
+
+ def test_class_check_pid_no_match
+ def (ActionMailer::ARSendmail).open(path, mode, perm)
+ fake_io = Object.new
+ def fake_io.write(data) end
+
+ File.open path, mode, perm do |io|
+ yield fake_io
+
+ io.write 0
+ end
+ end
+
+ out, err = capture_io do
+ assert_raises SystemExit do
+ ActionMailer::ARSendmail.check_pid @pid_file
+ end
+ end
+
+ assert_equal '', out
+ assert_equal "pid 0 from #{@pid_file} doesn't match $$ #{$$}, exiting\n",
+ err
+ ensure
+ class << ActionMailer::ARSendmail
+ send :remove_method, :open
+ end
end
def test_class_create_migration
@@ -300,6 +355,37 @@ def test_class_parse_args_once
assert_equal true, options[:Once]
end
+ def test_class_parse_args_pid_file
+ argv = %w[]
+
+ options = ActionMailer::ARSendmail.process_args argv
+
+ refute options.key?(:PidFile), 'no --pid-file option'
+
+ argv = %w[--pid-file]
+
+ options = ActionMailer::ARSendmail.process_args argv
+
+ assert_equal '/var/run/ar_sendmail/ar_sendmail.pid', options[:PidFile]
+
+ argv = %w[--pid-file=/tmp/ar_sendmail.pid]
+
+ options = ActionMailer::ARSendmail.process_args argv
+
+ assert_equal '/tmp/ar_sendmail.pid', options[:PidFile]
+ end
+
+ def test_class_parse_args_pid_file_nonexistent
+ argv = %w[--pid-file /nonexistent/ar_sendmail.pid]
+
+ e = assert_raises OptionParser::InvalidArgument do
+ ActionMailer::ARSendmail.process_args argv
+ end
+
+ assert_equal 'invalid argument: --pid-file directory /nonexistent does not exist',
+ e.message
+ end
+
def test_class_parse_args_table_name
argv = %w[-t Email]
@@ -331,7 +417,7 @@ def test_class_usage
end
assert_equal '', out
- assert_equal "hi\n\nopts\n", err
+ assert_equal "opts\n\nhi\n", err
end
def test_cleanup

0 comments on commit 5cecfc8

Please sign in to comment.
Something went wrong with that request. Please try again.