Permalink
Browse files

establish exit status codes (closes #422)

When performing triggers:
  exit 0: all triggers succeeded without warnings
  exit 1: all triggers succeeded, but some had warnings
  exit 2: all triggers processed, but some failed
  exit 3: a fatal exception caused backup to exit

When `--check` is used:
  exit 0: success
  exit 1: failure
  • Loading branch information...
1 parent d89df3d commit 76a46e722f31f275a0c994487780a14994743530 Brian D. Burns committed Mar 23, 2013
Showing with 98 additions and 14 deletions.
  1. +23 −4 lib/backup/cli.rb
  2. +1 −1 lib/backup/model.rb
  3. +72 −7 spec/cli_spec.rb
  4. +2 −2 spec/model_spec.rb
View
27 lib/backup/cli.rb
@@ -9,12 +9,27 @@ class CLI < Thor
##
# [Perform]
# Performs the backup process. The only required option is the --trigger [-t].
- # If the other options (--config-file, --data-path, --cache-path, --tmp-path) aren't specified
- # they will fallback to the (good) defaults
+ # If the other options (--config-file, --data-path, --cache-path, --tmp-path)
+ # aren't specified they will fallback to the (good) defaults.
#
# If --root-path is given, it will be used as the base path for our defaults,
# as well as the base path for any option specified as a relative path.
# Any option given as an absolute path will be used "as-is".
+ #
+ # If the --check option is given, the config.rb and all model files will be
+ # loaded, but no triggers will be run. If the check fails, errors will be
+ # reported to the console. If the check passes, a success message will be
+ # reported to the console unless --quiet is set. Use --no-quiet to ensure
+ # these messages are output. The command will exit with status 0 if
+ # successful, or status 1 if there were problems.
+ #
+ # This command will exit with one of the following status codes:
+ #
+ # 0: All triggers were successful and no warnings were issued.
+ # 1: All triggers were successful, but some had warnings.
+ # 2: All triggers were processed, but some failed.
+ # 3: A fatal error caused Backup to exit.
+ # Some triggers may not have been processed.
desc 'perform', "Performs the backup for the specified trigger(s)."
long_desc "Performs the backup for the specified trigger(s).\n\n" +
"You may perform multiple backups by providing multiple triggers, separated by commas.\n\n" +
@@ -110,13 +125,13 @@ def perform
# Finalize Logger configuration and begin real-time logging.
Logger.start!
- rescue => err
+ rescue Exception => err
Logger.error Errors::CLIError.wrap(err)
Logger.error 'Configuration Check Failed.' if options[:check]
# Logger configuration will be ignored
# and messages will be output to the console only.
Logger.abort!
- exit(1)
+ exit(options[:check] ? 1 : 3)
end
if options[:check]
@@ -128,10 +143,14 @@ def perform
#
# Model#perform! handles all exceptions from this point,
# as each model may fail and return here to allow others to run.
+ warnings = errors = false
models.each do |model|
model.perform!
+ warnings ||= Logger.has_warnings?
+ errors ||= Logger.has_errors?
Logger.clear!
end
+ exit(errors ? 2 : 1) if errors || warnings
end
end
View
2 lib/backup/model.rb
@@ -241,7 +241,7 @@ def perform!
rescue Exception => err
log!(:failure, err)
send_failure_notifications
- exit(1) unless err.is_a?(StandardError)
+ exit(3) unless err.is_a?(StandardError)
end
private
View
79 spec/cli_spec.rb
@@ -214,8 +214,8 @@
Backup::Logger.expects(:start!).never
- model_a.expects(:preform!).never
- model_b.expects(:preform!).never
+ model_a.expects(:perform!).never
+ model_b.expects(:perform!).never
Backup::Logger.expects(:clear!).never
end
@@ -225,7 +225,7 @@
raises('config load error')
end
- it 'aborts and logs messages to the console only' do
+ it 'aborts with status code 3 and logs messages to the console only' do
Backup::Logger.expects(:error).in_sequence(s).with do |err|
err.should be_a(Backup::Errors::CLIError)
@@ -239,7 +239,7 @@
['perform', '-t', 'test_trigger_a']
)
cli.start
- end.to raise_error(SystemExit) {|exit| exit.status.should be(1) }
+ end.to raise_error(SystemExit) {|exit| exit.status.should be(3) }
end
end
@@ -263,11 +263,69 @@
['perform', '-t', 'test_trigger_foo']
)
cli.start
- end.to raise_error(SystemExit) {|exit| exit.status.should be(1) }
+ end.to raise_error(SystemExit) {|exit| exit.status.should be(3) }
end
end
end # describe 'failure to prepare for backups'
+ describe 'exit codes when backups have errors or warnings' do
+ before do
+ Backup::Config.stubs(:load_config!)
+ Backup::Logger.stubs(:start!)
+ end
+
+ specify 'when a job has warnings' do
+ model_a.expects(:perform!).in_sequence(s)
+ Backup::Logger.expects(:has_warnings?).in_sequence(s).returns(true)
+ Backup::Logger.expects(:has_errors?).in_sequence(s).returns(false)
+ Backup::Logger.expects(:clear!).in_sequence(s)
+ model_b.expects(:perform!).in_sequence(s)
+ Backup::Logger.expects(:has_errors?).in_sequence(s).returns(false)
+ Backup::Logger.expects(:clear!).in_sequence(s)
+
+ expect do
+ ARGV.replace(
+ ['perform', '-t', 'test_trigger_a,test_trigger_b']
+ )
+ cli.start
+ end.to raise_error(SystemExit) {|err| err.status.should be(1) }
+ end
+
+ specify 'when a job has errors' do
+ model_a.expects(:perform!).in_sequence(s)
+ Backup::Logger.expects(:has_warnings?).in_sequence(s).returns(false)
+ Backup::Logger.expects(:has_errors?).in_sequence(s).returns(true)
+ Backup::Logger.expects(:clear!).in_sequence(s)
+ model_b.expects(:perform!).in_sequence(s)
+ Backup::Logger.expects(:has_warnings?).in_sequence(s).returns(false)
+ Backup::Logger.expects(:clear!).in_sequence(s)
+
+ expect do
+ ARGV.replace(
+ ['perform', '-t', 'test_trigger_a,test_trigger_b']
+ )
+ cli.start
+ end.to raise_error(SystemExit) {|err| err.status.should be(2) }
+ end
+
+ specify 'when a jobs have errors and warnings' do
+ model_a.expects(:perform!).in_sequence(s)
+ Backup::Logger.expects(:has_warnings?).in_sequence(s).returns(false)
+ Backup::Logger.expects(:has_errors?).in_sequence(s).returns(true)
+ Backup::Logger.expects(:clear!).in_sequence(s)
+ model_b.expects(:perform!).in_sequence(s)
+ Backup::Logger.expects(:has_warnings?).in_sequence(s).returns(true)
+ Backup::Logger.expects(:clear!).in_sequence(s)
+
+ expect do
+ ARGV.replace(
+ ['perform', '-t', 'test_trigger_a,test_trigger_b']
+ )
+ cli.start
+ end.to raise_error(SystemExit) {|err| err.status.should be(2) }
+ end
+ end # describe 'exit codes when backups have errors or warnings'
+
describe '--check' do
before do
# performs no jobs
@@ -280,8 +338,8 @@
Backup::Config.expects(:load_config!).in_sequence(s)
- model_a.expects(:preform!).never
- model_b.expects(:preform!).never
+ model_a.expects(:perform!).never
+ model_b.expects(:perform!).never
Backup::Logger.expects(:clear!).never
end
@@ -383,6 +441,7 @@
cli.start
end
+ err.should be_empty
out.should == "Generated model file: '#{ model_file }'.\n" +
"Generated configuration file: '#{ config_file }'.\n"
File.exist?(model_file).should be_true
@@ -407,6 +466,7 @@
cli.start
end
+ err.should be_empty
out.should == "Generated model file: '#{ model_file }'.\n"
File.exist?(model_file).should be_true
end
@@ -452,6 +512,7 @@
cli.start
end
+ err.should be_empty
out.should == "Generated model file: '#{ model_file }'.\n" +
"Generated configuration file: '#{ config_file }'.\n"
File.exist?(model_file).should be_true
@@ -491,6 +552,7 @@
cli.start
end
+ err.should be_empty
output_usage, output_options, output_description =
out.split(/Usage:|Options:|Description:/, 4)[1..3]
@@ -539,6 +601,7 @@
cli.start
end
+ err.should be_empty
out.should == "Generated configuration file: '#{ config_file }'.\n"
File.exist?(config_file).should be_true
end
@@ -556,6 +619,7 @@
cli.start
end
+ err.should be_empty
out.should == "Generated configuration file: '#{ config_file }'.\n"
File.exist?(config_file).should be_true
end
@@ -854,6 +918,7 @@
out, err = capture_io do
cli.start
end
+ err.should be_empty
out.should == "Backup #{ Backup::Version.current }\n"
end
end
View
4 spec/model_spec.rb
@@ -432,7 +432,7 @@ def using_fake(const, replacement)
expect { model.perform! }.not_to raise_error
end
- it 'logs, notifies and exits if an Exception is rescued' do
+ it 'logs, notifies and exits with status code 3 if an Exception is rescued' do
err = Exception.new 'fatal error'
Time.stubs(:now).raises(err)
@@ -441,7 +441,7 @@ def using_fake(const, replacement)
expect do
model.perform!
- end.to raise_error(SystemExit) {|exit| exit.status.should be(1) }
+ end.to raise_error(SystemExit) {|exit| exit.status.should be(3) }
end
end # context 'when errors occur'

0 comments on commit 76a46e7

Please sign in to comment.