mcollective-client 2.5.3 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/lib/mcollective.rb +1 -1
  2. data/lib/mcollective/application.rb +21 -6
  3. data/lib/mcollective/client.rb +7 -0
  4. data/lib/mcollective/config.rb +13 -1
  5. data/lib/mcollective/connector/base.rb +2 -0
  6. data/lib/mcollective/facts/base.rb +18 -5
  7. data/lib/mcollective/log.rb +7 -0
  8. data/lib/mcollective/logger/base.rb +12 -8
  9. data/lib/mcollective/logger/file_logger.rb +7 -0
  10. data/lib/mcollective/message.rb +1 -1
  11. data/lib/mcollective/optionparser.rb +4 -0
  12. data/lib/mcollective/registration/base.rb +24 -10
  13. data/lib/mcollective/rpc/agent.rb +7 -1
  14. data/lib/mcollective/rpc/client.rb +89 -35
  15. data/lib/mcollective/rpc/helpers.rb +8 -3
  16. data/lib/mcollective/rpc/result.rb +4 -0
  17. data/lib/mcollective/rpc/stats.rb +6 -2
  18. data/lib/mcollective/shell.rb +2 -0
  19. data/lib/mcollective/ssl.rb +5 -0
  20. data/lib/mcollective/util.rb +29 -1
  21. data/lib/mcollective/validator.rb +9 -4
  22. data/spec/spec_helper.rb +6 -0
  23. data/spec/unit/config_spec.rb +10 -0
  24. data/spec/unit/connector/base_spec.rb +28 -0
  25. data/spec/unit/facts/base_spec.rb +35 -0
  26. data/spec/unit/log_spec.rb +9 -0
  27. data/spec/unit/logger/base_spec.rb +12 -2
  28. data/spec/unit/logger/file_logger_spec.rb +82 -0
  29. data/spec/unit/plugins/mcollective/application/plugin_spec.rb +1 -0
  30. data/spec/unit/plugins/mcollective/connector/activemq_spec.rb +44 -17
  31. data/spec/unit/plugins/mcollective/connector/rabbitmq_spec.rb +20 -19
  32. data/spec/unit/plugins/mcollective/data/fact_data_spec.rb +92 -0
  33. data/spec/unit/registration/base_spec.rb +46 -0
  34. data/spec/unit/rpc/agent_spec.rb +37 -0
  35. data/spec/unit/rpc/client_spec.rb +68 -15
  36. data/spec/unit/rpc/result_spec.rb +21 -0
  37. data/spec/unit/runner_spec.rb +97 -19
  38. data/spec/unit/shell_spec.rb +5 -0
  39. data/spec/unit/ssl_spec.rb +5 -0
  40. data/spec/unit/util_spec.rb +163 -1
  41. metadata +215 -209
@@ -264,9 +264,14 @@ module MCollective
264
264
  parser.on('--one', '-1', 'Send request to only one discovered nodes') do |v|
265
265
  options[:mcollective_limit_targets] = 1
266
266
  end
267
-
268
- parser.on('--batch SIZE', Integer, 'Do requests in batches') do |v|
269
- options[:batch_size] = v
267
+
268
+ parser.on('--batch SIZE', 'Do requests in batches') do |v|
269
+ # validate batch string. Is it x% where x > 0 or is it an integer
270
+ if ((v =~ /^(\d+)%$/ && Integer($1) != 0) || v =~ /^(\d+)$/)
271
+ options[:batch_size] = v
272
+ else
273
+ raise(::OptionParser::InvalidArgument.new(v))
274
+ end
270
275
  end
271
276
 
272
277
  parser.on('--batch-sleep SECONDS', Float, 'Sleep time between batches') do |v|
@@ -40,6 +40,10 @@ module MCollective
40
40
  :statusmsg => @results[:statusmsg],
41
41
  :data => @results[:data]}.to_json(*a)
42
42
  end
43
+
44
+ def <=>(other)
45
+ self[:sender] <=> other[:sender]
46
+ end
43
47
  end
44
48
  end
45
49
  end
@@ -242,8 +242,12 @@ module MCollective
242
242
  result_text.puts Util.colorize(:red, "No response from:")
243
243
  result_text.puts
244
244
 
