right_agent 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +78 -0
  3. data/Rakefile +86 -0
  4. data/lib/right_agent.rb +66 -0
  5. data/lib/right_agent/actor.rb +163 -0
  6. data/lib/right_agent/actor_registry.rb +76 -0
  7. data/lib/right_agent/actors/agent_manager.rb +189 -0
  8. data/lib/right_agent/agent.rb +735 -0
  9. data/lib/right_agent/agent_config.rb +403 -0
  10. data/lib/right_agent/agent_identity.rb +209 -0
  11. data/lib/right_agent/agent_tags_manager.rb +213 -0
  12. data/lib/right_agent/audit_formatter.rb +107 -0
  13. data/lib/right_agent/broker_client.rb +683 -0
  14. data/lib/right_agent/command.rb +30 -0
  15. data/lib/right_agent/command/agent_manager_commands.rb +134 -0
  16. data/lib/right_agent/command/command_client.rb +136 -0
  17. data/lib/right_agent/command/command_constants.rb +42 -0
  18. data/lib/right_agent/command/command_io.rb +128 -0
  19. data/lib/right_agent/command/command_parser.rb +87 -0
  20. data/lib/right_agent/command/command_runner.rb +105 -0
  21. data/lib/right_agent/command/command_serializer.rb +63 -0
  22. data/lib/right_agent/console.rb +65 -0
  23. data/lib/right_agent/core_payload_types.rb +42 -0
  24. data/lib/right_agent/core_payload_types/cookbook.rb +61 -0
  25. data/lib/right_agent/core_payload_types/cookbook_position.rb +46 -0
  26. data/lib/right_agent/core_payload_types/cookbook_repository.rb +116 -0
  27. data/lib/right_agent/core_payload_types/cookbook_sequence.rb +70 -0
  28. data/lib/right_agent/core_payload_types/dev_repositories.rb +90 -0
  29. data/lib/right_agent/core_payload_types/event_categories.rb +38 -0
  30. data/lib/right_agent/core_payload_types/executable_bundle.rb +138 -0
  31. data/lib/right_agent/core_payload_types/login_policy.rb +72 -0
  32. data/lib/right_agent/core_payload_types/login_user.rb +62 -0
  33. data/lib/right_agent/core_payload_types/planned_volume.rb +94 -0
  34. data/lib/right_agent/core_payload_types/recipe_instantiation.rb +60 -0
  35. data/lib/right_agent/core_payload_types/repositories_bundle.rb +50 -0
  36. data/lib/right_agent/core_payload_types/right_script_attachment.rb +95 -0
  37. data/lib/right_agent/core_payload_types/right_script_instantiation.rb +73 -0
  38. data/lib/right_agent/core_payload_types/secure_document.rb +66 -0
  39. data/lib/right_agent/core_payload_types/secure_document_location.rb +63 -0
  40. data/lib/right_agent/core_payload_types/software_repository_instantiation.rb +61 -0
  41. data/lib/right_agent/daemonize.rb +35 -0
  42. data/lib/right_agent/dispatcher.rb +348 -0
  43. data/lib/right_agent/enrollment_result.rb +217 -0
  44. data/lib/right_agent/exceptions.rb +30 -0
  45. data/lib/right_agent/ha_broker_client.rb +1278 -0
  46. data/lib/right_agent/idempotent_request.rb +140 -0
  47. data/lib/right_agent/log.rb +418 -0
  48. data/lib/right_agent/monkey_patches.rb +29 -0
  49. data/lib/right_agent/monkey_patches/amqp_patch.rb +274 -0
  50. data/lib/right_agent/monkey_patches/ruby_patch.rb +49 -0
  51. data/lib/right_agent/monkey_patches/ruby_patch/array_patch.rb +29 -0
  52. data/lib/right_agent/monkey_patches/ruby_patch/darwin_patch.rb +24 -0
  53. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch.rb +24 -0
  54. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch/file_patch.rb +30 -0
  55. data/lib/right_agent/monkey_patches/ruby_patch/object_patch.rb +49 -0
  56. data/lib/right_agent/monkey_patches/ruby_patch/singleton_patch.rb +46 -0
  57. data/lib/right_agent/monkey_patches/ruby_patch/string_patch.rb +107 -0
  58. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch.rb +32 -0
  59. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/file_patch.rb +90 -0
  60. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/process_patch.rb +63 -0
  61. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/stdio_patch.rb +27 -0
  62. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/time_patch.rb +55 -0
  63. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/win32ole_patch.rb +34 -0
  64. data/lib/right_agent/multiplexer.rb +91 -0
  65. data/lib/right_agent/operation_result.rb +270 -0
  66. data/lib/right_agent/packets.rb +637 -0
  67. data/lib/right_agent/payload_formatter.rb +104 -0
  68. data/lib/right_agent/pid_file.rb +159 -0
  69. data/lib/right_agent/platform.rb +319 -0
  70. data/lib/right_agent/platform/darwin.rb +227 -0
  71. data/lib/right_agent/platform/linux.rb +268 -0
  72. data/lib/right_agent/platform/windows.rb +1204 -0
  73. data/lib/right_agent/scripts/agent_controller.rb +522 -0
  74. data/lib/right_agent/scripts/agent_deployer.rb +379 -0
  75. data/lib/right_agent/scripts/common_parser.rb +153 -0
  76. data/lib/right_agent/scripts/log_level_manager.rb +193 -0
  77. data/lib/right_agent/scripts/stats_manager.rb +256 -0
  78. data/lib/right_agent/scripts/usage.rb +58 -0
  79. data/lib/right_agent/secure_identity.rb +92 -0
  80. data/lib/right_agent/security.rb +32 -0
  81. data/lib/right_agent/security/cached_certificate_store_proxy.rb +63 -0
  82. data/lib/right_agent/security/certificate.rb +102 -0
  83. data/lib/right_agent/security/certificate_cache.rb +89 -0
  84. data/lib/right_agent/security/distinguished_name.rb +56 -0
  85. data/lib/right_agent/security/encrypted_document.rb +84 -0
  86. data/lib/right_agent/security/rsa_key_pair.rb +76 -0
  87. data/lib/right_agent/security/signature.rb +86 -0
  88. data/lib/right_agent/security/static_certificate_store.rb +69 -0
  89. data/lib/right_agent/sender.rb +937 -0
  90. data/lib/right_agent/serialize.rb +29 -0
  91. data/lib/right_agent/serialize/message_pack.rb +102 -0
  92. data/lib/right_agent/serialize/secure_serializer.rb +131 -0
  93. data/lib/right_agent/serialize/secure_serializer_initializer.rb +47 -0
  94. data/lib/right_agent/serialize/serializable.rb +135 -0
  95. data/lib/right_agent/serialize/serializer.rb +149 -0
  96. data/lib/right_agent/stats_helper.rb +731 -0
  97. data/lib/right_agent/subprocess.rb +38 -0
  98. data/lib/right_agent/tracer.rb +124 -0
  99. data/right_agent.gemspec +60 -0
  100. data/spec/actor_registry_spec.rb +81 -0
  101. data/spec/actor_spec.rb +99 -0
  102. data/spec/agent_config_spec.rb +226 -0
  103. data/spec/agent_identity_spec.rb +75 -0
  104. data/spec/agent_spec.rb +571 -0
  105. data/spec/broker_client_spec.rb +961 -0
  106. data/spec/command/agent_manager_commands_spec.rb +51 -0
  107. data/spec/command/command_io_spec.rb +93 -0
  108. data/spec/command/command_parser_spec.rb +79 -0
  109. data/spec/command/command_runner_spec.rb +72 -0
  110. data/spec/command/command_serializer_spec.rb +51 -0
  111. data/spec/core_payload_types/dev_repositories_spec.rb +64 -0
  112. data/spec/core_payload_types/executable_bundle_spec.rb +59 -0
  113. data/spec/core_payload_types/login_user_spec.rb +98 -0
  114. data/spec/core_payload_types/right_script_attachment_spec.rb +65 -0
  115. data/spec/core_payload_types/spec_helper.rb +23 -0
  116. data/spec/dispatcher_spec.rb +372 -0
  117. data/spec/enrollment_result_spec.rb +53 -0
  118. data/spec/ha_broker_client_spec.rb +1673 -0
  119. data/spec/idempotent_request_spec.rb +136 -0
  120. data/spec/log_spec.rb +177 -0
  121. data/spec/monkey_patches/amqp_patch_spec.rb +100 -0
  122. data/spec/monkey_patches/eventmachine_spec.rb +62 -0
  123. data/spec/monkey_patches/string_patch_spec.rb +99 -0
  124. data/spec/multiplexer_spec.rb +48 -0
  125. data/spec/operation_result_spec.rb +171 -0
  126. data/spec/packets_spec.rb +418 -0
  127. data/spec/platform/platform_spec.rb +60 -0
  128. data/spec/results_mock.rb +45 -0
  129. data/spec/secure_identity_spec.rb +50 -0
  130. data/spec/security/cached_certificate_store_proxy_spec.rb +56 -0
  131. data/spec/security/certificate_cache_spec.rb +71 -0
  132. data/spec/security/certificate_spec.rb +49 -0
  133. data/spec/security/distinguished_name_spec.rb +46 -0
  134. data/spec/security/encrypted_document_spec.rb +55 -0
  135. data/spec/security/rsa_key_pair_spec.rb +55 -0
  136. data/spec/security/signature_spec.rb +66 -0
  137. data/spec/security/static_certificate_store_spec.rb +52 -0
  138. data/spec/sender_spec.rb +887 -0
  139. data/spec/serialize/message_pack_spec.rb +131 -0
  140. data/spec/serialize/secure_serializer_spec.rb +102 -0
  141. data/spec/serialize/serializable_spec.rb +90 -0
  142. data/spec/serialize/serializer_spec.rb +174 -0
  143. data/spec/spec.opts +2 -0
  144. data/spec/spec_helper.rb +77 -0
  145. data/spec/stats_helper_spec.rb +681 -0
  146. data/spec/tracer_spec.rb +114 -0
  147. metadata +320 -0
