mcollective-client 1.3.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of mcollective-client might be problematic. Click here for more details.

Files changed (103) hide show
  1. data/bin/mc-call-agent +54 -0
  2. data/bin/mco +27 -0
  3. data/lib/mcollective.rb +70 -0
  4. data/lib/mcollective/agents.rb +160 -0
  5. data/lib/mcollective/application.rb +354 -0
  6. data/lib/mcollective/applications.rb +145 -0
  7. data/lib/mcollective/client.rb +292 -0
  8. data/lib/mcollective/config.rb +202 -0
  9. data/lib/mcollective/connector.rb +18 -0
  10. data/lib/mcollective/connector/base.rb +24 -0
  11. data/lib/mcollective/facts.rb +39 -0
  12. data/lib/mcollective/facts/base.rb +86 -0
  13. data/lib/mcollective/log.rb +103 -0
  14. data/lib/mcollective/logger.rb +5 -0
  15. data/lib/mcollective/logger/base.rb +73 -0
  16. data/lib/mcollective/logger/console_logger.rb +61 -0
  17. data/lib/mcollective/logger/file_logger.rb +46 -0
  18. data/lib/mcollective/logger/syslog_logger.rb +53 -0
  19. data/lib/mcollective/matcher.rb +16 -0
  20. data/lib/mcollective/matcher/parser.rb +93 -0
  21. data/lib/mcollective/matcher/scanner.rb +123 -0
  22. data/lib/mcollective/message.rb +201 -0
  23. data/lib/mcollective/monkey_patches.rb +104 -0
  24. data/lib/mcollective/optionparser.rb +164 -0
  25. data/lib/mcollective/pluginmanager.rb +180 -0
  26. data/lib/mcollective/pluginpackager.rb +26 -0
  27. data/lib/mcollective/pluginpackager/agent_definition.rb +79 -0
  28. data/lib/mcollective/pluginpackager/standard_definition.rb +59 -0
  29. data/lib/mcollective/registration.rb +16 -0
  30. data/lib/mcollective/registration/base.rb +75 -0
  31. data/lib/mcollective/rpc.rb +188 -0
  32. data/lib/mcollective/rpc/actionrunner.rb +142 -0
  33. data/lib/mcollective/rpc/agent.rb +441 -0
  34. data/lib/mcollective/rpc/audit.rb +38 -0
  35. data/lib/mcollective/rpc/client.rb +793 -0
  36. data/lib/mcollective/rpc/ddl.rb +258 -0
  37. data/lib/mcollective/rpc/helpers.rb +339 -0
  38. data/lib/mcollective/rpc/progress.rb +63 -0
  39. data/lib/mcollective/rpc/reply.rb +61 -0
  40. data/lib/mcollective/rpc/request.rb +51 -0
  41. data/lib/mcollective/rpc/result.rb +41 -0
  42. data/lib/mcollective/rpc/stats.rb +185 -0
  43. data/lib/mcollective/runnerstats.rb +90 -0
  44. data/lib/mcollective/security.rb +26 -0
  45. data/lib/mcollective/security/base.rb +237 -0
  46. data/lib/mcollective/shell.rb +87 -0
  47. data/lib/mcollective/ssl.rb +246 -0
  48. data/lib/mcollective/unix_daemon.rb +37 -0
  49. data/lib/mcollective/util.rb +274 -0
  50. data/lib/mcollective/vendor.rb +41 -0
  51. data/lib/mcollective/vendor/require_vendored.rb +2 -0
  52. data/lib/mcollective/windows_daemon.rb +25 -0
  53. data/spec/Rakefile +16 -0
  54. data/spec/fixtures/application/test.rb +7 -0
  55. data/spec/fixtures/test-cert.pem +15 -0
  56. data/spec/fixtures/test-private.pem +15 -0
  57. data/spec/fixtures/test-public.pem +6 -0
  58. data/spec/monkey_patches/instance_variable_defined.rb +7 -0
  59. data/spec/spec.opts +1 -0
  60. data/spec/spec_helper.rb +25 -0
  61. data/spec/unit/agents_spec.rb +280 -0
  62. data/spec/unit/application_spec.rb +636 -0
  63. data/spec/unit/applications_spec.rb +155 -0
  64. data/spec/unit/array.rb +30 -0
  65. data/spec/unit/config_spec.rb +148 -0
  66. data/spec/unit/facts/base_spec.rb +118 -0
  67. data/spec/unit/facts_spec.rb +39 -0
  68. data/spec/unit/log_spec.rb +71 -0
  69. data/spec/unit/logger/base_spec.rb +110 -0
  70. data/spec/unit/logger/syslog_logger_spec.rb +86 -0
  71. data/spec/unit/matcher/parser_spec.rb +106 -0
  72. data/spec/unit/matcher/scanner_spec.rb +71 -0
  73. data/spec/unit/message_spec.rb +401 -0
  74. data/spec/unit/optionparser_spec.rb +113 -0
  75. data/spec/unit/pluginmanager_spec.rb +173 -0
  76. data/spec/unit/pluginpackager/agent_definition_spec.rb +130 -0
  77. data/spec/unit/pluginpackager/standard_definition_spec.rb +75 -0
  78. data/spec/unit/plugins/mcollective/connector/activemq_spec.rb +533 -0
  79. data/spec/unit/plugins/mcollective/connector/stomp/eventlogger_spec.rb +34 -0
  80. data/spec/unit/plugins/mcollective/connector/stomp_spec.rb +417 -0
  81. data/spec/unit/plugins/mcollective/packagers/ospackage_spec.rb +229 -0
  82. data/spec/unit/plugins/mcollective/security/psk_spec.rb +156 -0
  83. data/spec/unit/registration/base_spec.rb +77 -0
  84. data/spec/unit/rpc/actionrunner_spec.rb +213 -0
  85. data/spec/unit/rpc/agent_spec.rb +155 -0
  86. data/spec/unit/rpc/client_spec.rb +523 -0
  87. data/spec/unit/rpc/ddl_spec.rb +388 -0
  88. data/spec/unit/rpc/helpers_spec.rb +55 -0
  89. data/spec/unit/rpc/reply_spec.rb +143 -0
  90. data/spec/unit/rpc/request_spec.rb +115 -0
  91. data/spec/unit/rpc/result_spec.rb +66 -0
  92. data/spec/unit/rpc/stats_spec.rb +288 -0
  93. data/spec/unit/runnerstats_spec.rb +40 -0
  94. data/spec/unit/security/base_spec.rb +279 -0
  95. data/spec/unit/shell_spec.rb +144 -0
  96. data/spec/unit/ssl_spec.rb +244 -0
  97. data/spec/unit/symbol.rb +11 -0
  98. data/spec/unit/unix_daemon.rb +41 -0
  99. data/spec/unit/util_spec.rb +342 -0
  100. data/spec/unit/vendor_spec.rb +34 -0
  101. data/spec/unit/windows_daemon.rb +43 -0
  102. data/spec/windows_spec.opts +1 -0
  103. metadata +242 -0