245
- @noresponsefrom.sort.in_groups_of(3) do |c|
246
- result_text.puts " %-30s%-30s%-30s" % c
245
+ field_size = Util.field_size(@noresponsefrom, 30)
246
+ fields_num = Util.field_number(field_size)
247
+ format = " " + ( " %-#{field_size}s" * fields_num )
248
+
249
+ @noresponsefrom.sort.in_groups_of(fields_num) do |c|
250
+ result_text.puts format % c
247
251
  end
248
252
 
249
253
  result_text.puts
@@ -57,7 +57,9 @@ module MCollective
57
57
  @environment = {}
58
58
  else
59
59
  @environment.merge!(val.dup)
60
+ @environment = @environment.delete_if { |k,v| v.nil? }
60
61
  end
62
+
61
63
  when "timeout"
62
64
  raise "timeout should be a positive integer or the symbol :on_thread_exit symbol" unless val.eql?(:on_thread_exit) || ( val.is_a?(Fixnum) && val>0 )
63
65
  @timeout = val
@@ -193,6 +193,11 @@ module MCollective
193
193
  end
194
194
 
195
195
  def self.base64_decode(string)
196
+ # The Base 64 character set is A-Z a-z 0-9 + / =
197
+ # Also allow for whitespace, but raise if we get anything else
198
+ if string !~ /^[A-Za-z0-9+\/=\s]+$/
199
+ raise ArgumentError, 'invalid base64'
200
+ end
196
201
  Base64.decode64(string)
197
202
  end
198
203
 
@@ -75,11 +75,21 @@ module MCollective
75
75
  return false if fact.nil?
76
76
 
77
77
  fact = fact.clone
78
+ case fact
79
+ when Array
80
+ return fact.any? { |element| test_fact_value(element, value, operator)}
81
+ when Hash
82
+ return fact.keys.any? { |element| test_fact_value(element, value, operator)}
83
+ else
84
+ return test_fact_value(fact, value, operator)
85
+ end
86
+ end
78
87
 
88
+ def self.test_fact_value(fact, value, operator)
79
89
  if operator == '=~'
80
90
  # to maintain backward compat we send the value
81
91
  # as /.../ which is what 1.0.x needed. this strips
82
- # off the /'s wich is what we need here
92
+ # off the /'s which is what we need here
83
93
  if value =~ /^\/(.+)\/$/
84
94
  value = $1
85
95
  end
@@ -104,6 +114,7 @@ module MCollective
104
114
 
105
115
  false
106
116
  end
117
+ private_class_method :test_fact_value
107
118
 
108
119
  # Checks if the configured identity matches the one supplied
109
120
  #
@@ -492,5 +503,22 @@ module MCollective
492
503
  template_path = File.join("/etc/mcollective", template_file)
493
504
  return template_path
494
505
  end
506
+
507
+ # subscribe to the direct addressing queue
508
+ def self.subscribe_to_direct_addressing_queue
509
+ subscribe(make_subscriptions("mcollective", :directed))
510
+ end
511
+
512
+ # Get field size for printing
513
+ def self.field_size(elements, min_size=40)
514
+ max_length = elements.max_by { |e| e.length }.length
515
+ max_length > min_size ? max_length : min_size
516
+ end
517
+
518
+ # Calculate number of fields for printing
519
+ def self.field_number(field_size, max_size=90)
520
+ number = (max_size/field_size).to_i
521
+ (number == 0) ? 1 : number
522
+ end
495
523
  end
496
524
  end
@@ -1,12 +1,18 @@
1
1
  module MCollective
2
2
  module Validator
3
3
  @last_load = nil
4
+ @@validator_mutex = Mutex.new
4
5
 
5
6
  # Loads the validator plugins. Validators will only be loaded every 5 minutes
6
7
  def self.load_validators
7
- if load_validators?
8
- @last_load = Time.now.to_i
9
- PluginManager.find_and_load("validator")
8
+ begin
9
+ @@validator_mutex.lock
10
+ if load_validators?
11
+ @last_load = Time.now.to_i
12
+ PluginManager.find_and_load("validator")
13
+ end
14
+ ensure
15
+ @@validator_mutex.unlock
10
16
  end
11
17
  end
12
18
 
@@ -44,7 +50,6 @@ module MCollective
44
50
 
45
51
  def self.load_validators?
46
52
  return true if @last_load.nil?
47
-
48
53
  (@last_load - Time.now.to_i) > 300
49
54
  end
50
55
 
data/spec/spec_helper.rb CHANGED
@@ -28,3 +28,9 @@ RSpec.configure do |config|
28
28
  MCollective::PluginManager.clear
29
29
  end
30
30
  end
