Skip to content

Commit

Permalink
11280 - mco should exit != 0 when no nodes are found
Browse files Browse the repository at this point in the history
There are various failure states for a typical client and the unix
convention is that failure should be indicated by non 0 exit code

This commit introduce a halt() helper on the Applications class and
changes all the built in application plugins to use this helper to
standardize the exit codes as per the list:

 * Exit with 0 if nodes were discovered and all passed
 * Exit with 0 if no discovery were done and > 0 responses were received
 * Exit with 1 if no nodes were discovered
 * Exit with 2 if nodes were discovered but some RPC requests failed
 * Exit with 3 if no responses were received but discovery were done
 * Exit with 4 if no discovery were done and no responses were received
  • Loading branch information
ripienaar committed Jan 10, 2012
1 parent f95a5f3 commit ee4b20e
Show file tree
Hide file tree
Showing 10 changed files with 168 additions and 7 deletions.
42 changes: 42 additions & 0 deletions lib/mcollective/application.rb
Expand Up @@ -252,6 +252,48 @@ def main
exit 1
end

# A helper that creates a consistent exit code for applications by looking at an
# instance of MCollective::RPC::Stats
#
# Exit with 0 if nodes were discovered and all passed
# Exit with 0 if no discovery were done and > 0 responses were received
# Exit with 1 if no nodes were discovered
# Exit with 2 if nodes were discovered but some RPC requests failed
# Exit with 3 if nodes were discovered, but not responses receivedif
# Exit with 4 if no discovery were done and no responses were received
def halt(stats)
request_stats = {:discoverytime => 0,
:discovered => 0,
:failcount => 0}.merge(stats.to_hash)

# was discovery done?
if request_stats[:discoverytime] != 0
# was any nodes discovered
if request_stats[:discovered] == 0
exit 1

# nodes were discovered, did we get responses
elsif request_stats[:responses] == 0
exit 3

else
# we got responses and discovery was done, no failures
if request_stats[:failcount] == 0
exit 0
else
exit 2
end
end
else
# discovery wasnt done and we got no responses
if request_stats[:responses] == 0
exit 4
else
exit 0
end
end
end

# Wrapper around MC::RPC#rpcclient that forcably supplies our options hash
# if someone forgets to pass in options in an application the filters and other
# cli options wouldnt take effect which could have a disasterous outcome
Expand Down
4 changes: 2 additions & 2 deletions plugins/mcollective/application/controller.rb
Expand Up @@ -85,8 +85,8 @@ def main
client.disconnect

client.display_stats(statistics, false, "mcollectived controller summary")

halt statistics
end
end
end

# vim: set ts=4 sw=4 et :
2 changes: 2 additions & 0 deletions plugins/mcollective/application/facts.rb
Expand Up @@ -47,5 +47,7 @@ def main
show_single_fact_report(configuration[:fact], facts, options[:verbose])

printrpcstats

halt rpcutil.stats
end
end
2 changes: 2 additions & 0 deletions plugins/mcollective/application/find.rb
Expand Up @@ -10,5 +10,7 @@ def main
end

client.display_stats(stats) if options[:verbose]

halt stats
end
end
4 changes: 2 additions & 2 deletions plugins/mcollective/application/inventory.rb
@@ -1,5 +1,3 @@
require 'pp'

class MCollective::Application::Inventory<MCollective::Application
description "General reporting tool for nodes, collectives and subcollectives"

Expand Down Expand Up @@ -173,6 +171,8 @@ def node_inventory
end
end
end

halt nodestats
end

# Helpers to create a simple DSL for scriptlets
Expand Down
2 changes: 2 additions & 0 deletions plugins/mcollective/application/ping.rb
Expand Up @@ -71,6 +71,8 @@ def main
else
puts("No responses received")
end

halt client.stats
end
end
end
2 changes: 2 additions & 0 deletions plugins/mcollective/application/rpc.rb
Expand Up @@ -129,6 +129,8 @@ def main
printrpc mc.send(configuration[:action], configuration[:arguments])

printrpcstats :caption => "#{configuration[:agent]}##{configuration[:action]} call stats"