@@ -0,0 +1,53 @@
1
+ # Copyright (c) 2009-2011 RightScale, Inc, All Rights Reserved Worldwide.
2
+ #
3
+ # THIS PROGRAM IS CONFIDENTIAL AND PROPRIETARY TO RIGHTSCALE
4
+ # AND CONSTITUTES A VALUABLE TRADE SECRET. Any unauthorized use,
5
+ # reproduction, modification, or disclosure of this program is
6
+ # strictly prohibited. Any use of this program by an authorized
7
+ # licensee is strictly subject to the terms and conditions,
8
+ # including confidentiality obligations, set forth in the applicable
9
+ # License Agreement between RightScale.com, Inc. and
10
+ # the licensee.
11
+
12
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
13
+ require File.expand_path(File.join(File.dirname(__FILE__), '..' , 'lib', 'right_agent', 'enrollment_result'))
14
+
15
+ describe RightScale::EnrollmentResult do
16
+ before(:each) do
17
+ @key = 'topsecret'
18
+ @result = RightScale::EnrollmentResult.new(6, Time.now, 'mapper cert', 'my cert', 'my private key', @key)
19
+ @message = RightScale::EnrollmentResult.dump(@result)
20
+ end
21
+
22
+ it 'should serialize and unserialize correctly' do
23
+ r2 = RightScale::EnrollmentResult.load(@message, @key)
24
+ @result.should == r2
25
+ end
26
+
27
+ context "supporting different versions" do
28
+ RightScale::EnrollmentResult::SUPPORTED_VERSIONS.each do |v|
29
+ it "should support version #{v}" do
30
+ @result = RightScale::EnrollmentResult.new(v, Time.now, 'mapper cert', 'my cert', 'my private key', @key)
31
+ serialized = RightScale::EnrollmentResult.dump(@result)
32
+ @result2 = RightScale::EnrollmentResult.load(serialized, @key)
33
+ @result.should == @result2
34
+ end
35
+ end
36
+ end
37
+
38
+ it 'should fail to decrypt if tampered with' do
39
+ #Simulate some ciphertext tampering.
40
+ @message.gsub! /[0-9]/, '1'
41
+ @message.gsub! /"r_s_version":"[0-9]+"/, '"r_s_version":"6"'
42
+
43
+ lambda do
44
+ RightScale::EnrollmentResult.load(@message, @key)
45
+ end.should raise_error(RightScale::EnrollmentResult::IntegrityFailure)
46
+ end
47
+
48
+ it 'should fail to decrypt if the key is wrong' do
49
+ lambda do
50
+ RightScale::EnrollmentResult.load(@message, @key + "evil")
51
+ end.should raise_error(RightScale::EnrollmentResult::IntegrityFailure)
52
+ end
53
+ end
@@ -0,0 +1,1673 @@
1
+ #
2
+ # Copyright (c) 2009-2011 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
24
+
25
+ describe RightScale::HABrokerClient do
26
+
27
+ include FlexMock::ArgumentTypes
28
+
29
+ before(:each) do
30
+ flexmock(RightScale::Log).should_receive(:error).by_default.and_return { |m| raise RightScale::Log.format(*m) }
31
+ flexmock(RightScale::Log).should_receive(:warning).by_default.and_return { |m| raise RightScale::Log.format(*m) }
32
+ flexmock(RightScale::Log).should_receive(:info).by_default
33
+ end
34
+
35
+ describe "Context" do
36
+
37
+ before(:each) do
38
+ @packet1 = flexmock("packet1", :class => RightScale::Request, :name => "request", :type => "type1",
39
+ :from => "from1", :token => "token1", :one_way => false)
40
+ @packet2 = flexmock("packet2", :class => FlexMock, :name => "flexmock")
41
+ @brokers = ["broker"]
42
+ @options = {:option => "option"}
43
+ end
44
+
45
+ it "should initialize context" do
46
+ context = RightScale::HABrokerClient::Context.new(@packet1, @options, @brokers)
47
+ context.name.should == "request"
48
+ context.type.should == "type1"
49
+ context.from.should == "from1"
50
+ context.token.should == "token1"
51
+ context.one_way.should be_false
52
+ context.options.should == @options
53
+ context.brokers.should == @brokers
54
+ context.failed.should == []
55
+ end
56
+
57
+ it "should treat type, from, token, and one_way as optional members of packet but default one_way to true" do
58
+ context = RightScale::HABrokerClient::Context.new(@packet2, @options, @brokers)
59
+ context.name.should == "flexmock"
60
+ context.type.should be_nil
61
+ context.from.should be_nil
62
+ context.token.should be_nil
63
+ context.one_way.should be_true
64
+ context.options.should == @options
65
+ context.brokers.should == @brokers
66
+ context.failed.should == []
67
+ end
68
+
69
+ end
70
+
71
+ describe "Caching" do
72
+
73
+ before(:each) do
74
+ flexmock(Time).should_receive(:now).and_return(Time.at(1000000)).by_default
75
+ @published = RightScale::HABrokerClient::Published.new
76
+ @message1 = MessagePack.dump(:signature => "signature1")
77
+ @key1 = @message1[@message1 =~ /signature/, 1000]
78
+ @message2 = JSON.dump(:signature => "signature2")
79
+ @key2 = @message2[@message2 =~ /signature/, 1000]
80
+ @message3 = MessagePack.dump(:data => "just data")
81
+ @key3 = @message3
82
+ @packet1 = flexmock("packet1", :class => RightScale::Request, :name => "request", :type => "type1",
83
+ :from => "from1", :token => "token1", :one_way => false)
84
+ @packet2 = flexmock("packet2", :class => RightScale::Request, :name => "request", :type => "type2",
85
+ :from => "from2", :token => "token2", :one_way => false)
86
+ @packet3 = flexmock("packet3", :class => RightScale::Push, :name => "push", :type => "type3",
87
+ :from => "from3", :token => "token3", :one_way => true)
88
+ @brokers = ["broker"]
89
+ @options = {:option => "option"}
90
+ @context1 = RightScale::HABrokerClient::Context.new(@packet1, @options, @brokers)
91
+ @context2 = RightScale::HABrokerClient::Context.new(@packet2, @options, @brokers)
92
+ @context3 = RightScale::HABrokerClient::Context.new(@packet3, @options, @brokers)
93
+ end
94
+
95
+ it "should use message signature as cache hash key if it has one" do
96
+ @published.identify(@message1).should == @key1
97
+ @published.identify(@message2).should == @key2
98
+ @published.identify(@message3).should == @key3
99
+ end
100
+
101
+ it "should store message info" do
102
+ @published.store(@message1, @context1)
103
+ @published.instance_variable_get(:@cache)[@key1].should == [1000000, @context1]
104
+ @published.instance_variable_get(:@lru).should == [@key1]
105
+ end
106
+
107
+ it "should update timestamp and lru list when store to existing entry" do
108
+ @published.store(@message1, @context1)
109
+ @published.instance_variable_get(:@cache)[@key1].should == [1000000, @context1]
110
+ @published.instance_variable_get(:@lru).should == [@key1]
111
+ @published.store(@message2, @context2)
112
+ @published.instance_variable_get(:@lru).should == [@key1, @key2]
113
+ flexmock(Time).should_receive(:now).and_return(Time.at(1000010))
114
+ @published.store(@message1, @context1)
115
+ @published.instance_variable_get(:@cache)[@key1].should == [1000010, @context1]
116
+ @published.instance_variable_get(:@lru).should == [@key2, @key1]
117
+ end
118
+
119
+ it "should remove old cache entries when store new one" do
120
+ @published.store(@message1, @context1)
121
+ @published.store(@message2, @context2)
122
+ @published.instance_variable_get(:@cache).keys.should == [@key1, @key2]
123
+ @published.instance_variable_get(:@lru).should == [@key1, @key2]
124
+ flexmock(Time).should_receive(:now).and_return(Time.at(1000031))
125
+ @published.store(@message3, @context3)
126
+ @published.instance_variable_get(:@cache).keys.should == [@key3]
127
+ @published.instance_variable_get(:@lru).should == [@key3]
128
+ end
129
+
130
+ it "should fetch message info and make it the most recently used" do
131
+ @published.store(@message1, @context1)
132
+ @published.store(@message2, @context2)
133
+ @published.instance_variable_get(:@lru).should == [@key1, @key2]
134
+ @published.fetch(@message1).should == @context1
135
+ @published.instance_variable_get(:@lru).should == [@key2, @key1]
136
+ end
137
+
138
+ it "should fetch empty hash if entry not found" do
139
+ @published.fetch(@message1).should be_nil
140
+ @published.store(@message1, @context1)
141
+ @published.fetch(@message1).should_not be_nil
142
+ @published.fetch(@message2).should be_nil
143
+ end
144
+
145
+ end # Published
146
+
147
+ context "when initializing" do
148
+
149
+ before(:each) do
150
+ @serializer = flexmock("Serializer")
151
+ @exceptions = RightScale::StatsHelper::ExceptionStats
152
+ @identity = "rs-broker-localhost-5672"
153
+ @address = {:host => "localhost", :port => 5672, :index => 0}
154
+ @broker = flexmock("broker_client", :identity => @identity, :usable? => true)
155
+ @broker.should_receive(:return_message).by_default
156
+ @broker.should_receive(:update_status).by_default
157
+ flexmock(RightScale::BrokerClient).should_receive(:new).and_return(@broker).by_default
158
+ @island1 = flexmock("island1", :id => 11, :broker_hosts => "second:1,first:0", :broker_ports => "5673")
159
+ @island2 = flexmock("island2", :id => 22, :broker_hosts => "third:0,fourth:1", :broker_ports => nil)
160
+ @islands = {11 => @island1, 22 => @island2}
161
+ end
162
+
163
+ it "should create a broker client for default host and port" do
164
+ flexmock(RightScale::BrokerClient).should_receive(:new).with(@identity, @address, @serializer,
165
+ @exceptions, Hash, nil, nil).and_return(@broker).once
166
+ ha = RightScale::HABrokerClient.new(@serializer)
167
+ ha.brokers.should == [@broker]
168
+ end
169
+
170
+ it "should create broker clients for specified hosts and ports and assign index in order of creation" do
171
+ address1 = {:host => "first", :port => 5672, :index => 0}
172
+ broker1 = flexmock("broker_client1", :identity => "rs-broker-first-5672", :usable? => true, :return_message => true)
173
+ flexmock(RightScale::BrokerClient).should_receive(:new).with("rs-broker-first-5672", address1, @serializer,
174
+ RightScale::StatsHelper::ExceptionStats, Hash, nil, nil).and_return(broker1).once
175
+ address2 = {:host => "second", :port => 5672, :index => 1}
176
+ broker2 = flexmock("broker_client2", :identity => "rs-broker-second-5672", :usable? => true, :return_message => true)
177
+ flexmock(RightScale::BrokerClient).should_receive(:new).with("rs-broker-second-5672", address2, @serializer,
178
+ RightScale::StatsHelper::ExceptionStats, Hash, nil, nil).and_return(broker2).once
179
+ ha = RightScale::HABrokerClient.new(@serializer, :host => "first, second", :port => 5672)
180
+ ha.brokers.should == [broker1, broker2]
181
+ ha.home_island.should be_nil
182
+ end
183
+
184
+ it "should create broker clients for specified islands" do
185
+ address1 = {:host => "first", :port => 5673, :index => 0}
186
+ broker1 = flexmock("broker_client1", :identity => "rs-broker-first-5673", :usable? => true, :return_message => true)
187
+ flexmock(RightScale::BrokerClient).should_receive(:new).with("rs-broker-first-5673", address1, @serializer,
188
+ RightScale::StatsHelper::ExceptionStats, Hash, @island1, nil).and_return(broker1).once
189
+ address2 = {:host => "second", :port => 5673, :index => 1}
190
+ broker2 = flexmock("broker_client2", :identity => "rs-broker-second-5673", :usable? => true, :return_message => true)
191
+ flexmock(RightScale::BrokerClient).should_receive(:new).with("rs-broker-second-5673", address2, @serializer,
192
+ RightScale::StatsHelper::ExceptionStats, Hash, @island1, nil).and_return(broker2).once
193
+ address3 = {:host => "third", :port => 5672, :index => 0}
194
+ broker3 = flexmock("broker_client3", :identity => "rs-broker-third-5672", :usable? => true, :return_message => true)
195
+ flexmock(RightScale::BrokerClient).should_receive(:new).with("rs-broker-third-5672", address3, @serializer,
196
+ RightScale::StatsHelper::ExceptionStats, Hash, @island2, nil).and_return(broker3).once
197
+ address4 = {:host => "fourth", :port => 5672, :index => 1}
198
+ broker4 = flexmock("broker_client4", :identity => "rs-broker-fourth-5672", :usable? => true, :return_message => true)
199
+ flexmock(RightScale::BrokerClient).should_receive(:new).with("rs-broker-fourth-5672", address4, @serializer,
200
+ RightScale::StatsHelper::ExceptionStats, Hash, @island2, nil).and_return(broker4).once
201
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => 22)
202
+ ha.brokers.should == [broker3, broker4, broker2, broker1]
203
+ ha.home_island.should == 22
204
+ end
205
+
206
+ it "should raise an ArgumentError if it cannot find the home island" do
207
+ flexmock(RightScale::BrokerClient).should_receive(:new).never
208
+ lambda { RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => 33) }.
209
+ should raise_error(ArgumentError, /Could not find home island 33/)
210
+ end
211
+
212
+ it "should setup to receive returned messages from each usable broker client" do
213
+ @broker.should_receive(:return_message).twice
214
+ flexmock(RightScale::BrokerClient).should_receive(:new).and_return(@broker).twice
215
+ RightScale::HABrokerClient.new(@serializer, :host => "first, second", :port => 5672)
216
+ end
217
+
218
+ end # when initializing
219
+
220
+ context "when parsing user_data" do
221
+
222
+ it "should extra host list from RS_rn_url and RS_rn_host" do
223
+ RightScale::HABrokerClient.parse_user_data("RS_rn_url=rs@first/right_net&RS_rn_host=:0,second:1").should ==
224
+ ["first:0,second:1", nil]
225
+ end
226
+
227
+ it "should extra port list from RS_rn_port" do
228
+ RightScale::HABrokerClient.parse_user_data("RS_rn_url=rs@host/right_net&RS_rn_host=:1,host:0&RS_rn_port=5673:1,5672:0").should ==
229
+ ["host:1,host:0", "5673:1,5672:0"]
230
+ end
231
+
232
+ it "should raise an exception if there is no user data" do
233
+ lambda { RightScale::HABrokerClient.parse_user_data(nil) }.should raise_error(RightScale::HABrokerClient::NoUserData)
234
+ lambda { RightScale::HABrokerClient.parse_user_data("") }.should raise_error(RightScale::HABrokerClient::NoUserData)
235
+ end
236
+
237
+ it "should raise an exception if there are no broker hosts defined in the data" do
238
+ lambda { RightScale::HABrokerClient.parse_user_data("blah") }.should raise_error(RightScale::HABrokerClient::NoBrokerHosts)
239
+ end
240
+
241
+ it "should translate old host name to standard form" do
242
+ RightScale::HABrokerClient.parse_user_data("RS_rn_url=rs@broker.rightscale.com/right_net").should ==
243
+ ["broker1-1.rightscale.com", nil]
244
+ end
245
+
246
+ end # when parsing user_data
247
+
248
+ context "when addressing" do
249
+
250
+ it "should form list of broker addresses from specified hosts and ports" do
251
+ RightScale::HABrokerClient.addresses("first,second", "5672, 5674").should ==
252
+ [{:host => "first", :port => 5672, :index => 0}, {:host => "second", :port => 5674, :index => 1}]
253
+ end
254
+
255
+ it "should form list of broker addresses from specified hosts and ports and use ids associated with hosts" do
256
+ RightScale::HABrokerClient.addresses("first:1,second:2", "5672, 5674").should ==
257
+ [{:host => "first", :port => 5672, :index => 1}, {:host => "second", :port => 5674, :index => 2}]
258
+ end
259
+
260
+ it "should form list of broker addresses from specified hosts and ports and use ids associated with ports" do
261
+ RightScale::HABrokerClient.addresses("host", "5672:0, 5674:2").should ==
262
+ [{:host => "host", :port => 5672, :index => 0}, {:host => "host", :port => 5674, :index => 2}]
263
+ end
264
+
265
+ it "should use default host and port for broker identity if none provided" do
266
+ RightScale::HABrokerClient.addresses(nil, nil).should == [{:host => "localhost", :port => 5672, :index => 0}]
267
+ end
268
+
269
+ it "should use default port when ports is an empty string" do
270
+ RightScale::HABrokerClient.addresses("first, second", "").should ==
271
+ [{:host => "first", :port => 5672, :index => 0}, {:host => "second", :port => 5672, :index => 1}]
272
+ end
273
+
274
+ it "should use default host when hosts is an empty string" do
275
+ RightScale::HABrokerClient.addresses("", "5672, 5673").should ==
276
+ [{:host => "localhost", :port => 5672, :index => 0}, {:host => "localhost", :port => 5673, :index => 1}]
277
+ end
278
+
279
+ it "should reuse host if there is only one but multiple ports" do
280
+ RightScale::HABrokerClient.addresses("first", "5672, 5674").should ==
281
+ [{:host => "first", :port => 5672, :index => 0}, {:host => "first", :port => 5674, :index => 1}]
282
+ end
283
+
284
+ it "should reuse port if there is only one but multiple hosts" do
285
+ RightScale::HABrokerClient.addresses("first, second", 5672).should ==
286
+ [{:host => "first", :port => 5672, :index => 0}, {:host => "second", :port => 5672, :index => 1}]
287
+ end
288
+
289
+ it "should apply ids associated with host" do
290
+ RightScale::HABrokerClient.addresses("first:0, third:2", 5672).should ==
291
+ [{:host => "first", :port => 5672, :index => 0}, {:host => "third", :port => 5672, :index => 2}]
292
+ end
293
+
294
+ it "should not allow mismatched number of hosts and ports" do
295
+ runner = lambda { RightScale::HABrokerClient.addresses("first, second", "5672, 5673, 5674") }
296
+ runner.should raise_exception(ArgumentError)
297
+ end
298
+
299
+ end # when addressing
300
+
301
+ context "when identifying" do
302
+
303
+ before(:each) do
304
+ @serializer = flexmock("Serializer")
305
+ @exceptions = RightScale::StatsHelper::ExceptionStats
306
+
307
+ @address1 = {:host => "first", :port => 5672, :index => 0}
308
+ @identity1 = "rs-broker-first-5672"
309
+ @broker1 = flexmock("broker_client1", :identity => @identity1, :usable? => true, :return_message => true,
310
+ :alias => "b0", :host => "first", :port => 5672, :index => 0)
311
+ @broker1.should_receive(:island_id).and_return(nil).by_default
312
+ @broker1.should_receive(:in_home_island).and_return(true).by_default
313
+ flexmock(RightScale::BrokerClient).should_receive(:new).with(@identity1, @address1, @serializer,
314
+ @exceptions, Hash, nil, nil).and_return(@broker1).by_default
315
+
316
+ @address2 = {:host => "second", :port => 5672, :index => 1}
317
+ @identity2 = "rs-broker-second-5672"
318
+ @broker2 = flexmock("broker_client2", :identity => @identity2, :usable? => true, :return_message => true,
319
+ :alias => "b1", :host => "second", :port => 5672, :index => 1)
320
+ @broker2.should_receive(:island_id).and_return(nil).by_default
321
+ @broker2.should_receive(:in_home_island).and_return(true).by_default
322
+ flexmock(RightScale::BrokerClient).should_receive(:new).with(@identity2, @address2, @serializer,
323
+ @exceptions, Hash, nil, nil).and_return(@broker2).by_default
324
+
325
+ @address3 = {:host => "third", :port => 5672, :index => 2}
326
+ @identity3 = "rs-broker-third-5672"
327
+ @broker3 = flexmock("broker_client3", :identity => @identity3, :usable? => true, :return_message => true,
328
+ :alias => "b2", :host => "third", :port => 5672, :index => 2)
329
+ @broker3.should_receive(:island_id).and_return(nil).by_default
330
+ @broker3.should_receive(:in_home_island).and_return(true).by_default
331
+ flexmock(RightScale::BrokerClient).should_receive(:new).with(@identity3, @address3, @serializer,
332
+ @exceptions, Hash, nil, nil).and_return(@broker3).by_default
333
+ end
334
+
335
+ it "should use host and port to uniquely identity broker in AgentIdentity format" do
336
+ RightScale::HABrokerClient.identity("localhost", 5672).should == "rs-broker-localhost-5672"
337
+ RightScale::HABrokerClient.identity("10.21.102.23", 1234).should == "rs-broker-10.21.102.23-1234"
338
+ end
339
+
340
+ it "should replace '-' with '~' in host names when forming broker identity" do
341
+ RightScale::HABrokerClient.identity("9-1-1", 5672).should == "rs-broker-9~1~1-5672"
342
+ end
343
+
344
+ it "should use default port when forming broker identity" do
345
+ RightScale::HABrokerClient.identity("10.21.102.23").should == "rs-broker-10.21.102.23-5672"
346
+ end
347
+
348
+ it "should list broker identities" do
349
+ RightScale::HABrokerClient.identities("first,second", "5672, 5674").should ==
350
+ ["rs-broker-first-5672", "rs-broker-second-5674"]
351
+ end
352
+
353
+ it "should convert identities into aliases" do
354
+ ha = RightScale::HABrokerClient.new(@serializer, :host => "first:0, third:2", :port => 5672)
355
+ ha.aliases([@identity3]).should == ["b2"]
356
+ ha.aliases([@identity3, @identity1]).should == ["b2", "b0"]
357
+ end
358
+
359
+ it "should convert identities into nil alias when unknown" do
360
+ ha = RightScale::HABrokerClient.new(@serializer, :host => "first:0, third:2", :port => 5672)
361
+ ha.aliases(["rs-broker-second-5672", nil]).should == [nil, nil]
362
+ end
363
+
364
+ it "should convert identity into alias" do
365
+ ha = RightScale::HABrokerClient.new(@serializer, :host => "first:0, third:2", :port => 5672)
366
+ ha.alias_(@identity3).should == "b2"
367
+ end
368
+
369
+ it "should convert identity into nil alias when unknown" do
370
+ ha = RightScale::HABrokerClient.new(@serializer, :host => "first:0, third:2", :port => 5672)
371
+ ha.alias_("rs-broker-second-5672").should == nil
372
+ end
373
+
374
+ it "should convert identity into parts" do
375
+ ha = RightScale::HABrokerClient.new(@serializer, :host => "first:0, third:2", :port => 5672)
376
+ ha.identity_parts(@identity3).should == ["third", 5672, 2, 1, nil]
377
+ end
378
+
379
+ it "should convert an alias into parts" do
380
+ ha = RightScale::HABrokerClient.new(@serializer, :host => "first:0, third:2", :port => 5672)
381
+ ha.identity_parts("b2").should == ["third", 5672, 2, 1, nil]
382
+ end
383
+
384
+ it "should convert unknown identity into nil parts" do
385
+ ha = RightScale::HABrokerClient.new(@serializer, :host => "first:0, third:2", :port => 5672)
386
+ ha.identity_parts("rs-broker-second-5672").should == [nil, nil, nil, nil, nil]
387
+ end
388
+
389
+ it "should get identity from identity" do
390
+ ha = RightScale::HABrokerClient.new(@serializer, :host => "first:0, third:2", :port => 5672)
391
+ ha.get(@identity1).should == @identity1
392
+ ha.get("rs-broker-second-5672").should be_nil
393
+ ha.get(@identity3).should == @identity3
394
+ end
395
+
396
+ it "should get identity from an alias" do
397
+ ha = RightScale::HABrokerClient.new(@serializer, :host => "first:0, third:2", :port => 5672)
398
+ ha.get("b0").should == @identity1
399
+ ha.get("b1").should be_nil
400
+ ha.get("b2").should == @identity3
401
+ end
402
+
403
+ it "should generate host:index list" do
404
+ ha = RightScale::HABrokerClient.new(@serializer, :host => "second:1, first:0, third:2", :port => 5672)
405
+ ha.hosts.should == "second:1,first:0,third:2"
406
+ end
407
+
408
+ it "should generate port:index list" do
409
+ ha = RightScale::HABrokerClient.new(@serializer, :host => "second:1, third:2, first:0", :port => 5672)
410
+ ha.ports.should == "5672:1,5672:2,5672:0"
411
+ end
412
+
413
+ context "when using islands" do
414
+
415
+ before(:each) do
416
+ @island1 = flexmock("island1", :id => 11, :broker_hosts => "second:1,first:0", :broker_ports => "5672")
417
+ @island2 = flexmock("island2", :id => 22, :broker_hosts => "third:0,fourth:1", :broker_ports => nil)
418
+ @islands = {11 => @island1, 22 => @island2}
419
+
420
+ @broker1.should_receive(:island_id).and_return(11)
421
+ @broker1.should_receive(:in_home_island).and_return(false)
422
+ flexmock(RightScale::BrokerClient).should_receive(:new).with(@identity1, @address1, @serializer,
423
+ @exceptions, Hash, @island1, nil).and_return(@broker1)
424
+
425
+ @broker2.should_receive(:island_id).and_return(11)
426
+ @broker2.should_receive(:in_home_island).and_return(false)
427
+ flexmock(RightScale::BrokerClient).should_receive(:new).with(@identity2, @address2, @serializer,
428
+ @exceptions, Hash, @island1, nil).and_return(@broker2)
429
+
430
+ @address3 = {:host => "third", :port => 5672, :index => 0}
431
+ @identity3 = "rs-broker-third-5672"
432
+ @broker3 = flexmock("broker_client3", :identity => @identity3, :usable? => true, :return_message => true,
433
+ :alias => "b0", :host => "third", :port => 5672, :index => 0)
434
+ @broker3.should_receive(:island_id).and_return(22).by_default
435
+ @broker3.should_receive(:in_home_island).and_return(true)
436
+ flexmock(RightScale::BrokerClient).should_receive(:new).with(@identity3, @address3, @serializer,
437
+ @exceptions, Hash, @island2, nil).and_return(@broker3)
438
+
439
+ @address4 = {:host => "fourth", :port => 5672, :index => 1}
440
+ @identity4 = "rs-broker-fourth-5672"
441
+ @broker4 = flexmock("broker_client4", :identity => @identity4, :usable? => true, :return_message => true,
442
+ :alias => "b1", :host => "fourth", :port => 5672, :index => 1)
443
+ @broker4.should_receive(:island_id).and_return(22)
444
+ @broker4.should_receive(:in_home_island).and_return(true)
445
+ flexmock(RightScale::BrokerClient).should_receive(:new).with(@identity4, @address4, @serializer,
446
+ @exceptions, Hash, @island2, nil).and_return(@broker4)
447
+ end
448
+
449
+ it "should convert identity into parts that includes island_id" do
450
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => 22)
451
+ ha.identity_parts(@identity1).should == ["first", 5672, 0, 1, 11]
452
+ ha.identity_parts(@identity2).should == ["second", 5672, 1, 0, 11]
453
+ ha.identity_parts(@identity3).should == ["third", 5672, 0, 0, 22]
454
+ ha.identity_parts(@identity4).should == ["fourth", 5672, 1, 1, 22]
455
+ end
456
+
457
+ it "should generate host:index list for home island" do
458
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => 22)
459
+ ha.hosts.should == "third:0,fourth:1"
460
+ ha.hosts(22).should == "third:0,fourth:1"
461
+ end
462
+
463
+ it "should generate host:index list for other island" do
464
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => 22)
465
+ ha.hosts(11).should == "second:1,first:0"
466
+ end
467
+
468
+ it "should generate port:index list for home island" do
469
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => 22)
470
+ ha.ports.should == "5672:0,5672:1"
471
+ ha.ports(22).should == "5672:0,5672:1"
472
+ end
473
+
474
+ it "should generate port:index list for other island" do
475
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => 22)
476
+ ha.ports(11).should == "5672:1,5672:0"
477
+ end
478
+
479
+ end # when using islands
480
+
481
+ end # when identifying
482
+
483
+ context "when" do
484
+
485
+ before(:each) do
486
+ @serializer = flexmock("Serializer")
487
+ @exceptions = RightScale::StatsHelper::ExceptionStats
488
+
489
+ @island1 = flexmock("island1", :id => 11, :index => 0, :broker_ports => nil)
490
+ @island1.should_receive(:broker_hosts).and_return("first:0,second:1").by_default
491
+ @island2 = flexmock("island2", :id => 22, :index => 1, :broker_ports => nil)
492
+ @island2.should_receive(:broker_hosts).and_return("third:0,fourth:1").by_default
493
+ @islands = {11 => @island1, 22 => @island2}
494
+ @home = 22
495
+
496
+ # Generate mocking for five BrokerClients in two islands with second being home island
497
+ # The fifth client is not configured for use except when doing island updates
498
+ # key index host alias island_id
499
+ { 1 => [0, "first", "i0b0", 1],
500
+ 2 => [1, "second", "i0b1", 1],
501
+ 3 => [0, "third", "b0" , 2],
502
+ 4 => [1, "fourth", "b1" , 2],
503
+ 5 => [2, "fifth", "b2" , 2] }.each do |k, v|
504
+ i, h, a, d = v
505
+ eval("@identity#{k} = 'rs-broker-#{h}-5672'")
506
+ eval("@address#{k} = {:host => '#{h}', :port => 5672, :index => #{i}}")
507
+ eval("@broker#{k} = flexmock('broker_client#{k}', :identity => @identity#{k}, :alias => '#{a}', " +
508
+ ":host => '#{h}', :port => 5672, :index => #{i}, :island_id => #{d}#{d}, " +
509
+ ":in_home_island => #{d}#{d} == @home)")
510
+ eval("@broker#{k}.should_receive(:status).and_return(:connected).by_default")
511
+ eval("@broker#{k}.should_receive(:usable?).and_return(true).by_default")
512
+ eval("@broker#{k}.should_receive(:connected?).and_return(true).by_default")
513
+ eval("@broker#{k}.should_receive(:subscribe).and_return(true).by_default")
514
+ eval("@broker#{k}.should_receive(:return_message).and_return(true).by_default")
515
+ eval("flexmock(RightScale::BrokerClient).should_receive(:new).with(@identity#{k}, @address#{k}, " +
516
+ "@serializer, @exceptions, Hash, @island#{d}, nil).and_return(@broker#{k}).by_default")
517
+ end
518
+ end
519
+
520
+ context "connecting" do
521
+
522
+ # Generate mocking for three BrokerClients that do not use islands
523
+ before(:each) do
524
+ {0 => "a", 1 => "b", 2 => "c"}.each do |i, h|
525
+ eval("@identity_#{h} = 'rs-broker-#{h}-5672'")
526
+ eval("@address_#{h} = {:host => '#{h}', :port => 5672, :index => #{i}}")
527
+ eval("@broker_#{h} = flexmock('broker_client_#{h}', :identity => @identity_#{h}, :alias => 'b#{i}', " +
528
+ ":host => '#{h}', :port => 5672, :index => #{i}, :island_id => nil, " +
529
+ ":in_home_island => true)")
530
+ eval("@broker_#{h}.should_receive(:usable?).and_return(true).by_default")
531
+ eval("@broker_#{h}.should_receive(:return_message).and_return(true).by_default")
532
+ eval("flexmock(RightScale::BrokerClient).should_receive(:new).with(@identity_#{h}, @address_#{h}, " +
533
+ "@serializer, @exceptions, Hash, nil, any).and_return(@broker_#{h}).by_default")
534
+ end
535
+ end
536
+
537
+ it "should connect and add a new broker client to the end of the list" do
538
+ ha = RightScale::HABrokerClient.new(@serializer, :host => "a", :port => 5672)
539
+ ha.brokers.size.should == 1
540
+ ha.brokers[0].alias == "b0"
541
+ flexmock(RightScale::BrokerClient).should_receive(:new).with(@identity_b, @address_b, @serializer,
542
+ @exceptions, Hash, nil, nil).and_return(@broker_b).once
543
+ res = ha.connect("b", 5672, 1)
544
+ res.should be_true
545
+ ha.brokers.size.should == 2
546
+ ha.brokers[1].alias == "b1"
547
+ end
548
+
549
+ it "should reconnect an existing broker client after closing it if it is not connected" do
550
+ ha = RightScale::HABrokerClient.new(@serializer, :host => "a, b")
551
+ ha.brokers.size.should == 2
552
+ @broker_a.should_receive(:usable?).and_return(false)
553
+ @broker_a.should_receive(:close).and_return(true).once
554
+ flexmock(RightScale::BrokerClient).should_receive(:new).with(@identity_a, @address_a, @serializer,
555
+ @exceptions, Hash, nil, ha.brokers[0]).and_return(@broker_a).once
556
+ res = ha.connect("a", 5672, 0)
557
+ res.should be_true
558
+ ha.brokers.size.should == 2
559
+ ha.brokers[0].alias == "b0"
560
+ ha.brokers[1].alias == "b1"
561
+ end
562
+
563
+ it "should a new broker client and reconnect broker clients when an island is specified" do
564
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => {22 => @island2}, :home_island => @home)
565
+ ha.brokers.size.should == 2
566
+ ha.brokers[0].alias == "b0"
567
+ ha.brokers[1].alias == "b1"
568
+ flexmock(RightScale::BrokerClient).should_receive(:new).with(@identity2, @address2, @serializer,
569
+ @exceptions, Hash, @island1, nil).and_return(@broker2).by_default
570
+ res = ha.connect("second", 5672, 1, nil, @island1)
571
+ res.should be_true
572
+ ha.brokers.size.should == 3
573
+ ha.brokers[0].alias == "b0"
574
+ ha.brokers[1].alias == "b1"
575
+ ha.brokers[2].alias == "i0b1"
576
+ @broker3.should_receive(:usable?).and_return(false)
577
+ @broker3.should_receive(:close).and_return(true).once
578
+ flexmock(RightScale::BrokerClient).should_receive(:new).with(@identity3, @address3, @serializer,
579
+ @exceptions, Hash, @island2, ha.brokers[0]).and_return(@broker3).by_default
580
+ res = ha.connect("third", 5672, 0, nil, @island2)
581
+ res.should be_true
582
+ ha.brokers.size.should == 3
583
+ ha.brokers[0].alias == "b0"
584
+ ha.brokers[1].alias == "b1"
585
+ ha.brokers[2].alias == "i0b1"
586
+ flexmock(RightScale::BrokerClient).should_receive(:new).with(@identity1, @address1, @serializer,
587
+ @exceptions, Hash, @island1, nil).and_return(@broker1).by_default
588
+ res = ha.connect("first", 5672, 0, 0, @island1)
589
+ res.should be_true
590
+ ha.brokers.size.should == 4
591
+ ha.brokers[0].alias == "b0"
592
+ ha.brokers[1].alias == "b1"
593
+ ha.brokers[2].alias == "i0b0"
594
+ ha.brokers[3].alias == "i0b1"
595
+ end
596
+
597
+ it "should not do anything except log a message if asked to reconnect an already connected broker client" do
598
+ flexmock(RightScale::Log).should_receive(:info).with(/Ignored request to reconnect/).once
599
+ ha = RightScale::HABrokerClient.new(@serializer, :host => "a, b")
600
+ ha.brokers.size.should == 2
601
+ @broker_a.should_receive(:status).and_return(:connected).once
602
+ @broker_a.should_receive(:close).and_return(true).never
603
+ flexmock(RightScale::BrokerClient).should_receive(:new).with(@identity_a, @address_a, @serializer,
604
+ @exceptions, Hash, nil, ha.brokers[0]).and_return(@broker_a).never
605
+ res = ha.connect("a", 5672, 0)
606
+ res.should be_false
607
+ ha.brokers.size.should == 2
608
+ ha.brokers[0].alias == "b0"
609
+ ha.brokers[1].alias == "b1"
610
+ end
611
+
612
+ it "should reconnect already connected broker client if force specified" do
613
+ flexmock(RightScale::Log).should_receive(:info).with(/Ignored request to reconnect/).never
614
+ ha = RightScale::HABrokerClient.new(@serializer, :host => "a, b")
615
+ ha.brokers.size.should == 2
616
+ @broker_a.should_receive(:close).and_return(true).once
617
+ flexmock(RightScale::BrokerClient).should_receive(:new).with(@identity_a, @address_a, @serializer,
618
+ @exceptions, Hash, nil, ha.brokers[0]).and_return(@broker_a).once
619
+ res = ha.connect("a", 5672, 0, nil, nil, force = true)
620
+ res.should be_true
621
+ ha.brokers.size.should == 2
622
+ ha.brokers[0].alias == "b0"
623
+ ha.brokers[1].alias == "b1"
624
+ end
625
+
626
+ it "should slot broker client into specified priority position when at end of list" do
627
+ ha = RightScale::HABrokerClient.new(@serializer, :host => "a, b")
628
+ ha.brokers.size.should == 2
629
+ res = ha.connect("c", 5672, 2, 2)
630
+ res.should be_true
631
+ ha.brokers.size.should == 3
632
+ ha.brokers[0].alias == "b0"
633
+ ha.brokers[1].alias == "b1"
634
+ ha.brokers[2].alias == "b2"
635
+ end
636
+
637
+ it "should slot broker client into specified priority position when already is a client in that position" do
638
+ ha = RightScale::HABrokerClient.new(@serializer, :host => "a, b")
639
+ ha.brokers.size.should == 2
640
+ res = ha.connect("c", 5672, 2, 1)
641
+ res.should be_true
642
+ ha.brokers.size.should == 3
643
+ ha.brokers[0].alias == "b0"
644
+ ha.brokers[1].alias == "b2"
645
+ ha.brokers[2].alias == "b1"
646
+ end
647
+
648
+ it "should slot broker client into nex priority position if specified priority would leave a gap" do
649
+ flexmock(RightScale::Log).should_receive(:info).with(/Reduced priority setting for broker/).once
650
+ ha = RightScale::HABrokerClient.new(@serializer, :host => "a")
651
+ ha.brokers.size.should == 1
652
+ res = ha.connect("c", 5672, 2, 2)
653
+ res.should be_true
654
+ ha.brokers.size.should == 2
655
+ ha.brokers[0].alias == "b0"
656
+ ha.brokers[1].alias == "b2"
657
+ end
658
+
659
+ it "should yield to the block provided with the newly connected broker identity" do
660
+ ha = RightScale::HABrokerClient.new(@serializer, :host => "a")
661
+ ha.brokers.size.should == 1
662
+ ha.brokers[0].alias == "b0"
663
+ identity = nil
664
+ res = ha.connect("b", 5672, 1) { |i| identity = i }
665
+ res.should be_true
666
+ identity.should == @identity_b
667
+ ha.brokers.size.should == 2
668
+ ha.brokers[1].alias == "b1"
669
+ end
670
+
671
+ it "should raise an exception if try to change host and port of an existing broker client" do
672
+ ha = RightScale::HABrokerClient.new(@serializer, :host => "a, b")
673
+ lambda { ha.connect("c", 5672, 0) }.should raise_error(Exception, /Not allowed to change host or port/)
674
+ end
675
+
676
+ end # connecting
677
+
678
+ context "updating connection" do
679
+
680
+ it "should connect to any brokers for which not currently connected and return their identity" do
681
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
682
+ ha.brokers.size.should == 4
683
+ @island2.should_receive(:broker_hosts).and_return("third:0,fourth:1,fifth:2")
684
+ ha.connect_update(@islands)
685
+ ha.brokers.size.should == 5
686
+ ha.brokers[0].alias.should == "b0"
687
+ ha.brokers[1].alias.should == "b1"
688
+ ha.brokers[2].alias.should == "b2"
689
+ ha.brokers[3].alias.should == "i0b0"
690
+ ha.brokers[4].alias.should == "i0b1"
691
+ ha.instance_variable_get(:@brokers_hash)[@identity5].should == @broker5
692
+ end
693
+
694
+ it "should do nothing if there is no change" do
695
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
696
+ ha.brokers.size.should == 4
697
+ ha.connect_update(@islands)
698
+ ha.brokers.size.should == 4
699
+ ha.brokers[0].alias.should == "b0"
700
+ ha.brokers[1].alias.should == "b1"
701
+ ha.brokers[2].alias.should == "i0b0"
702
+ ha.brokers[3].alias.should == "i0b1"
703
+ end
704
+
705
+ it "should remove any broker clients for islands in which they are no longer configured" do
706
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
707
+ ha.brokers.size.should == 4
708
+ @broker1.should_receive(:close).and_return(true).once
709
+ @broker3.should_receive(:close).and_return(true).once
710
+ @island1.should_receive(:broker_hosts).and_return("second:1")
711
+ @island2.should_receive(:broker_hosts).and_return("fourth:1,fifth:2")
712
+ ha.connect_update(@islands)
713
+ ha.brokers.size.should == 3
714
+ ha.brokers[0].alias.should == "b2"
715
+ ha.brokers[1].alias.should == "b1"
716
+ ha.brokers[2].alias.should == "i0b1"
717
+ ha.instance_variable_get(:@brokers_hash)[@identity1].should be_nil
718
+ ha.instance_variable_get(:@brokers_hash)[@identity3].should be_nil
719
+ ha.instance_variable_get(:@brokers_hash)[@identity5].should == @broker5
720
+ end
721
+
722
+ end # updating connection
723
+
724
+ context "subscribing" do
725
+
726
+ it "should subscribe on all usable broker clients and return their identities" do
727
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
728
+ @broker1.should_receive(:usable?).and_return(false)
729
+ @broker1.should_receive(:subscribe).never
730
+ @broker2.should_receive(:subscribe).and_return(true).once
731
+ @broker3.should_receive(:subscribe).and_return(true).once
732
+ @broker4.should_receive(:subscribe).and_return(true).once
733
+ result = ha.subscribe({:name => "queue"}, {:type => :direct, :name => "exchange"})
734
+ result.should == [@identity3, @identity4, @identity2]
735
+ end
736
+
737
+ it "should not return the identity if subscribe fails" do
738
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
739
+ @broker1.should_receive(:usable?).and_return(false)
740
+ @broker1.should_receive(:subscribe).never
741
+ @broker2.should_receive(:subscribe).and_return(true).once
742
+ @broker3.should_receive(:subscribe).and_return(false).once
743
+ @broker4.should_receive(:subscribe).and_return(true).once
744
+ result = ha.subscribe({:name => "queue"}, {:type => :direct, :name => "exchange"})
745
+ result.should == [@identity4, @identity2]
746
+ end
747
+
748
+ it "should subscribe only on specified brokers" do
749
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
750
+ @broker1.should_receive(:usable?).and_return(false)
751
+ @broker1.should_receive(:subscribe).never
752
+ @broker2.should_receive(:subscribe).and_return(true).once
753
+ @broker3.should_receive(:subscribe).never
754
+ @broker4.should_receive(:subscribe).never
755
+ result = ha.subscribe({:name => "queue"}, {:type => :direct, :name => "exchange"},
756
+ :brokers => [@identity1, @identity2])
757
+ result.should == [@identity2]
758
+ end
759
+
760
+ end # subscribing
761
+
762
+ context "unsubscribing" do
763
+
764
+ before(:each) do
765
+ @timer = flexmock("timer", :cancel => true).by_default
766
+ flexmock(EM::Timer).should_receive(:new).and_return(@timer).by_default
767
+ @queue_name = "my_queue"
768
+ @queue = flexmock("queue", :name => @queue_name)
769
+ @queues = [@queue]
770
+ @broker1.should_receive(:queues).and_return(@queues).by_default
771
+ @broker1.should_receive(:unsubscribe).and_return(true).and_yield.by_default
772
+ @broker2.should_receive(:queues).and_return(@queues).by_default
773
+ @broker2.should_receive(:unsubscribe).and_return(true).and_yield.by_default
774
+ @broker3.should_receive(:queues).and_return(@queues).by_default
775
+ @broker3.should_receive(:unsubscribe).and_return(true).and_yield.by_default
776
+ @broker4.should_receive(:queues).and_return(@queues).by_default
777
+ @broker4.should_receive(:unsubscribe).and_return(true).and_yield.by_default
778
+ end
779
+
780
+ it "should unsubscribe from named queues on all usable broker clients" do
781
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
782
+ @broker1.should_receive(:usable?).and_return(false)
783
+ @broker1.should_receive(:unsubscribe).never
784
+ @broker2.should_receive(:unsubscribe).and_return(true).once
785
+ @broker3.should_receive(:unsubscribe).and_return(true).once
786
+ @broker4.should_receive(:unsubscribe).and_return(true).once
787
+ ha.unsubscribe([@queue_name]).should be_true
788
+ end
789
+
790
+ it "should yield to supplied block after unsubscribing" do
791
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
792
+ ha.subscribe({:name => @queue_name}, {:type => :direct, :name => "exchange"})
793
+ called = 0
794
+ ha.unsubscribe([@queue_name]) { called += 1 }
795
+ called.should == 1
796
+ end
797
+
798
+ it "should yield to supplied block if timeout before finish unsubscribing" do
799
+ flexmock(EM::Timer).should_receive(:new).with(10, Proc).and_return(@timer).and_yield.once
800
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
801
+ ha.subscribe({:name => @queue_name}, {:type => :direct, :name => "exchange"})
802
+ called = 0
803
+ ha.unsubscribe([@queue_name], 10) { called += 1 }
804
+ called.should == 1
805
+ end
806
+
807
+ it "should cancel timer if finish unsubscribing before timer fires" do
808
+ @timer.should_receive(:cancel).once
809
+ flexmock(EM::Timer).should_receive(:new).with(10, Proc).and_return(@timer).once
810
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
811
+ ha.subscribe({:name => @queue_name}, {:type => :direct, :name => "exchange"})
812
+ called = 0
813
+ ha.unsubscribe([@queue_name], 10) { called += 1 }
814
+ called.should == 1
815
+ end
816
+
817
+ it "should yield to supplied block after unsubscribing even if no queues to unsubscribe" do
818
+ @broker1.should_receive(:queues).and_return([])
819
+ @broker2.should_receive(:queues).and_return([])
820
+ @broker3.should_receive(:queues).and_return([])
821
+ @broker4.should_receive(:queues).and_return([])
822
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
823
+ called = 0
824
+ ha.unsubscribe([@queue_name]) { called += 1 }
825
+ called.should == 1
826
+ end
827
+
828
+ it "should yield to supplied block once after unsubscribing all queues" do
829
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
830
+ ha.subscribe({:name => @queue_name}, {:type => :direct, :name => "exchange"})
831
+ called = 0
832
+ ha.unsubscribe([@queue_name]) { called += 1 }
833
+ called.should == 1
834
+ end
835
+
836
+ end # unsubscribing
837
+
838
+ context "declaring" do
839
+
840
+ it "should declare exchange on all usable broker clients and return their identities" do
841
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
842
+ @broker1.should_receive(:usable?).and_return(false)
843
+ @broker1.should_receive(:declare).never
844
+ @broker2.should_receive(:declare).and_return(true).once
845
+ @broker3.should_receive(:declare).and_return(true).once
846
+ @broker4.should_receive(:declare).and_return(true).once
847
+ result = ha.declare(:exchange, "x", :durable => true)
848
+ result.should == [@identity3, @identity4, @identity2]
849
+ end
850
+
851
+ it "should not return the identity if declare fails" do
852
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
853
+ @broker1.should_receive(:usable?).and_return(false)
854
+ @broker1.should_receive(:declare).never
855
+ @broker2.should_receive(:declare).and_return(true).once
856
+ @broker3.should_receive(:declare).and_return(false).once
857
+ @broker4.should_receive(:declare).and_return(true).once
858
+ result = ha.declare(:exchange, "x", :durable => true)
859
+ result.should == [@identity4, @identity2]
860
+ end
861
+
862
+ it "should declare exchange only on specified brokers" do
863
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
864
+ @broker1.should_receive(:usable?).and_return(false)
865
+ @broker1.should_receive(:declare).never
866
+ @broker2.should_receive(:declare).and_return(true).once
867
+ @broker3.should_receive(:declare).never
868
+ @broker4.should_receive(:declare).never
869
+ result = ha.declare(:exchange, "x", :durable => true, :brokers => [@identity1, @identity2])
870
+ result.should == [@identity2]
871
+ end
872
+
873
+ end # declaring
874
+
875
+ context "publishing" do
876
+
877
+ before(:each) do
878
+ @message = flexmock("message")
879
+ @packet = flexmock("packet", :class => RightScale::Request, :to_s => true, :version => [12, 12]).by_default
880
+ @serializer.should_receive(:dump).with(@packet).and_return(@message).by_default
881
+ @broker1.should_receive(:publish).and_return(true).by_default
882
+ @broker2.should_receive(:publish).and_return(true).by_default
883
+ @broker3.should_receive(:publish).and_return(true).by_default
884
+ @broker4.should_receive(:publish).and_return(true).by_default
885
+ end
886
+
887
+ it "should serialize message, publish it, and return list of broker identifiers" do
888
+ @serializer.should_receive(:dump).with(@packet).and_return(@message).once
889
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
890
+ ha.publish({:type => :direct, :name => "exchange", :options => {:durable => true}},
891
+ @packet, :persistent => true).should == [@identity3]
892
+ end
893
+
894
+ it "should try other broker clients if a publish fails" do
895
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
896
+ @broker3.should_receive(:publish).and_return(false)
897
+ ha.publish({:type => :direct, :name => "exchange"}, @packet).should == [@identity4]
898
+ end
899
+
900
+ it "should only try to use home island brokers by default" do
901
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
902
+ @broker3.should_receive(:publish).and_return(false)
903
+ @broker4.should_receive(:publish).and_return(false)
904
+ lambda { ha.publish({:type => :direct, :name => "exchange"}, @packet) }.
905
+ should raise_error(RightScale::HABrokerClient::NoConnectedBrokers)
906
+ end
907
+
908
+ it "should publish to a randomly selected broker if random requested" do
909
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
910
+ srand(100)
911
+ ha.publish({:type => :direct, :name => "exchange"}, @packet, :order => :random,
912
+ :brokers =>[@identity1, @identity2, @identity3, @identity4]).should == [@identity2]
913
+ end
914
+
915
+ it "should publish to all connected brokers if fanout requested" do
916
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
917
+ ha.publish({:type => :direct, :name => "exchange"}, @packet, :fanout => true,
918
+ :brokers =>[@identity1, @identity2]).should == [@identity1, @identity2]
919
+ end
920
+
921
+ it "should publish only using specified brokers" do
922
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
923
+ ha.publish({:type => :direct, :name => "exchange"}, @packet,
924
+ :brokers =>[@identity1, @identity2]).should == [@identity1]
925
+ end
926
+
927
+ it "should log an error if a selected broker is unknown but still publish with any remaining brokers" do
928
+ flexmock(RightScale::Log).should_receive(:error).with(/Invalid broker identity "rs-broker-fifth-5672"/).once
929
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
930
+ ha.publish({:type => :direct, :name => "exchange"}, @packet,
931
+ :brokers =>["rs-broker-fifth-5672", @identity1]).should == [@identity1]
932
+ end
933
+
934
+ it "should raise an exception if all available brokers fail to publish" do
935
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
936
+ @broker3.should_receive(:publish).and_return(false)
937
+ @broker4.should_receive(:publish).and_return(false)
938
+ lambda { ha.publish({:type => :direct, :name => "exchange"}, @packet) }.
939
+ should raise_error(RightScale::HABrokerClient::NoConnectedBrokers)
940
+ end
941
+
942
+ it "should not serialize the message if it is already serialized" do
943
+ @serializer.should_receive(:dump).with(@packet).and_return(@message).never
944
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
945
+ ha.publish({:type => :direct, :name => "exchange"}, @packet, :no_serialize => true).should == [@identity3]
946
+ end
947
+
948
+ it "should store message info for use by message returns if :mandatory specified" do
949
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
950
+ ha.publish({:type => :direct, :name => "exchange"}, @packet, :mandatory => true).should == [@identity3]
951
+ ha.instance_variable_get(:@published).instance_variable_get(:@cache).size.should == 1
952
+ end
953
+
954
+ it "should not store message info for use by message returns if message already serialized" do
955
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
956
+ ha.publish({:type => :direct, :name => "exchange"}, @packet, :no_serialize => true).should == [@identity3]
957
+ ha.instance_variable_get(:@published).instance_variable_get(:@cache).size.should == 0
958
+ end
959
+
960
+ it "should not store message info for use by message returns if mandatory not specified" do
961
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
962
+ ha.publish({:type => :direct, :name => "exchange"}, @packet).should == [@identity3]
963
+ ha.instance_variable_get(:@published).instance_variable_get(:@cache).size.should == 0
964
+ end
965
+
966
+ end # publishing
967
+
968
+ context "returning" do
969
+
970
+ before(:each) do
971
+ @message = flexmock("message")
972
+ @packet = flexmock("packet", :class => RightScale::Request, :to_s => true, :version => [12, 12]).by_default
973
+ @serializer.should_receive(:dump).with(@packet).and_return(@message).by_default
974
+ @broker1.should_receive(:publish).and_return(true).by_default
975
+ @broker2.should_receive(:publish).and_return(true).by_default
976
+ @broker3.should_receive(:publish).and_return(true).by_default
977
+ @broker4.should_receive(:publish).and_return(true).by_default
978
+ end
979
+
980
+ it "should invoke return block" do
981
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
982
+ @broker1.should_receive(:return_message).and_yield("exchange", "NO_CONSUMERS", @message).once
983
+ called = 0
984
+ ha.return_message do |id, reason, message, to, context|
985
+ called += 1
986
+ id.should == @identity1
987
+ reason.should == "NO_CONSUMERS"
988
+ message.should == @message
989
+ to.should == "exchange"
990
+ end
991
+ called.should == 1
992
+ end
993
+
994
+ it "should record failure in message context if there is message context" do
995
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
996
+ ha.publish({:type => :direct, :name => "exchange", :options => {:durable => true}},
997
+ @packet, :mandatory => true).should == [@identity3]
998
+ @broker3.should_receive(:return_message).and_yield("exchange", "NO_CONSUMERS", @message).once
999
+ ha.return_message do |id, reason, message, to, context|
1000
+ id.should == @identity3
1001
+ reason.should == "NO_CONSUMERS"
1002
+ message.should == @message
1003
+ to.should == "exchange"
1004
+ end
1005
+ ha.instance_variable_get(:@published).fetch(@message).failed.should == [@identity3]
1006
+ end
1007
+
1008
+ context "when non-delivery" do
1009
+
1010
+ it "should store non-delivery block for use by return handler" do
1011
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1012
+ non_delivery = lambda {}
1013
+ ha.non_delivery(&non_delivery)
1014
+ ha.instance_variable_get(:@non_delivery).should == non_delivery
1015
+ end
1016
+
1017
+ end
1018
+
1019
+ context "when handling return" do
1020
+
1021
+ before(:each) do
1022
+ @options = {}
1023
+ @brokers = [@identity3, @identity4]
1024
+ @context = RightScale::HABrokerClient::Context.new(@packet, @options, @brokers)
1025
+ end
1026
+
1027
+ it "should republish using a broker not yet tried if possible and log that re-routing" do
1028
+ flexmock(RightScale::Log).should_receive(:info).with(/RE-ROUTE/).once
1029
+ flexmock(RightScale::Log).should_receive(:info).with(/RETURN reason/).once
1030
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1031
+ @context.record_failure(@identity3)
1032
+ @broker4.should_receive(:publish).and_return(true).once
1033
+ ha.__send__(:handle_return, @identity3, "reason", @message, "to", @context)
1034
+ end
1035
+
1036
+ it "should republish to same broker without mandatory if message is persistent and no other brokers available" do
1037
+ flexmock(RightScale::Log).should_receive(:info).with(/RE-ROUTE/).once
1038
+ flexmock(RightScale::Log).should_receive(:info).with(/RETURN reason/).once
1039
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1040
+ @context.record_failure(@identity3)
1041
+ @context.record_failure(@identity4)
1042
+ @packet.should_receive(:persistent).and_return(true)
1043
+ @broker3.should_receive(:publish).and_return(true).once
1044
+ ha.__send__(:handle_return, @identity4, "NO_CONSUMERS", @message, "to", @context)
1045
+ end
1046
+
1047
+ it "should republish to same broker without mandatory if message is one-way and no other brokers available" do
1048
+ flexmock(RightScale::Log).should_receive(:info).with(/RE-ROUTE/).once
1049
+ flexmock(RightScale::Log).should_receive(:info).with(/RETURN reason/).once
1050
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1051
+ @context.record_failure(@identity3)
1052
+ @context.record_failure(@identity4)
1053
+ @packet.should_receive(:one_way).and_return(true)
1054
+ @broker3.should_receive(:publish).and_return(true).once
1055
+ ha.__send__(:handle_return, @identity4, "NO_CONSUMERS", @message, "to", @context)
1056
+ end
1057
+
1058
+ it "should update status to :stopping if message returned because access refused" do
1059
+ flexmock(RightScale::Log).should_receive(:info).with(/RE-ROUTE/).once
1060
+ flexmock(RightScale::Log).should_receive(:info).with(/RETURN reason/).once
1061
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1062
+ @context.record_failure(@identity3)
1063
+ @broker4.should_receive(:publish).and_return(true).once
1064
+ @broker3.should_receive(:update_status).with(:stopping).and_return(true).once
1065
+ ha.__send__(:handle_return, @identity3, "ACCESS_REFUSED", @message, "to", @context)
1066
+ end
1067
+
1068
+ it "should log info and make non-delivery call even if persistent when returned because of no queue" do
1069
+ flexmock(RightScale::Log).should_receive(:info).with(/NO ROUTE/).once
1070
+ flexmock(RightScale::Log).should_receive(:info).with(/RETURN reason/).once
1071
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1072
+ called = 0
1073
+ ha.non_delivery { |reason, type, token, from, to| called += 1 }
1074
+ @context.record_failure(@identity3)
1075
+ @context.record_failure(@identity4)
1076
+ @packet.should_receive(:persistent).and_return(true)
1077
+ @broker3.should_receive(:publish).and_return(true).never
1078
+ @broker4.should_receive(:publish).and_return(true).never
1079
+ ha.__send__(:handle_return, @identity4, "NO_QUEUE", @message, "to", @context)
1080
+ called.should == 1
1081
+ end
1082
+
1083
+ it "should log info and make non-delivery call if no route can be found" do
1084
+ flexmock(RightScale::Log).should_receive(:info).with(/NO ROUTE/).once
1085
+ flexmock(RightScale::Log).should_receive(:info).with(/RETURN reason/).once
1086
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1087
+ called = 0
1088
+ ha.non_delivery { |reason, type, token, from, to| called += 1 }
1089
+ @context.record_failure(@identity3)
1090
+ @context.record_failure(@identity4)
1091
+ @broker3.should_receive(:publish).and_return(true).never
1092
+ @broker4.should_receive(:publish).and_return(true).never
1093
+ ha.__send__(:handle_return, @identity4, "any reason", @message, "to", @context)
1094
+ called.should == 1
1095
+ end
1096
+
1097
+ it "should log info if no message context available for re-routing it" do
1098
+ flexmock(RightScale::Log).should_receive(:info).with(/Dropping/).once
1099
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1100
+ ha.__send__(:handle_return, @identity4, "any reason", @message, "to", nil)
1101
+ end
1102
+
1103
+ end
1104
+
1105
+ end # returning
1106
+
1107
+ context "deleting" do
1108
+
1109
+ it "should delete queue on all usable broker clients and return their identities" do
1110
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1111
+ @broker1.should_receive(:usable?).and_return(false)
1112
+ @broker1.should_receive(:delete).never
1113
+ @broker2.should_receive(:delete).and_return(true).once
1114
+ @broker3.should_receive(:delete).and_return(true).once
1115
+ @broker4.should_receive(:delete).and_return(true).once
1116
+ ha.delete("queue").should == [@identity3, @identity4, @identity2]
1117
+ end
1118
+
1119
+ it "should not return the identity if delete fails" do
1120
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1121
+ @broker1.should_receive(:usable?).and_return(false)
1122
+ @broker1.should_receive(:delete).never
1123
+ @broker2.should_receive(:delete).and_return(true).once
1124
+ @broker3.should_receive(:delete).and_return(false).once
1125
+ @broker4.should_receive(:delete).and_return(true).once
1126
+ ha.delete("queue").should == [@identity4, @identity2]
1127
+ end
1128
+
1129
+ end # deleting
1130
+
1131
+ context "removing" do
1132
+
1133
+ it "should remove broker client after disconnecting and pass identity to block" do
1134
+ flexmock(RightScale::Log).should_receive(:info).with(/Removing/).once
1135
+ @broker2.should_receive(:close).with(true, true, false).once
1136
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1137
+ identity = nil
1138
+ result = ha.remove("second", 5672) { |i| identity = i }
1139
+ result.should == @identity2
1140
+ identity.should == @identity2
1141
+ ha.get(@identity2).should be_nil
1142
+ ha.get(@identity1).should_not be_nil
1143
+ ha.get(@identity3).should_not be_nil
1144
+ ha.get("rs-broker-fourth-5672").should_not be_nil
1145
+ ha.brokers.size.should == 3
1146
+ end
1147
+
1148
+ it "should remove broker when no block supplied but still return a result" do
1149
+ flexmock(RightScale::Log).should_receive(:info).with(/Removing/).once
1150
+ @broker2.should_receive(:close).once
1151
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1152
+ result = ha.remove("second", 5672)
1153
+ result.should == @identity2
1154
+ ha.get(@identity2).should be_nil
1155
+ ha.get(@identity1).should_not be_nil
1156
+ ha.get(@identity3).should_not be_nil
1157
+ ha.get(@identity4).should_not be_nil
1158
+ ha.brokers.size.should == 3
1159
+ end
1160
+
1161
+ it "should remove last broker if requested" do
1162
+ flexmock(RightScale::Log).should_receive(:info).with(/Removing/).times(4)
1163
+ @broker1.should_receive(:close).once
1164
+ @broker2.should_receive(:close).once
1165
+ @broker3.should_receive(:close).once
1166
+ @broker4.should_receive(:close).once
1167
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1168
+ result = ha.remove("second", 5672)
1169
+ result.should == @identity2
1170
+ ha.get(@identity2).should be_nil
1171
+ result = ha.remove("third", 5672)
1172
+ result.should == @identity3
1173
+ ha.get(@identity3).should be_nil
1174
+ result = ha.remove("fourth", 5672)
1175
+ result.should == @identity4
1176
+ ha.get(@identity4).should be_nil
1177
+ ha.brokers.size.should == 1
1178
+ identity = nil
1179
+ result = ha.remove("first", 5672) { |i| identity = i }
1180
+ result.should == @identity1
1181
+ identity.should == @identity1
1182
+ ha.get(@identity1).should be_nil
1183
+ ha.brokers.size.should == 0
1184
+ end
1185
+
1186
+ it "should return nil and not execute block if broker is unknown" do
1187
+ flexmock(RightScale::Log).should_receive(:info).with(/Ignored request to remove/).once
1188
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1189
+ ha.remove("fifth", 5672).should be_nil
1190
+ ha.brokers.size.should == 4
1191
+ end
1192
+
1193
+ it "should close connection and mark as failed when told broker is not usable" do
1194
+ @broker2.should_receive(:close).with(true, false, false).once
1195
+ @broker3.should_receive(:close).with(true, false, false).once
1196
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1197
+ result = ha.declare_unusable([@identity2, @identity3])
1198
+ ha.brokers.size.should == 4
1199
+ end
1200
+
1201
+ it "should raise an exception if broker that is declared not usable is unknown" do
1202
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1203
+ lambda { ha.declare_unusable(["rs-broker-fifth-5672"]) }.should raise_error(Exception, /Cannot mark unknown/)
1204
+ ha.brokers.size.should == 4
1205
+ end
1206
+
1207
+ end # removing
1208
+
1209
+ context "monitoring" do
1210
+
1211
+ include RightScale::StatsHelper
1212
+
1213
+ before(:each) do
1214
+ @timer = flexmock("timer")
1215
+ flexmock(EM::Timer).should_receive(:new).and_return(@timer).by_default
1216
+ @timer.should_receive(:cancel).by_default
1217
+ @identity = "rs-broker-localhost-5672"
1218
+ @address = {:host => "localhost", :port => 5672, :index => 0}
1219
+ @broker = flexmock("broker_client", :identity => @identity, :alias => "b0", :host => "localhost",
1220
+ :port => 5672, :index => 0, :island_id => nil, :in_home_island => true)
1221
+ @broker.should_receive(:status).and_return(:connected).by_default
1222
+ @broker.should_receive(:usable?).and_return(true).by_default
1223
+ @broker.should_receive(:connected?).and_return(true).by_default
1224
+ @broker.should_receive(:subscribe).and_return(true).by_default
1225
+ @broker.should_receive(:return_message).and_return(true).by_default
1226
+ flexmock(RightScale::BrokerClient).should_receive(:new).and_return(@broker).by_default
1227
+ @broker1.should_receive(:failed?).and_return(false).by_default
1228
+ @broker2.should_receive(:failed?).and_return(false).by_default
1229
+ @broker3.should_receive(:failed?).and_return(false).by_default
1230
+ @broker4.should_receive(:failed?).and_return(false).by_default
1231
+ end
1232
+
1233
+ it "should give access to or list usable brokers" do
1234
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1235
+ aliases = []
1236
+ res = ha.__send__(:each_usable) { |b| aliases << b.alias }
1237
+ aliases.should == ["b0", "b1", "i0b0", "i0b1"]
1238
+ res.size.should == 4
1239
+ res[0].alias.should == "b0"
1240
+ res[1].alias.should == "b1"
1241
+ res[2].alias.should == "i0b0"
1242
+ res[3].alias.should == "i0b1"
1243
+
1244
+ @broker1.should_receive(:usable?).and_return(true)
1245
+ @broker2.should_receive(:usable?).and_return(false)
1246
+ @broker3.should_receive(:usable?).and_return(false)
1247
+ @broker4.should_receive(:usable?).and_return(false)
1248
+ aliases = []
1249
+ res = ha.__send__(:each_usable) { |b| aliases << b.alias }
1250
+ aliases.should == ["i0b0"]
1251
+ res.size.should == 1
1252
+ res[0].alias.should == "i0b0"
1253
+ end
1254
+
1255
+ it "should give list of unusable brokers" do
1256
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1257
+ @broker1.should_receive(:usable?).and_return(true)
1258
+ @broker2.should_receive(:usable?).and_return(false)
1259
+ @broker3.should_receive(:usable?).and_return(false)
1260
+ @broker4.should_receive(:usable?).and_return(true)
1261
+ ha.unusable.should == [@identity3, @identity2]
1262
+ end
1263
+
1264
+ it "should give access to each selected usable broker" do
1265
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1266
+ @broker2.should_receive(:usable?).and_return(true)
1267
+ @broker3.should_receive(:usable?).and_return(false)
1268
+ aliases = []
1269
+ res = ha.__send__(:each_usable, [@identity2, @identity3]) { |b| aliases << b.alias }
1270
+ aliases.should == ["i0b1"]
1271
+ res.size.should == 1
1272
+ res[0].alias.should == "i0b1"
1273
+ end
1274
+
1275
+ it "should tell whether a broker is connected" do
1276
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1277
+ @broker2.should_receive(:connected?).and_return(false)
1278
+ @broker3.should_receive(:connected?).and_return(true)
1279
+ ha.connected?(@identity2).should be_false
1280
+ ha.connected?(@identity3).should be_true
1281
+ ha.connected?("rs-broker-fifth-5672").should be_nil
1282
+ end
1283
+
1284
+ it "should give list of connected brokers for home island by default" do
1285
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1286
+ @broker1.should_receive(:connected?).and_return(true)
1287
+ @broker2.should_receive(:connected?).and_return(false)
1288
+ @broker3.should_receive(:connected?).and_return(true)
1289
+ @broker4.should_receive(:connected?).and_return(false)
1290
+ ha.connected.should == [@identity3]
1291
+ end
1292
+
1293
+ it "should give list of connected brokers for a specific island" do
1294
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1295
+ @broker1.should_receive(:connected?).and_return(true)
1296
+ @broker2.should_receive(:connected?).and_return(false)
1297
+ @broker3.should_receive(:connected?).and_return(true)
1298
+ @broker4.should_receive(:connected?).and_return(false)
1299
+ ha.connected(11).should == [@identity1]
1300
+ ha.connected(22).should == [@identity3]
1301
+ end
1302
+
1303
+ it "should give list of all brokers" do
1304
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1305
+ ha.all.should == [@identity3, @identity4, @identity1, @identity2]
1306
+ end
1307
+
1308
+ it "should give list of failed brokers" do
1309
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1310
+ @broker1.should_receive(:failed?).and_return(true)
1311
+ @broker2.should_receive(:failed?).and_return(false)
1312
+ @broker3.should_receive(:failed?).and_return(true)
1313
+ @broker4.should_receive(:failed?).and_return(false)
1314
+ ha.failed.should == [@identity3, @identity1]
1315
+ end
1316
+
1317
+ it "should give broker client status list" do
1318
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1319
+ @broker1.should_receive(:summary).and_return("summary1")
1320
+ @broker2.should_receive(:summary).and_return("summary2")
1321
+ @broker3.should_receive(:summary).and_return("summary3")
1322
+ @broker4.should_receive(:summary).and_return("summary4")
1323
+ ha.status.should == ["summary3", "summary4", "summary1", "summary2"]
1324
+ end
1325
+
1326
+ it "should give broker client statistics" do
1327
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1328
+ @broker1.should_receive(:stats).and_return("stats1")
1329
+ @broker2.should_receive(:stats).and_return("stats2")
1330
+ @broker3.should_receive(:stats).and_return("stats3")
1331
+ @broker4.should_receive(:stats).and_return("stats4")
1332
+ ha.stats.should == {"brokers" => ["stats3", "stats4", "stats1", "stats2"],
1333
+ "exceptions" => nil,
1334
+ "returns" => nil}
1335
+ end
1336
+
1337
+ it "should log broker client status update if there is a change" do
1338
+ flexmock(RightScale::Log).should_receive(:info).with(/Broker b0 is now connected/).once
1339
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1340
+ ha.__send__(:update_status, @broker3, false)
1341
+ end
1342
+
1343
+ it "should not log broker client status update if there is no change" do
1344
+ flexmock(RightScale::Log).should_receive(:info).with(/Broker b0 is now connected/).never
1345
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1346
+ ha.__send__(:update_status, @broker3, true)
1347
+ end
1348
+
1349
+ it "should log broker client status update when become disconnected" do
1350
+ flexmock(RightScale::Log).should_receive(:info).with(/Broker b0 is now disconnected/).once
1351
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1352
+ @broker3.should_receive(:status).and_return(:disconnected)
1353
+ @broker3.should_receive(:connected?).and_return(false)
1354
+ ha.__send__(:update_status, @broker3, true)
1355
+ end
1356
+
1357
+ it "should provide connection status callback when cross 0/1 connection boundary for home island" do
1358
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1359
+ connected = 0
1360
+ disconnected = 0
1361
+ ha.connection_status do |status|
1362
+ if status == :connected
1363
+ (ha.brokers[0].status == :connected ||
1364
+ ha.brokers[1].status == :connected).should be_true
1365
+ connected += 1
1366
+ elsif status == :disconnected
1367
+ (ha.brokers[0].status == :disconnected &&
1368
+ ha.brokers[1].status == :disconnected).should be_true
1369
+ disconnected += 1
1370
+ end
1371
+ end
1372
+ ha.__send__(:update_status, @broker3, false)
1373
+ connected.should == 0
1374
+ disconnected.should == 0
1375
+ @broker3.should_receive(:status).and_return(:disconnected)
1376
+ @broker3.should_receive(:connected?).and_return(false)
1377
+ ha.__send__(:update_status, @broker3, true)
1378
+ connected.should == 0
1379
+ disconnected.should == 0
1380
+ @broker4.should_receive(:status).and_return(:disconnected)
1381
+ @broker4.should_receive(:connected?).and_return(false)
1382
+ ha.__send__(:update_status, @broker4, true)
1383
+ connected.should == 0
1384
+ disconnected.should == 1
1385
+ # TODO fix this test so that also checks crossing boundary as become connected
1386
+ end
1387
+
1388
+ it "should provide connection status callback when cross n/n-1 connection boundary for home island when all specified" do
1389
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1390
+ connected = 0
1391
+ disconnected = 0
1392
+ ha.connection_status(:boundary => :all) do |status|
1393
+ if status == :connected
1394
+ (ha.brokers[0].status == :connected &&
1395
+ ha.brokers[1].status == :connected).should be_true
1396
+ connected += 1
1397
+ elsif status == :disconnected
1398
+ (ha.brokers[0].status == :disconnected ||
1399
+ ha.brokers[1].status == :disconnected).should be_true
1400
+ disconnected += 1
1401
+ end
1402
+ end
1403
+ ha.__send__(:update_status, @broker3, false)
1404
+ connected.should == 1
1405
+ disconnected.should == 0
1406
+ @broker3.should_receive(:status).and_return(:disconnected)
1407
+ @broker3.should_receive(:connected?).and_return(false)
1408
+ ha.__send__(:update_status, @broker3, true)
1409
+ connected.should == 1
1410
+ disconnected.should == 1
1411
+ @broker4.should_receive(:status).and_return(:disconnected)
1412
+ @broker4.should_receive(:connected?).and_return(false)
1413
+ ha.__send__(:update_status, @broker4, true)
1414
+ connected.should == 1
1415
+ disconnected.should == 1
1416
+ # TODO fix this test so that also checks crossing boundary as become disconnected
1417
+ end
1418
+
1419
+ it "should provide connection status callback when cross connection boundary for non-home island" do
1420
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1421
+ @broker1.should_receive(:island_alias).and_return(:i0)
1422
+ @broker2.should_receive(:island_alias).and_return(:i0)
1423
+ connected = 0
1424
+ disconnected = 0
1425
+ ha.connection_status do |status|
1426
+ if status == :connected
1427
+ (ha.brokers[2].status == :connected ||
1428
+ ha.brokers[3].status == :connected).should be_true
1429
+ connected += 1
1430
+ elsif status == :disconnected
1431
+ (ha.brokers[2].status == :disconnected &&
1432
+ ha.brokers[3].status == :disconnected).should be_true
1433
+ disconnected += 1
1434
+ end
1435
+ end
1436
+ ha.__send__(:update_status, @broker1, false)
1437
+ connected.should == 0
1438
+ disconnected.should == 0
1439
+ @broker1.should_receive(:status).and_return(:disconnected)
1440
+ @broker1.should_receive(:connected?).and_return(false)
1441
+ ha.__send__(:update_status, @broker1, true)
1442
+ connected.should == 0
1443
+ disconnected.should == 0
1444
+ @broker2.should_receive(:status).and_return(:disconnected)
1445
+ @broker2.should_receive(:connected?).and_return(false)
1446
+ ha.__send__(:update_status, @broker2, true)
1447
+ connected.should == 0
1448
+ disconnected.should == 1
1449
+ end
1450
+
1451
+ it "should provide connection status callback for specific broker set" do
1452
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1453
+ @broker1.should_receive(:island_alias).and_return(:i0)
1454
+ @broker2.should_receive(:island_alias).and_return(:i0)
1455
+ connected = 0
1456
+ disconnected = 0
1457
+ ha.connection_status(:brokers => [@identity1, @identity2]) do |status|
1458
+ if status == :connected
1459
+ (ha.brokers[2].status == :connected ||
1460
+ ha.brokers[3].status == :connected).should be_true
1461
+ connected += 1
1462
+ elsif status == :disconnected
1463
+ (ha.brokers[2].status == :disconnected &&
1464
+ ha.brokers[3].status == :disconnected).should be_true
1465
+ disconnected += 1
1466
+ end
1467
+ end
1468
+ ha.__send__(:update_status, @broker1, false)
1469
+ connected.should == 0
1470
+ disconnected.should == 0
1471
+ @broker1.should_receive(:status).and_return(:disconnected)
1472
+ @broker1.should_receive(:connected?).and_return(false)
1473
+ ha.__send__(:update_status, @broker1, true)
1474
+ connected.should == 0
1475
+ disconnected.should == 0
1476
+ @broker2.should_receive(:status).and_return(:disconnected)
1477
+ @broker2.should_receive(:connected?).and_return(false)
1478
+ ha.__send__(:update_status, @broker2, true)
1479
+ connected.should == 0
1480
+ disconnected.should == 1
1481
+ end
1482
+
1483
+ it "should provide connection status callback only once when one-off is requested" do
1484
+ flexmock(RightScale::BrokerClient).should_receive(:new).with(@identity, @address, @serializer,
1485
+ @exceptions, Hash, nil, nil).and_return(@broker).once
1486
+ ha = RightScale::HABrokerClient.new(@serializer)
1487
+ called = 0
1488
+ ha.connection_status(:one_off => 10) { |_| called += 1 }
1489
+ ha.__send__(:update_status, @broker, false)
1490
+ called.should == 1
1491
+ @broker.should_receive(:status).and_return(:disconnected)
1492
+ @broker.should_receive(:connected?).and_return(false)
1493
+ ha.__send__(:update_status, @broker, true)
1494
+ called.should == 1
1495
+ end
1496
+
1497
+ it "should use connection status timer when one-off is requested" do
1498
+ flexmock(EM::Timer).should_receive(:new).and_return(@timer).once
1499
+ @timer.should_receive(:cancel).once
1500
+ flexmock(RightScale::BrokerClient).should_receive(:new).with(@identity, @address, @serializer,
1501
+ @exceptions, Hash, nil, nil).and_return(@broker).once
1502
+ ha = RightScale::HABrokerClient.new(@serializer)
1503
+ called = 0
1504
+ ha.connection_status(:one_off => 10) { |_| called += 1 }
1505
+ ha.__send__(:update_status, @broker, false)
1506
+ called.should == 1
1507
+ end
1508
+
1509
+ it "should give timeout connection status if one-off request times out" do
1510
+ flexmock(EM::Timer).should_receive(:new).and_return(@timer).and_yield.once
1511
+ @timer.should_receive(:cancel).never
1512
+ flexmock(RightScale::BrokerClient).should_receive(:new).with(@identity, @address, @serializer,
1513
+ @exceptions, Hash, nil, nil).and_return(@broker).once
1514
+ ha = RightScale::HABrokerClient.new(@serializer)
1515
+ called = 0
1516
+ ha.connection_status(:one_off => 10) { |status| called += 1; status.should == :timeout }
1517
+ called.should == 1
1518
+ end
1519
+
1520
+ it "should be able to have multiple connection status callbacks" do
1521
+ flexmock(RightScale::BrokerClient).should_receive(:new).with(@identity, @address, @serializer,
1522
+ @exceptions, Hash, nil, nil).and_return(@broker).once
1523
+ ha = RightScale::HABrokerClient.new(@serializer)
1524
+ called1 = 0
1525
+ called2 = 0
1526
+ ha.connection_status(:one_off => 10) { |_| called1 += 1 }
1527
+ ha.connection_status(:boundary => :all) { |_| called2 += 1 }
1528
+ ha.__send__(:update_status, @broker, false)
1529
+ @broker.should_receive(:status).and_return(:disconnected)
1530
+ @broker.should_receive(:connected?).and_return(false)
1531
+ ha.__send__(:update_status, @broker, true)
1532
+ called1.should == 1
1533
+ called2.should == 2
1534
+ end
1535
+
1536
+ it "should provide failed connection status callback when all brokers fail to connect" do
1537
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1538
+ connected = disconnected = failed = 0
1539
+ ha.connection_status(:boundary => :all) do |status|
1540
+ if status == :connected
1541
+ connected += 1
1542
+ elsif status == :disconnected
1543
+ disconnected += 1
1544
+ elsif status == :failed
1545
+ (ha.brokers[0].failed? &&
1546
+ ha.brokers[1].failed? &&
1547
+ ha.brokers[2].failed? &&
1548
+ ha.brokers[3].failed?).should be_true
1549
+ failed += 1
1550
+ end
1551
+ end
1552
+ @broker1.should_receive(:failed?).and_return(true)
1553
+ @broker1.should_receive(:connected?).and_return(false)
1554
+ ha.__send__(:update_status, @broker1, false)
1555
+ connected.should == 0
1556
+ disconnected.should == 0
1557
+ failed.should == 0
1558
+ @broker2.should_receive(:failed?).and_return(true)
1559
+ @broker2.should_receive(:connected?).and_return(false)
1560
+ ha.__send__(:update_status, @broker2, false)
1561
+ connected.should == 0
1562
+ disconnected.should == 0
1563
+ failed.should == 0
1564
+ @broker3.should_receive(:failed?).and_return(true)
1565
+ @broker3.should_receive(:connected?).and_return(false)
1566
+ ha.__send__(:update_status, @broker3, false)
1567
+ connected.should == 0
1568
+ disconnected.should == 0
1569
+ failed.should == 0
1570
+ @broker4.should_receive(:failed?).and_return(true)
1571
+ @broker4.should_receive(:connected?).and_return(false)
1572
+ ha.__send__(:update_status, @broker4, false)
1573
+ connected.should == 0
1574
+ disconnected.should == 0
1575
+ failed.should == 1
1576
+ end
1577
+
1578
+ it "should provide failed connection status callback when brokers selected and all brokers fail to connect" do
1579
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1580
+ connected = disconnected = failed = 0
1581
+ ha.connection_status(:boundary => :all, :brokers => [@broker3.identity, @broker4.identity]) do |status|
1582
+ if status == :connected
1583
+ connected += 1
1584
+ elsif status == :disconnected
1585
+ disconnected += 1
1586
+ elsif status == :failed
1587
+ (ha.brokers[0].failed? &&
1588
+ ha.brokers[1].failed?).should be_true
1589
+ failed += 1
1590
+ end
1591
+ end
1592
+ @broker1.should_receive(:failed?).and_return(true)
1593
+ @broker2.should_receive(:failed?).and_return(true)
1594
+ @broker3.should_receive(:failed?).and_return(true)
1595
+ @broker3.should_receive(:connected?).and_return(false)
1596
+ ha.__send__(:update_status, @broker3, false)
1597
+ connected.should == 0
1598
+ disconnected.should == 0
1599
+ failed.should == 0
1600
+ @broker4.should_receive(:failed?).and_return(true)
1601
+ @broker4.should_receive(:connected?).and_return(false)
1602
+ ha.__send__(:update_status, @broker4, false)
1603
+ connected.should == 0
1604
+ disconnected.should == 0
1605
+ failed.should == 1
1606
+ end
1607
+
1608
+ end # monitoring
1609
+
1610
+ context "closing" do
1611
+
1612
+ it "should close all broker connections and execute block after all connections are closed" do
1613
+ @broker1.should_receive(:close).with(false, Proc).and_return(true).and_yield.once
1614
+ @broker2.should_receive(:close).with(false, Proc).and_return(true).and_yield.once
1615
+ @broker3.should_receive(:close).with(false, Proc).and_return(true).and_yield.once
1616
+ @broker4.should_receive(:close).with(false, Proc).and_return(true).and_yield.once
1617
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1618
+ called = 0
1619
+ ha.close { called += 1 }
1620
+ called.should == 1
1621
+ end
1622
+
1623
+ it "should close broker connections when no block supplied" do
1624
+ @broker1.should_receive(:close).with(false, Proc).and_return(true).and_yield.once
1625
+ @broker2.should_receive(:close).with(false, Proc).and_return(true).and_yield.once
1626
+ @broker3.should_receive(:close).with(false, Proc).and_return(true).and_yield.once
1627
+ @broker4.should_receive(:close).with(false, Proc).and_return(true).and_yield.once
1628
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1629
+ ha.close
1630
+ end
1631
+
1632
+ it "should close all broker connections even if encounter an exception" do
1633
+ flexmock(RightScale::Log).should_receive(:error).with(/Failed to close/, Exception, :trace).once
1634
+ @broker1.should_receive(:close).and_return(true).and_yield.once
1635
+ @broker2.should_receive(:close).and_raise(Exception).once
1636
+ @broker3.should_receive(:close).and_return(true).and_yield.once
1637
+ @broker4.should_receive(:close).and_return(true).and_yield.once
1638
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1639
+ called = 0
1640
+ ha.close { called += 1 }
1641
+ called.should == 1
1642
+ end
1643
+
1644
+ it "should close an individual broker connection" do
1645
+ @broker1.should_receive(:close).with(true).and_return(true).once
1646
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1647
+ ha.close_one(@identity1)
1648
+ end
1649
+
1650
+ it "should not propagate connection status change if requested not to" do
1651
+ @broker1.should_receive(:close).with(false).and_return(true).once
1652
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1653
+ ha.close_one(@identity1, propagate = false)
1654
+ end
1655
+
1656
+ it "should close an individual broker connection and execute block if given" do
1657
+ @broker1.should_receive(:close).with(true, Proc).and_return(true).and_yield.once
1658
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1659
+ called = 0
1660
+ ha.close_one(@identity1) { called += 1 }
1661
+ called.should == 1
1662
+ end
1663
+
1664
+ it "should raise exception if unknown broker" do
1665
+ ha = RightScale::HABrokerClient.new(@serializer, :islands => @islands, :home_island => @home)
1666
+ lambda { ha.close_one("rs-broker-fifth-5672") }.should raise_error(Exception, /Cannot close unknown broker/)
1667
+ end
1668
+
1669
+ end # closing
1670
+
1671
+ end # when
1672
+
1673
+ end # RightScale::HABrokerClient