31
+
32
+ # With the addition of the ddl requirement for connectors its becomes necessary
33
+ # to stub the inherited method. Because tests don't use a real config files libdirs
34
+ # aren't set and connectors have no way of finding their ddls so we stub it out
35
+ # in the general case and test for is specifically.
36
+ MCollective::Connector::Base.stubs(:inherited)
@@ -141,6 +141,16 @@ module MCollective
141
141
  Config.instance.loadconfig("/nonexisting")
142
142
  end
143
143
  end
144
+
145
+ it 'should enable agents by default' do
146
+ File.expects(:readlines).with("/nonexisting").returns(["libdir=/nonexistinglib"])
147
+ File.expects(:exists?).with("/nonexisting").returns(true)
148
+ PluginManager.stubs(:loadclass)
149
+ PluginManager.stubs("<<")
150
+
151
+ Config.instance.loadconfig("/nonexisting")
152
+ Config.instance.activate_agents.should == true
153
+ end
144
154
  end
145
155
 
146
156
  describe "#read_plugin_config_dir" do
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env rspec
2
+
3
+ require 'spec_helper'
4
+
5
+ module MCollective
6
+ module Connector
7
+ describe "base" do
8
+
9
+ before :each do
10
+ Base.unstub(:inherited)
11
+ end
12
+
13
+ it "should fail if the ddl isn't valid" do
14
+ PluginManager.expects(:<<).never
15
+
16
+ expect {
17
+ class TestConnectorA<Connector::Base;end
18
+ }.to raise_error RuntimeError
19
+ end
20
+
21
+ it "should load the ddl and add the connector to the PluginManager" do
22
+ DDL.stubs(:new)
23
+ class TestConnectorB<Connector::Base;end
24
+ PluginManager["connector_plugin"].class.should == MCollective::Connector::TestConnectorB
25
+ end
26
+ end
27
+ end
28
+ end
@@ -114,5 +114,40 @@ module MCollective::Facts
114
114
  end
115
115
  end
116
116
 
117
+ describe '#normalize_facts' do
118
+ it 'should make symbols that are keys be strings' do
119
+ Testfacts.new.send(:normalize_facts, {
120
+ :foo => "1",
121
+ "bar" => "2",
122
+ }).should == {
123
+ "foo" => "1",
124
+ "bar" => "2",
125
+ }
126
+ end
127
+
128
+ it 'should make values that are not strings be strings' do
129
+ Testfacts.new.send(:normalize_facts, {
130
+ "foo" => 1,
131
+ "bar" => :baz,
132
+ }).should == {
133
+ "foo" => "1",
134
+ "bar" => "baz",
135
+ }
136
+ end
137
+
138
+ it 'should not flatten arrays or hashes' do
139
+ Testfacts.new.send(:normalize_facts, {
140
+ "foo" => [ "1", "quux", 2 ],
141
+ "bar" => {
142
+ :baz => "quux",
143
+ },
144
+ }).should == {
145
+ "foo" => [ "1", "quux", "2" ],
146
+ "bar" => {
147
+ "baz" => "quux",
148
+ },
149
+ }
150
+ end
151
+ end
117
152
  end
118
153
  end
@@ -53,6 +53,15 @@ module MCollective
53
53
  end
54
54
  end
55
55
 
56
+ describe '#reopen' do
57
+ it 'should delegate the the logger' do
58
+ @logger.expects(:reopen)
59
+
60
+ Log.configure(@logger)
61
+ Log.reopen
62
+ end
63
+ end
64
+
56
65
  describe "#from" do
57
66
  let(:execution_stack) do
58
67
  if Util.windows?
@@ -42,7 +42,7 @@ module MCollective::Logger
42
42
  logger = Base.new
43
43
 
44
44
  expect {
45
- logger.send(:log, nil, nil, nil)
45
+ logger.log(nil, nil, nil)
46
46
  }.to raise_error("The logging class did not supply a log method")
47
47
  end
48
48
  end
@@ -52,11 +52,21 @@ module MCollective::Logger
52
52
  logger = Base.new
53
53
 
54
54
  expect {
55
- logger.send(:start)
55
+ logger.start
56
56
  }.to raise_error("The logging class did not supply a start method")
57
57
  end
58
58
  end
59
59
 
60
+ describe '#reopen' do
61
+ it 'should do nothing' do
62
+ logger = Base.new
63
+
64
+ expect {
65
+ logger.reopen
66
+ }.to_not raise_error
67
+ end
68
+ end
69
+
60
70
  describe "#map_level" do