halt mc.stats
end
end
end
78 changes: 78 additions & 0 deletions spec/unit/application_spec.rb
Expand Up @@ -353,6 +353,84 @@ module MCollective
end
end

describe "#halt" do
before do
@stats = {:discoverytime => 0, :discovered => 0, :failcount => 0, :responses => 0}
end

it "should exit with code 0 if discovery was done and all responses passed" do
app = Application.new

@stats[:discoverytime] = 2
@stats[:discovered] = 2
@stats[:responses] = 2

app.expects(:exit).with(0)

app.halt(@stats)
end

it "should exit with code 0 if no discovery were done but responses were received" do
app = Application.new

@stats[:responses] = 1

app.expects(:exit).with(0)

app.halt(@stats)
end

it "should exit with code 0 if discovery info is missing" do
app = Application.new

app.expects(:exit).with(0)

app.halt({})
end

it "should exit with code 1 if no nodes were discovered and discovery was done" do
app = Application.new

@stats[:discoverytime] = 2

app.expects(:exit).with(1)

app.halt(@stats)
end

it "should exit with code 2 if a request failed for some nodes" do
app = Application.new

@stats[:discovered] = 1
@stats[:failcount] = 1
@stats[:discoverytime] = 2
@stats[:responses] = 1

app.expects(:exit).with(2)

app.halt(@stats)
end

it "should exit with code 3 if no responses were received after discovery" do
app = Application.new

@stats[:discovered] = 1
@stats[:discoverytime] = 2

app.expects(:exit).with(3)

app.halt(@stats)
end

it "should exit with code 4 if no discovery was done and no responses were received" do
app = Application.new

app.expects(:exit).with(4)

app.halt(@stats)
end
end

describe "#disconnect" do
it "should disconnect from the connector plugin" do
connector = mock
Expand Down
1 change: 1 addition & 0 deletions website/changelog.md
Expand Up @@ -11,6 +11,7 @@ title: Changelog

|Date|Description|Ticket|
|----|-----------|------|
|2012/01/09|Add a halt method to the Application framework and standardize exit codes|11280|
|2011/11/21|Remove unintended dependency on _pp_ in the ActiveMQ plugin|10992|
|2011/11/17|Allow reply to destinations to be supplied on the command line or API|9847|
|*2011/11/17*|*Release 1.3.2*|*10830*|
Expand Down
38 changes: 35 additions & 3 deletions website/reference/plugins/application.md
Expand Up @@ -90,8 +90,6 @@ class MCollective::Application::Echo<MCollective::Application
printrpc mc.echo(:msg => configuration[:message], :options => options)

printrpcstats

mc.disconnect
end
end
{% endhighlight %}
Expand Down Expand Up @@ -222,7 +220,7 @@ class MCollective::Application::Echo<MCollective::Application
configuration[:message] = ARGV.shift
else
STDERR.puts "Please specify a message on the command line"
exit! 1
exit 1
end
end

Expand Down Expand Up @@ -255,4 +253,38 @@ class MCollective::Application::Echo<MCollective::Application
end
{% endhighlight %}

### Exiting your application
You can use the normal _exit_ Ruby method at any time to exit your application and you can supply any
exit code as normal.

As of version 1.3.3 the supplied applications have a standard exit code convention, if you want
your applications to exhibit the same behavior use the _halt_ helper. The exit codes are below:

|Code|Description |
|----|-----------------------------------------------------|
|0 |Nodes were discovered and all passed |
|0 |No discovery was done but responses were received |
|1 |No nodes were discovered |
|2 |Nodes were discovered but some responses failed |
|3 |Nodes were discovered but no responses were received |
|4 |No discovery were done and no responses were received|

{% highlight ruby %}
class MCollective::Application::Echo<MCollective::Application
description "Reports on usage for a specific fact"

def main
mc = rpcclient("echo")

printrpc mc.echo(:msg => "Hello World", :options => options)

printrpcstats

halt_ mc.stats
end
end
{% endhighlight %}

As you can see you pass the _halt_ helper an instance of the RPC Client statistics and it will then
use that to do the right exit code.

0 comments on commit ee4b20e

Please sign in to comment.