mcollective-client 2.5.3 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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