61
71
  it "should map levels correctly" do
62
72
  logger = Base.new
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env rspec
2
+
3
+ require 'spec_helper'
4
+
5
+ module MCollective
6
+ require 'mcollective/logger/file_logger'
7
+
8
+ module Logger
9
+ describe File_logger do
10
+ let(:mock_logger) { mock('logger') }
11
+
12
+ before :each do
13
+ Config.instance.stubs(:loglevel).returns("error")
14
+ Config.instance.stubs(:logfile).returns("testfile")
15
+ Config.instance.stubs(:keeplogs).returns(false)
16
+ Config.instance.stubs(:max_log_size).returns(42)
17
+ ::Logger.stubs(:new).returns(mock_logger)
18
+ mock_logger.stubs(:formatter=)
19
+ mock_logger.stubs(:level=)
20
+ end
21
+
22
+ describe "#start" do
23
+ it "should set the level to be that specfied in the config" do
24
+ logger = File_logger.new
25
+
26
+ logger.expects(:set_level).with(:error)
27
+ logger.start
28
+ end
29
+ end
30
+
31
+ describe 'reopen' do
32
+ let(:logger) {
33
+ logger = File_logger.new
34
+ logger.instance_variable_set(:@logger, mock_logger)
35
+ logger
36
+ }
37
+
38
+ before :each do
39
+ mock_logger.stubs(:level)
40
+ mock_logger.stubs(:close)
41
+ logger.stubs(:start)
42
+ mock_logger.stubs(:level=)
43
+ end
44
+
45
+ it 'should close the current handle' do
46
+ mock_logger.expects(:close)
47
+ logger.reopen
48
+ end
49
+
50
+ it 'should open a new handle' do
51
+ logger.expects(:start)
52
+ logger.reopen
53
+ end
54
+
55
+ it 'should preserve the level' do
56
+ mock_logger.expects(:level).returns(12252)
57
+ mock_logger.expects(:level=).with(12252)
58
+ logger.reopen
59
+ end
60
+ end
61
+
62
+ describe '#set_logging_level' do
63
+ it 'should set the level' do
64
+ logger = File_logger.new
65
+ logger.instance_variable_set(:@logger, mock_logger)
66
+ mock_logger.expects(:level=).with(::Logger::ERROR)
67
+ logger.set_level("error")
68
+ end
69
+ end
70
+
71
+ describe "#log" do
72
+ it "should delegate to logger" do
73
+ logger = File_logger.new
74
+ logger.instance_variable_set(:@logger, mock_logger)
75
+
76
+ mock_logger.expects(:add).with(::Logger::INFO)
77
+ logger.log(:info, "rspec", "message")
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -22,6 +22,7 @@ module MCollective
22
22
  PluginManager.stubs(:find).with(:data, "ddl").returns([""])
23
23
  PluginManager.stubs(:find).with(:discovery, "ddl").returns([""])
24
24
  PluginManager.stubs(:find).with(:validator, "ddl").returns([""])
25
+ PluginManager.stubs(:find).with(:connector, "ddl").returns([""])
25
26
  @app.stubs(:load_plugin_ddl).with('rspec', :agent).returns(ddl)
26
27
  ddl.expects(:help).with("rspec-helptemplate.erb").returns("agent_template")
27
28
  @app.expects(:puts).with("agent_template")
@@ -47,9 +47,9 @@ module MCollective
47
47
 
48
48
  let(:subscription) do
49
49
  sub = mock
50
- sub.stubs("<<").returns(true)
51
- sub.stubs("include?").returns(false)
52
- sub.stubs("delete").returns(false)
50
+ sub.stubs(:<<).returns(true)
51
+ sub.stubs(:include?).returns(false)
52
+ sub.stubs(:delete).returns(false)
53
53
  sub
54
54
  end
55
55
 
@@ -452,46 +452,46 @@ module MCollective
452
452
  end
453
453
 
454
454
  it "should use the make_target correctly" do
455
- connector.expects("make_target").with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}})
455
+ connector.expects(:make_target).with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}})
456
456
  connector.subscribe("test", :broadcast, "mcollective")
457
457
  end
458
458
 
459
459
  it "should check for existing subscriptions" do
460
- connector.expects("make_target").with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"})
461
- subscription.expects("include?").with("rspec").returns(false)
460
+ connector.expects(:make_target).with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"})
461
+ subscription.expects(:include?).with("rspec").returns(false)
462
462
  connection.expects(:subscribe).never