@@ -0,0 +1,7 @@
1
+ unless Object.respond_to?("instance_variable_defined?")
2
+ class Object
3
+ def instance_variable_defined?(meth)
4
+ instance_variables.include?(meth)
5
+ end
6
+ end
7
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --format s --colour --backtrace
@@ -0,0 +1,25 @@
1
+ dir = File.expand_path(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift("#{dir}/")
3
+ $LOAD_PATH.unshift("#{dir}/../lib")
4
+
5
+ require 'rubygems'
6
+
7
+ gem 'mocha'
8
+
9
+ require 'rspec'
10
+ require 'mcollective'
11
+ require 'rspec/mocks'
12
+ require 'mocha'
13
+ require 'ostruct'
14
+ require 'tmpdir'
15
+ require 'tempfile'
16
+
17
+ require 'monkey_patches/instance_variable_defined'
18
+
19
+ RSpec.configure do |config|
20
+ config.mock_with :mocha
21
+
22
+ config.before :each do
23
+ MCollective::PluginManager.clear
24
+ end
25
+ end
@@ -0,0 +1,280 @@
1
+ #!/usr/bin/env rspec
2
+
3
+ require 'spec_helper'
4
+
5
+ module MCollective
6
+ describe Agents do
7
+ before do
8
+ tmpfile = Tempfile.new("mc_agents_spec")
9
+ path = tmpfile.path
10
+ tmpfile.close!
11
+
12
+ @tmpdir = FileUtils.mkdir_p(path)
13
+ @tmpdir = @tmpdir[0] if @tmpdir.is_a?(Array) # ruby 1.9.2
14
+
15
+ @agentsdir = File.join([@tmpdir, "mcollective", "agent"])
16
+ FileUtils.mkdir_p(@agentsdir)
17
+
18
+ logger = mock
19
+ logger.stubs(:log)
20
+ logger.stubs(:start)
21
+ Log.configure(logger)
22
+ end
23
+
24
+ after do
25
+ FileUtils.rm_r(@tmpdir)
26
+ end
27
+
28
+ describe "#initialize" do
29
+ it "should fail if configuration has not been loaded" do
30
+ Config.any_instance.expects(:configured).returns(false)
31
+
32
+ expect {
33
+ Agents.new
34
+ }.to raise_error("Configuration has not been loaded, can't load agents")
35
+ end
36
+
37
+ it "should load agents" do
38
+ Config.any_instance.expects(:configured).returns(true)
39
+ Agents.any_instance.expects(:loadagents).once
40
+
41
+ Agents.new
42
+ end
43
+ end
44
+
45
+ describe "#clear!" do
46
+ it "should delete and unsubscribe all loaded agents" do
47
+ Config.any_instance.expects(:configured).returns(true).at_least_once
48
+ Config.any_instance.expects(:libdir).returns([@tmpdir])
49
+ PluginManager.expects(:delete).with("foo_agent").once
50
+ Util.expects(:make_subscriptions).with("foo", :broadcast).returns("foo_target")
51
+ Util.expects(:unsubscribe).with("foo_target")
52
+
53
+ a = Agents.new({"foo" => 1})
54
+ end
55
+ end
56
+
57
+ describe "#loadagents" do
58
+ before do
59
+ Config.any_instance.stubs(:configured).returns(true)
60
+ Config.any_instance.stubs(:libdir).returns([@tmpdir])
61
+ Agents.any_instance.stubs("clear!").returns(true)
62
+ end
63
+
64
+ it "should delete all existing agents" do
65
+ Agents.any_instance.expects("clear!").once
66
+ a = Agents.new
67
+ end
68
+
69
+ it "should attempt to load agents from all libdirs" do
70
+ Config.any_instance.expects(:libdir).returns(["/nonexisting", "/nonexisting"])
71
+ File.expects("directory?").with("/nonexisting/mcollective/agent").twice
72
+
73
+ a = Agents.new
74
+ end
75
+
76
+ it "should load found agents" do
77
+ Agents.any_instance.expects("loadagent").with("test").once
78
+
79
+ FileUtils.touch(File.join([@agentsdir, "test.rb"]))
80
+
81
+ a = Agents.new
82
+ end
83
+
84
+ it "should load each agent unless already loaded" do
85
+ Agents.any_instance.expects("loadagent").with("test").never
86
+
87
+ FileUtils.touch(File.join([@agentsdir, "test.rb"]))
88
+
89
+ PluginManager << {:type => "test_agent", :class => String.new}
90
+ a = Agents.new
91
+ end
92
+ end
93
+
94
+ describe "#loadagent" do
95
+ before do
96
+ FileUtils.touch(File.join([@agentsdir, "test.rb"]))
97
+ Config.any_instance.stubs(:configured).returns(true)
98
+ Config.any_instance.stubs(:libdir).returns([@tmpdir])
99
+ Agents.any_instance.stubs("clear!").returns(true)
100
+ PluginManager.stubs(:loadclass).returns(true)
101
+ Util.stubs(:make_subscriptions).with("test", :broadcast).returns([{:agent => "test", :type => :broadcast, :collective => "test"}])
102
+ Util.stubs(:subscribe).with([{:agent => "test", :type => :broadcast, :collective => "test"}]).returns(true)
103
+ Agents.stubs(:findagentfile).returns(File.join([@agentsdir, "test.rb"]))
104
+ Agents.any_instance.stubs("activate_agent?").returns(true)
105
+
106
+ @a = Agents.new
107
+ end
108
+
109
+ it "should return false if the agent file is missing" do
110
+ Agents.any_instance.expects(:findagentfile).returns(false).once
111
+ @a.loadagent("test").should == false
112
+ end
113
+
114
+ it "should delete the agent before loading again" do
115
+ PluginManager.expects(:delete).with("test_agent").twice
116
+ @a.loadagent("test")
117
+ end
118
+
119
+ it "should load the agent class from disk" do
120
+ PluginManager.expects(:loadclass).with("MCollective::Agent::Test")
121
+ @a.loadagent("test")
122
+ end
123
+
124
+ it "should check if the agent should be activated" do
125
+ Agents.any_instance.expects(:findagentfile).with("test").returns(File.join([@agentsdir, "test.rb"]))
126
+ Agents.any_instance.expects("activate_agent?").with("test").returns(true)
127
+ @a.loadagent("test").should == true
128
+ end
129
+
130
+ it "should set discovery and registration to be single instance plugins" do
131
+ PluginManager.expects("<<").with({:type => "registration_agent", :class => "MCollective::Agent::Registration", :single_instance => true}).once
132
+ PluginManager.expects("<<").with({:type => "discovery_agent", :class => "MCollective::Agent::Discovery", :single_instance => true}).once
133
+ Agents.any_instance.expects("activate_agent?").with("registration").returns(true)
134
+ Agents.any_instance.expects("activate_agent?").with("discovery").returns(true)
135
+
136
+ PluginManager.expects(:loadclass).with("MCollective::Agent::Registration").returns(true).once
137
+ PluginManager.expects(:loadclass).with("MCollective::Agent::Discovery").returns(true).once
138
+
139
+ FileUtils.touch(File.join([@agentsdir, "registration.rb"]))
140
+ FileUtils.touch(File.join([@agentsdir, "discovery.rb"]))
141
+
142
+ @a.loadagent("registration")
143
+ @a.loadagent("discovery")
144
+ end
145
+
146
+ it "should add general plugins as multiple instance plugins" do
147
+ PluginManager.expects("<<").with({:type => "test_agent", :class => "MCollective::Agent::Test", :single_instance => false}).once
148
+ @a.loadagent("test")
149
+ end
150
+
151
+ it "should add the agent to the plugin manager and subscribe" do
152
+ PluginManager.expects("<<").with({:type => "foo_agent", :class => "MCollective::Agent::Foo", :single_instance => false})
153
+ Util.stubs(:make_subscriptions).with("foo", :broadcast).returns([{:agent => "foo", :type => :broadcast, :collective => "test"}])
154
+ Util.expects("subscribe").with([{:type => :broadcast, :agent => 'foo', :collective => 'test'}]).returns(true)
155
+ Agents.any_instance.expects(:findagentfile).with("foo").returns(File.join([@agentsdir, "foo.rb"]))
156
+
157
+ FileUtils.touch(File.join([@agentsdir, "foo.rb"]))
158
+
159
+ @a.loadagent("foo")
160
+ end
161
+
162
+ it "should add the agent to the agent list" do
163
+ Agents.agentlist.should == ["test"]
164
+ end
165
+
166
+ it "should return true on success" do
167
+ @a.loadagent("test").should == true
168
+ end
169
+
170
+ it "should handle load exceptions" do
171
+ Agents.any_instance.expects(:findagentfile).with("foo").returns(File.join([@agentsdir, "foo.rb"]))
172
+ Log.expects(:error).with(regexp_matches(/Loading agent foo failed/))
173
+ @a.loadagent("foo").should == false
174
+ end
175
+
176
+ it "should delete plugins that failed to load" do
177
+ Agents.any_instance.expects(:findagentfile).with("foo").returns(File.join([@agentsdir, "foo.rb"]))
178
+ PluginManager.expects(:delete).with("foo_agent").twice
179
+
180
+ @a.loadagent("foo").should == false
181
+ end
182
+ end
183
+
184
+ describe "#class_for_agent" do
185
+ it "should return the correct class" do
186
+ Config.any_instance.stubs(:configured).returns(true)
187
+ Agents.any_instance.stubs(:loadagents).returns(true)
188
+ Agents.new.class_for_agent("foo").should == "MCollective::Agent::Foo"
189
+ end
190
+ end
191
+
192
+ describe "#activate_agent?" do
193
+ before do
194
+ Config.any_instance.stubs(:configured).returns(true)
195
+ Agents.any_instance.stubs(:loadagents).returns(true)
196
+ @a = Agents.new
197
+
198
+ module MCollective::Agent;end
199
+ class MCollective::Agent::Test; end
200
+ end
201
+
202
+ it "should check if the correct class has an activation method" do
203
+ Agent::Test.expects("respond_to?").with("activate?").once
204
+
205
+ @a.activate_agent?("test")
206
+ end
207
+
208
+ it "should call the activation method" do
209
+ Agent::Test.expects("activate?").returns(true).once
210
+ @a.activate_agent?("test")
211
+ end
212
+
213
+ it "should log a debug message and return true if the class has no activation method" do
214
+ Agent::Test.expects("respond_to?").with("activate?").returns(false).once
215
+ Log.expects(:debug).with("MCollective::Agent::Test does not have an activate? method, activating as default")
216
+
217
+ @a.activate_agent?("test").should == true
218
+ end
219
+
220
+ it "should handle exceptions in the activation as false" do
221
+ Agent::Test.expects("activate?").raises(RuntimeError)
222
+ @a.activate_agent?("test").should == false
223
+ end
224
+ end
225
+
226
+ describe "#findagentfile" do
227
+ before do
228
+ Config.any_instance.stubs(:configured).returns(true)
229
+ Config.any_instance.stubs(:libdir).returns([@tmpdir])
230
+ Agents.any_instance.stubs(:loadagents).returns(true)
231
+ @a = Agents.new
232
+ end
233
+
234
+ it "should support multiple libdirs" do
235
+ Config.any_instance.expects(:libdir).returns([@tmpdir, @tmpdir]).once
236
+ File.expects("exist?").returns(false).twice
237
+ @a.findagentfile("test")
238
+ end
239
+
240
+ it "should look for the correct filename in the libdir" do
241
+ File.expects("exist?").with(File.join([@tmpdir, "mcollective", "agent", "test.rb"])).returns(false).once
242
+ @a.findagentfile("test")
243
+ end
244
+
245
+ it "should return the full path if the agent is found" do
246
+ agentfile = File.join([@tmpdir, "mcollective", "agent", "test.rb"])
247
+ File.expects("exist?").with(agentfile).returns(true).once
248
+ @a.findagentfile("test").should == agentfile
249
+ end
250
+
251
+ it "should return false if no agent is found" do
252
+ @a.findagentfile("foo").should == false
253
+ end
254
+ end
255
+
256
+ describe "#include?" do
257
+ it "should correctly report the plugin state" do
258
+ Config.any_instance.stubs(:configured).returns(true)
259
+ Config.any_instance.stubs(:libdir).returns([@tmpdir])
260
+ Agents.any_instance.stubs(:loadagents).returns(true)
261
+ PluginManager.expects("include?").with("test_agent").returns(true)
262
+
263
+ @a = Agents.new
264
+
265
+ @a.include?("test").should == true
266
+ end
267
+ end
268
+
269
+ describe "#agentlist" do
270
+ it "should return the correct agent list" do
271
+ Config.any_instance.stubs(:configured).returns(true)
272
+ Config.any_instance.stubs(:libdir).returns([@tmpdir])
273
+ Agents.any_instance.stubs(:loadagents).returns(true)
274
+
275
+ @a = Agents.new("test" => true)
276
+ Agents.agentlist.should == ["test"]
277
+ end
278
+ end
279
+ end
280
+ end
@@ -0,0 +1,636 @@
1
+ #!/usr/bin/env rspec
2
+
3
+ require 'spec_helper'
4
+
5
+ module MCollective
6
+ describe Application do
7
+ before do
8
+ Application.intialize_application_options
9
+ @argv_backup = ARGV.clone
10
+ end
11
+
12
+ describe "#application_options" do
13
+ it "should return the application options" do
14
+ Application.application_options.should == {:description => nil,
15
+ :usage => [],
16
+ :cli_arguments => [],
17
+ :exclude_arg_sections => []}
18
+ end
19
+ end
20
+
21
+ describe "#[]=" do
22
+ it "should set the application option" do
23
+ Application["foo"] = "bar"
24
+ Application.application_options["foo"].should == "bar"
25
+ end
26
+ end
27
+
28
+ describe "#[]" do
29
+ it "should set the application option" do
30
+ Application[:cli_arguments].should == []
31
+ end
32
+ end
33
+
34
+ describe "#intialize_application_options" do
35
+ it "should initialize application options correctly" do
36
+ Application.intialize_application_options.should == {:description => nil,
37
+ :usage => [],
38
+ :cli_arguments => [],
39
+ :exclude_arg_sections => []}
40
+ end
41
+ end
42
+
43
+ describe "#description" do
44
+ it "should set the description correctly" do
45
+ Application.description "meh"
46
+ Application[:description].should == "meh"
47
+ end
48
+ end
49
+
50
+ describe "#usage" do
51
+ it "should set the usage correctly" do
52
+ Application.usage "meh"
53
+ Application.usage "foo"
54
+
55
+ Application[:usage].should == ["meh", "foo"]
56
+ end
57
+ end
58
+
59
+ describe "#exclude_argument_sections" do
60
+ it "should set the excluded sections correctly" do
61
+ Application.exclude_argument_sections "common", "rpc", "filter"
62
+ Application[:exclude_arg_sections].should == ["common", "rpc", "filter"]
63
+ Application.exclude_argument_sections ["common", "rpc", "filter"]
64
+ Application[:exclude_arg_sections].should == ["common", "rpc", "filter"]
65
+ end
66
+
67
+ it "should detect unknown sections" do
68
+ expect { Application.exclude_argument_sections "rspec" }.to raise_error("Unknown CLI argument section rspec")
69
+ end
70
+ end
71
+
72
+ describe "#option" do
73
+ it "should add an option correctly" do
74
+ Application.option :test,
75
+ :description => "description",
76
+ :arguments => "--config CONFIG",
77
+ :type => Integer,
78
+ :required => true
79
+
80
+ args = Application[:cli_arguments].first
81
+ args.delete(:validate)
82
+
83
+ args.should == {:name=>:test,
84
+ :arguments=>"--config CONFIG",
85
+ :required=>true,
86
+ :type=>Integer,
87
+ :description=>"description"}
88
+ end
89
+
90
+ it "should set correct defaults" do
91
+ Application.option :test, {}
92
+
93
+ args = Application[:cli_arguments].first
94
+ args.delete(:validate)
95
+
96
+ args.should == {:name=>:test,
97
+ :arguments=>[],
98
+ :required=>false,
99
+ :type=>String,
100
+ :description=>nil}
101
+ end
102
+ end
103
+
104
+ describe "#validate_option" do
105
+ it "should pass validations" do
106
+ a = Application.new
107
+ a.validate_option(Proc.new {|v| v == 1}, "key", 1)
108
+ end
109
+
110
+ it "should print an error to STDERR on error" do
111
+ IO.any_instance.expects(:puts).with("Validation of key failed: failed").at_least_once
112
+ Application.any_instance.stubs("exit").returns(true)
113
+
114
+ a = Application.new
115
+ a.validate_option(Proc.new {|v| "failed"}, "key", 1)
116
+ end
117
+
118
+ it "should exit on valdation error" do
119
+ IO.any_instance.expects(:puts).at_least_once
120
+ Application.any_instance.stubs("exit").returns(true)
121
+
122
+ a = Application.new
123
+ a.validate_option(Proc.new {|v| "failed"}, "key", 1)
124
+ end
125
+ end
126
+
127
+ describe "#application_parse_options" do
128
+ it "should pass the requested help value to the clioptions method" do
129
+ ARGV.clear
130
+
131
+ app = Application.new
132
+ app.expects(:clioptions).with(true)
133
+ app.application_parse_options(true)
134
+
135
+ ARGV.clear
136
+ @argv_backup.each{|a| ARGV << a}
137
+ end
138
+
139
+ it "should support creating arrays of values" do
140
+ Application.any_instance.stubs("main").returns(true)
141
+
142
+ Application.option :foo,
143
+ :description => "meh",
144
+ :arguments => "--foo [FOO]",
145
+ :type => :array
146
+
147
+ ARGV.clear
148
+ ARGV << "--foo=bar" << "--foo=baz"
149
+
150
+ a = Application.new
151
+ a.run
152
+ a.configuration.should == {:foo=>["bar", "baz"]}
153
+
154
+ ARGV.clear
155
+ @argv_backup.each{|a| ARGV << a}
156
+ end
157
+
158
+ it "should support boolean options" do
159
+ Application.any_instance.stubs("main").returns(true)
160
+
161
+ Application.option :foo,
162
+ :description => "meh",
163
+ :arguments => "--foo",
164
+ :type => :boolean
165
+
166
+ ARGV.clear
167
+ ARGV << "--foo"
168
+
169
+ a = Application.new
170
+ a.run
171
+ a.configuration.should == {:foo=>true}
172
+
173
+ ARGV.clear
174
+ @argv_backup.each{|a| ARGV << a}
175
+ end
176
+
177
+ it "should set the application description as head" do
178
+ OptionParser.any_instance.stubs(:define_head).with("meh")
179
+
180
+ ARGV.clear
181
+
182
+ Application.description "meh"
183
+ Application.new.application_parse_options
184
+
185
+ ARGV.clear
186
+ @argv_backup.each{|a| ARGV << a}
187
+ end
188
+
189
+ it "should set the application usage as a banner" do
190
+ OptionParser.any_instance.stubs(:banner).with("meh")
191
+
192
+ ARGV.clear
193
+
194
+ Application.usage "meh"
195
+ Application.new.application_parse_options
196
+
197
+ ARGV.clear
198
+ @argv_backup.each{|a| ARGV << a}
199
+ end
200
+
201
+ it "should support validation" do
202
+ IO.any_instance.expects(:puts).with("Validation of foo failed: failed").at_least_once
203
+ Application.any_instance.stubs("exit").returns(true)
204
+ Application.any_instance.stubs("main").returns(true)
205
+
206
+ Application.option :foo,
207
+ :description => "meh",
208
+ :required => true,
209
+ :default => "meh",
210
+ :arguments => "--foo [FOO]",
211
+ :validate => Proc.new {|v| "failed"}
212
+
213
+ ARGV.clear
214
+ ARGV << "--foo=bar"
215
+
216
+ a = Application.new
217
+ a.run
218
+
219
+ ARGV.clear
220
+ @argv_backup.each{|a| ARGV << a}
221
+ end
222
+
223
+ it "should support default values" do
224
+ Application.any_instance.stubs("main").returns(true)
225
+
226
+ Application.option :foo,
227
+ :description => "meh",
228
+ :required => true,
229
+ :default => "meh",
230
+ :arguments => "--foo [FOO]"
231
+
232
+ a = Application.new
233
+ a.run
234
+ a.configuration.should == {:foo => "meh"}
235
+ end
236
+
237
+ it "should enforce required options" do
238
+ Application.any_instance.stubs("exit").returns(true)
239
+ Application.any_instance.stubs("main").returns(true)
240
+ OptionParser.any_instance.stubs("parse!").returns(true)
241
+ IO.any_instance.expects(:puts).with(anything).at_least_once
242
+ IO.any_instance.expects(:puts).with("The foo option is mandatory").at_least_once
243
+
244
+ ARGV.clear
245
+ ARGV << "--foo=bar"
246
+
247
+ Application.option :foo,
248
+ :description => "meh",
249
+ :required => true,
250
+ :arguments => "--foo [FOO]"
251
+
252
+ Application.new.run
253
+
254
+ ARGV.clear
255
+ @argv_backup.each{|a| ARGV << a}
256
+ end
257
+
258
+ it "should call post_option_parser" do
259
+ OptionParser.any_instance.stubs("parse!").returns(true)
260
+ Application.any_instance.stubs("post_option_parser").returns(true).at_least_once
261
+ Application.any_instance.stubs("main").returns(true)
262
+
263
+ ARGV.clear
264
+ ARGV << "--foo=bar"
265
+
266
+ Application.option :foo,
267
+ :description => "meh",
268
+ :arguments => "--foo [FOO]"
269
+
270
+ Application.new.run
271
+
272
+ ARGV.clear
273
+ @argv_backup.each{|a| ARGV << a}
274
+ end
275
+
276
+ it "should create an application option" do
277
+ OptionParser.any_instance.stubs("parse!").returns(true)
278
+ OptionParser.any_instance.expects(:on).with(anything, anything, anything, anything).at_least_once
279
+ OptionParser.any_instance.expects(:on).with('--foo [FOO]', String, 'meh').at_least_once
280
+ Application.any_instance.stubs("main").returns(true)
281
+
282
+ ARGV.clear
283
+ ARGV << "--foo=bar"
284
+
285
+ Application.option :foo,
286
+ :description => "meh",
287
+ :arguments => "--foo [FOO]"
288
+
289
+ Application.new.run
290
+
291
+ ARGV.clear
292
+ @argv_backup.each{|a| ARGV << a}
293
+ end
294
+ end
295
+
296
+ describe "#initialize" do
297
+ it "should parse the command line options at application run" do
298
+ Application.any_instance.expects("application_parse_options").once
299
+ Application.any_instance.stubs("main").returns(true)
300
+
301
+ Application.new.run
302
+ end
303
+ end
304
+
305
+ describe "#application_options" do
306
+ it "sshould return the application options" do
307
+ Application.new.application_options.should == Application.application_options
308
+ end
309
+ end
310
+
311
+ describe "#application_description" do
312
+ it "should provide the right description" do
313
+ Application.description "Foo"
314
+ Application.new.application_description.should == "Foo"
315
+ end
316
+ end
317
+
318
+ describe "#application_usage" do
319
+ it "should provide the right usage" do
320
+ Application.usage "Foo"
321
+ Application.new.application_usage.should == ["Foo"]
322
+ end
323
+ end
324
+
325
+ describe "#application_cli_arguments" do
326
+ it "should provide the right usage" do
327
+ Application.option :foo,
328
+ :description => "meh",
329
+ :arguments => "--foo [FOO]"
330
+
331
+ args = Application.new.application_cli_arguments.first
332
+
333
+ # need to remove this cos we cant validate procs for equality afaik
334
+ args.delete(:validate)
335
+
336
+ args.should == {:description=>"meh",
337
+ :name=>:foo,
338
+ :arguments=>"--foo [FOO]",
339
+ :type=>String,
340
+ :required=>false}
341
+ end
342
+ end
343
+
344
+ describe "#help" do
345
+ it "should generate help using the full user supplied options" do
346
+ app = Application.new
347
+ app.expects(:clioptions).with(true).once
348
+ app.help
349
+ end
350
+ end
351
+
352
+ describe "#main" do
353
+ it "should detect applications without a #main" do
354
+ IO.any_instance.expects(:puts).with("Applications need to supply a 'main' method")
355
+
356
+ expect {
357
+ Application.new.run
358
+ }.to raise_error(SystemExit)
359
+ end
360
+
361
+ it "should raise SystemExit exceptions for exit events" do
362
+ connector = mock
363
+ connector.expects(:disconnect)
364
+ PluginManager.expects("[]").with("connector_plugin").returns(connector)
365
+
366
+ a = Application.new
367
+ a.expects(:main).raises(SystemExit)
368
+
369
+ expect {
370
+ a.run
371
+ }.to raise_error(SystemExit)
372
+ end
373
+ end
374
+
375
+ describe "#configuration" do
376
+ it "should return the correct configuration" do
377
+ Application.any_instance.stubs("main").returns(true)
378
+
379
+ ARGV.clear
380
+ ARGV << "--foo=bar"
381
+
382
+ Application.option :foo,
383
+ :description => "meh",
384
+ :arguments => "--foo [FOO]"
385
+
386
+ a = Application.new
387
+ a.run
388
+
389
+ a.configuration.should == {:foo => "bar"}
390
+
391
+ ARGV.clear
392
+ @argv_backup.each{|a| ARGV << a}
393
+ end
394
+ end
395
+
396
+ describe "#halt" do
397
+ before do
398
+ @stats = {:discoverytime => 0, :discovered => 0, :failcount => 0, :responses => 0}
399
+ end
400
+
401
+ it "should exit with code 0 if discovery was done and all responses passed" do
402
+ app = Application.new
403
+
404
+ @stats[:discoverytime] = 2
405
+ @stats[:discovered] = 2
406
+ @stats[:responses] = 2
407
+
408
+ app.expects(:exit).with(0)
409
+
410
+ app.halt(@stats)
411
+ end
412
+
413
+ it "should exit with code 0 if no discovery were done but responses were received" do
414
+ app = Application.new
415
+
416
+ @stats[:responses] = 1
417
+
418
+ app.expects(:exit).with(0)
419
+
420
+ app.halt(@stats)
421
+ end
422
+
423
+ it "should exit with code 0 if discovery info is missing" do
424
+ app = Application.new
425
+
426
+ app.expects(:exit).with(0)
427
+
428
+ app.halt({})
429
+ end
430
+
431
+ it "should exit with code 1 if no nodes were discovered and discovery was done" do
432
+ app = Application.new
433
+
434
+ @stats[:discoverytime] = 2
435
+
436
+ app.expects(:exit).with(1)
437
+
438
+ app.halt(@stats)
439
+ end
440
+
441
+ it "should exit with code 2 if a request failed for some nodes" do
442
+ app = Application.new
443
+
444
+ @stats[:discovered] = 1
445
+ @stats[:failcount] = 1
446
+ @stats[:discoverytime] = 2
447
+ @stats[:responses] = 1
448
+
449
+ app.expects(:exit).with(2)
450
+
451
+ app.halt(@stats)
452
+ end
453
+
454
+ it "should exit with code 3 if no responses were received after discovery" do
455
+ app = Application.new
456
+
457
+ @stats[:discovered] = 1
458
+ @stats[:discoverytime] = 2
459
+
460
+ app.expects(:exit).with(3)
461
+
462
+ app.halt(@stats)
463
+ end
464
+
465
+ it "should exit with code 4 if no discovery was done and no responses were received" do
466
+ app = Application.new
467
+
468
+ app.expects(:exit).with(4)
469
+
470
+ app.halt(@stats)
471
+ end
472
+ end
473
+
474
+ describe "#disconnect" do
475
+ it "should disconnect from the connector plugin" do
476
+ connector = mock
477
+ connector.expects(:disconnect)
478
+ PluginManager.expects("[]").with("connector_plugin").returns(connector)
479
+
480
+ Application.new.disconnect
481
+ end
482
+ end
483
+
484
+ describe "#clioptions" do
485
+ it "should pass the excluded argument section" do
486
+ oparser = mock
487
+ oparser.stubs(:parse)
488
+
489
+ Application.exclude_argument_sections "rpc"
490
+
491
+ Optionparser.expects(:new).with({:verbose => false, :progress_bar => true}, "filter", ["rpc"]).returns(oparser)
492
+
493
+ Application.new.clioptions(false)
494
+ end
495
+
496
+ it "should add the RPC options" do
497
+ oparser = mock
498
+ oparser.stubs(:parse).yields(oparser, {})
499
+ oparser.stubs(:banner=)
500
+ oparser.stubs(:define_tail)
501
+
502
+ Optionparser.stubs(:new).with({:verbose => false, :progress_bar => true}, "filter", []).returns(oparser)
503
+ RPC::Helpers.expects(:add_simplerpc_options).with(oparser, {})
504
+
505
+ Application.new.clioptions(false)
506
+ end
507
+
508
+ it "should support bypassing the RPC options" do
509
+ oparser = mock
510
+ oparser.stubs(:parse).yields(oparser, {})
511
+ oparser.stubs(:banner=)
512
+ oparser.stubs(:define_tail)
513
+
514
+ Application.exclude_argument_sections "rpc"
515
+
516
+ Optionparser.stubs(:new).with({:verbose => false, :progress_bar => true}, "filter", ["rpc"]).returns(oparser)
517
+ RPC::Helpers.expects(:add_simplerpc_options).never
518
+
519
+ Application.new.clioptions(false)
520
+ end
521
+
522
+ it "should return the help text if requested" do
523
+ parser = mock
524
+ parser.expects(:help)
525
+
526
+ oparser = mock
527
+ oparser.stubs(:parse).yields(oparser, {})
528
+ oparser.stubs(:banner=)
529
+ oparser.stubs(:define_tail)
530
+ oparser.expects(:parser).returns(parser)
531
+
532
+ Optionparser.stubs(:new).with({:verbose => false, :progress_bar => true}, "filter", []).returns(oparser)
533
+ RPC::Helpers.expects(:add_simplerpc_options).with(oparser, {})
534
+
535
+ Application.new.clioptions(true)
536
+ end
537
+ end
538
+
539
+ describe "#application_failure" do
540
+ before do
541
+ @app = Application.new
542
+ end
543
+
544
+ it "on SystemExit it should disconnect and exit without backtraces or error messages" do
545
+ @app.expects(:disconnect)
546
+ expect { @app.application_failure(SystemExit.new) }.to raise_error(SystemExit)
547
+ end
548
+
549
+ it "should print a single line error message" do
550
+ out = StringIO.new
551
+ @app.stubs(:disconnect)
552
+ @app.stubs(:exit).with(1)
553
+
554
+ e = mock
555
+ e.stubs(:backtrace).returns([])
556
+ e.stubs(:to_s).returns("rspec")
557
+
558
+ out.expects(:puts).with("#{$0} failed to run: rspec (Mocha::Mock)")
559
+
560
+ @app.application_failure(e, out)
561
+ end
562
+
563
+ it "should print a backtrace if options are unset or verbose is enabled" do
564
+ out = StringIO.new
565
+ @app.stubs(:disconnect)
566
+ @app.stubs(:exit).with(1)
567
+
568
+ e = mock
569
+ e.stubs(:backtrace).returns(["rspec"])
570
+ e.stubs(:to_s).returns("rspec")
571
+
572
+ @app.expects(:options).returns({:verbose => true}).twice
573
+ out.expects(:puts).with("#{$0} failed to run: rspec (Mocha::Mock)")
574
+ out.expects(:puts).with("\tfrom rspec")
575
+
576
+ @app.application_failure(e, out)
577
+ end
578
+ end
579
+
580
+ describe "#run" do
581
+ before do
582
+ @app = Application.new
583
+ end
584
+
585
+ it "should parse the application options, run main and disconnect" do
586
+ @app.expects(:application_parse_options)
587
+ @app.expects(:main)
588
+ @app.expects(:disconnect)
589
+
590
+ @app.run
591
+ end
592
+
593
+ it "should allow the application plugin to validate configuration variables" do
594
+ @app.expects("respond_to?").with(:validate_configuration).returns(true)
595
+ @app.expects(:validate_configuration).once
596
+
597
+ @app.stubs(:application_parse_options)
598
+ @app.stubs(:main)
599
+ @app.stubs(:disconnect)
600
+
601
+ @app.run
602
+ end
603
+
604
+ it "should start the sleeper thread on windows" do
605
+ Util.expects("windows?").returns(true)
606
+ Util.expects(:setup_windows_sleeper).once
607
+
608
+ @app.stubs(:application_parse_options)
609
+ @app.stubs(:main)
610
+ @app.stubs(:disconnect)
611
+
612
+ @app.run
613
+ end
614
+
615
+ it "should catch handle exit() correctly" do
616
+ Application.send(:define_method, :main) do
617
+ exit 1
618
+ end
619
+
620
+ @app.expects(:disconnect).once
621
+
622
+ expect { @app.run }.to raise_error(SystemExit)
623
+ end
624
+
625
+ it "should catch all exceptions and process them correctly" do
626
+ @app.expects(:application_failure).once
627
+
628
+ Application.send(:define_method, :main) do
629
+ raise "rspec"
630
+ end
631
+
632
+ @app.run
633
+ end
634
+ end
635
+ end
636
+ end