463
463
 
464
464
  connector.subscribe("test", :broadcast, "mcollective")
465
465
  end
466
466
 
467
467
  it "should subscribe to the middleware" do
468
- connector.expects("make_target").with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"})
468
+ connector.expects(:make_target).with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"})
469
469
  connection.expects(:subscribe).with("test", {}, "rspec")
470
470
  connector.subscribe("test", :broadcast, "mcollective")
471
471
  end
472
472
 
473
473
  it "should add to the list of subscriptions" do
474
- connector.expects("make_target").with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"})
475
- subscription.expects("<<").with("rspec")
474
+ connector.expects(:make_target).with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"})
475
+ subscription.expects(:<<).with("rspec")
476
476
  connector.subscribe("test", :broadcast, "mcollective")
477
477
  end
478
478
  end
479
479
 
480
480
  describe "#unsubscribe" do
481
481
  it "should use make_target correctly" do
482
- connector.expects("make_target").with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}})
482
+ connector.expects(:make_target).with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}})
483
483
  connector.unsubscribe("test", :broadcast, "mcollective")
484
484
  end
485
485
 
486
486
  it "should unsubscribe from the target" do
487
- connector.expects("make_target").with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"})
487
+ connector.expects(:make_target).with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"})
488
488
  connection.expects(:unsubscribe).with("test", {}, "rspec").once
489
489
 
490
490
  connector.unsubscribe("test", :broadcast, "mcollective")
491
491
  end
492
492
 
493
493
  it "should delete the source from subscriptions" do
494
- connector.expects("make_target").with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"})
494
+ connector.expects(:make_target).with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"})
495
495
  subscription.expects(:delete).with("rspec").once
496
496
 
497
497
  connector.unsubscribe("test", :broadcast, "mcollective")
@@ -633,11 +633,38 @@ module MCollective
633
633
 
634
634
  describe "#make_target" do
635
635
  it "should create correct targets" do
636
- connector.make_target("test", :reply, "mcollective").should == {:name => "/queue/mcollective.reply.rspec_#{$$}", :headers => {}, :id => "/queue/mcollective.reply.rspec_#{$$}"}
637
- connector.make_target("test", :broadcast, "mcollective").should == {:name => "/topic/mcollective.test.agent", :headers => {}, :id => "/topic/mcollective.test.agent"}
638
- connector.make_target("test", :request, "mcollective").should == {:name => "/topic/mcollective.test.agent", :headers => {}, :id => "/topic/mcollective.test.agent"}
639
- connector.make_target("test", :direct_request, "mcollective").should == {:headers=>{}, :name=>"/queue/mcollective.nodes", :id => "/queue/mcollective.nodes"}
640
- connector.make_target("test", :directed, "mcollective").should == {:name => "/queue/mcollective.nodes", :headers=>{"selector"=>"mc_identity = 'rspec'"}, :id => "mcollective_directed_to_identity"}
636
+ Client.stubs(:request_sequence).returns(42)
637
+ connector.make_target("test", :reply, "mcollective").should == {
638
+ :name => "/queue/mcollective.reply.rspec_#{$$}.42",
639
+ :headers => {},
640
+ :id => "/queue/mcollective.reply.rspec_#{$$}.42",
641
+ }
642
+
643
+ connector.make_target("test", :broadcast, "mcollective").should == {
644
+ :name => "/topic/mcollective.test.agent",
645
+ :headers => {},
646
+ :id => "/topic/mcollective.test.agent",
647
+ }
648
+
649
+ connector.make_target("test", :request, "mcollective").should == {
650
+ :name => "/topic/mcollective.test.agent",
651
+ :headers => {},
652
+ :id => "/topic/mcollective.test.agent",
653
+ }
654
+
655
+ connector.make_target("test", :direct_request, "mcollective").should == {
656
+ :name => "/queue/mcollective.nodes",
657
+ :headers => {},
658
+ :id => "/queue/mcollective.nodes",
659
+ }
660
+
661
+ connector.make_target("test", :directed, "mcollective").should == {
662
+ :name => "/queue/mcollective.nodes",
663
+ :headers => {
664
+ "selector" => "mc_identity = 'rspec'",
665
+ },
666
+ :id => "mcollective_directed_to_identity",
667
+ }
641
668
  end
642
669
 
643
670
  it "should raise an error for unknown collectives" do