right_infrastructure_agent 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,54 @@
1
+ # -*-ruby-*-
2
+ # Copyright: Copyright (c) 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 NONINFRINGEMENT.
18
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require 'rubygems'
24
+
25
+ Gem::Specification.new do |spec|
26
+ spec.name = 'right_infrastructure_agent'
27
+ spec.version = '1.1.2'
28
+ spec.date = '2014-07-24'
29
+ spec.authors = ['Lee Kirchhoff', 'Raphael Simon']
30
+ spec.email = 'lee@rightscale.com'
31
+ spec.homepage = 'https://github.com/rightscale/right_infrastructure_agent'
32
+ spec.platform = Gem::Platform::RUBY
33
+ spec.summary = 'RightAgent extension for use by RightScale infrastructure servers'
34
+ spec.has_rdoc = true
35
+ spec.rdoc_options = ["--main", "README.rdoc", "--title", "RightInfrastructureAgent"]
36
+ spec.extra_rdoc_files = ["README.rdoc"]
37
+ spec.required_ruby_version = '>= 1.8.7'
38
+ spec.require_path = 'lib'
39
+
40
+ spec.add_dependency('right_support', '~> 2.1')
41
+ spec.add_dependency('right_amqp', '~> 0.4')
42
+ spec.add_dependency('right_agent', '~> 2.0')
43
+
44
+ spec.description = <<-EOF
45
+ RightInfrastructureAgent provides the foundation for RightScale infrastructure
46
+ servers that connect into the RightScale system via the RabbitMQ message bus.
47
+ It extends RightAgent in configuration, monitoring, and packet handling areas
48
+ as needed generically by infrastructure servers.
49
+ EOF
50
+
51
+ candidates = Dir.glob("{lib,spec}/**/*") +
52
+ ["LICENSE", "README.rdoc", "Rakefile", "right_infrastructure_agent.gemspec"]
53
+ spec.files = candidates.sort
54
+ end
@@ -0,0 +1,305 @@
1
+ #--
2
+ # Copyright (c) 2011-2013 RightScale, Inc, All Rights Reserved Worldwide.
3
+ #
4
+ # THIS PROGRAM IS CONFIDENTIAL AND PROPRIETARY TO RIGHTSCALE
5
+ # AND CONSTITUTES A VALUABLE TRADE SECRET. Any unauthorized use,
6
+ # reproduction, modification, or disclosure of this program is
7
+ # strictly prohibited. Any use of this program by an authorized
8
+ # licensee is strictly subject to the terms and conditions,
9
+ # including confidentiality obligations, set forth in the applicable
10
+ # License Agreement between RightScale.com, Inc. and the licensee.
11
+ #++
12
+
13
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
14
+
15
+ describe RightScale::GlobalObjectReplicatorSink do
16
+
17
+ include FlexMock::ArgumentTypes
18
+
19
+ class SystemConfig; end
20
+
21
+ module RightSharding
22
+ module ShardHandler
23
+ def self.fetch_this_shard_id(system_config)
24
+ 9
25
+ end
26
+ end
27
+ end
28
+
29
+ # A pseudo HTTP controller applying GlobalObjectReplicatorSink
30
+ class GlobalObjectReplicatorSinkController
31
+ include RightScale::GlobalObjectReplicatorSink
32
+
33
+ attr_reader :logger
34
+ attr_reader :params
35
+
36
+ def initialize(params = {})
37
+ @params = params
38
+ @logger = RightScale::Log
39
+ end
40
+
41
+ def global_object_replicator_sink
42
+ "core"
43
+ end
44
+
45
+ def render(hash)
46
+ nil
47
+ end
48
+ end
49
+
50
+ class FakeGlobalSinkObject
51
+
52
+ attr_accessor :id, :name, :foo, :global_object_version
53
+
54
+ def initialize
55
+ @id = 71
56
+ @name = "fake"
57
+ @foo = "bar"
58
+ @global_object_version = 999
59
+ self.class.store(self)
60
+ end
61
+
62
+ def attributes
63
+ {"name" => @name, "foo" => @foo}
64
+ end
65
+
66
+ def self.will_replicate_sink?
67
+ true
68
+ end
69
+
70
+ def self.will_replicate_current_schema_version
71
+ 3
72
+ end
73
+
74
+ def self.calculate_global_object_checksum(checksum_type, begin_id = nil, end_id = nil)
75
+ 123
76
+ end
77
+
78
+ def self.will_replicate_initialization_chunk_size
79
+ 2
80
+ end
81
+
82
+ def self.max_id
83
+ 101
84
+ end
85
+
86
+ def self.handle_global_object_change(id, schema_version, global_object_version, attrs)
87
+ object = (@objects ||= {})[id]
88
+ raise ArgumentError("Invalid id: #{id}") unless object
89
+ raise ArgumentError("Invalid schema verson: #{schema_version}") unless schema_version == FakeGlobalSinkObject.will_replicate_current_schema_version
90
+ object.global_object_version = global_object_version
91
+ YAML.load(attrs).each { |name, value| object.instance_variable_set(eval(":@#{name}"), value) }
92
+ end
93
+
94
+ def self.store(object)
95
+ (@objects ||= {})[object.id] = object
96
+ end
97
+
98
+ def self.purge
99
+ @objects = nil
100
+ end
101
+ end
102
+
103
+ before(:each) do
104
+ @request_params = {}
105
+ @controller = GlobalObjectReplicatorSinkController.new(@request_params)
106
+ flexmock(@controller).should_receive(:query).and_yield.by_default
107
+ @checksum_type = "global_object_version_sum"
108
+ end
109
+
110
+ context "handle_global_object_change" do
111
+
112
+ before(:each) do
113
+ @object = FakeGlobalSinkObject.new
114
+ @updated_name = "Updated Name"
115
+ @request_params.merge!({
116
+ :source => "library",
117
+ :api_version => "1.5",
118
+ :request_token => "1234",
119
+ :class_name => FakeGlobalSinkObject.to_s,
120
+ :id => @object.id,
121
+ :schema_version => FakeGlobalSinkObject.will_replicate_current_schema_version,
122
+ :global_object_version => @object.global_object_version + 1,
123
+ :attrs => @object.attributes.merge("name" => @updated_name).to_yaml })
124
+ flexmock(RightScale::RightHttpClient).should_receive(:push).by_default
125
+ end
126
+
127
+ it "should succeed for a valid global object" do
128
+ @controller.handle_global_object_change.should be_true
129
+ @object.name.should == @updated_name
130
+ end
131
+
132
+ it "should ignore requests for unknown objects" do
133
+ flexmock(@controller.logger).should_receive(:warn).with(/GlobalObjectReplica: Ignoring/).once
134
+ @request_params[:class_name] = "Server"
135
+ @controller.handle_global_object_change.should be_true
136
+ end
137
+ end
138
+
139
+ context "verify_next_replica_range" do
140
+
141
+ it "should send a request to the library_replicator" do
142
+ flexmock(EM).should_receive(:next_tick).and_yield.once
143
+ flexmock(RightScale::RightHttpClient).should_receive(:push).with("/library_replicator/verify_replica_range", hsh(:sink => "core")).once
144
+ @controller.send(:verify_next_replica_range, "library", FakeGlobalSinkObject, @checksum_type, 20, 1, 10).should be_true
145
+ end
146
+
147
+ it "logs exception if synchronize_replica_range request fails" do
148
+ flexmock(@controller.logger).should_receive(:error).with(/Failed to verify_replica_range.*failure/).once
149
+ flexmock(EM).should_receive(:next_tick).and_yield.once
150
+ flexmock(RightScale::RightHttpClient).should_receive(:push).and_raise(Exception, "failure").once
151
+ @controller.send(:verify_next_replica_range, "library", FakeGlobalSinkObject, @checksum_type, 20, 1, 10).should be_true
152
+ end
153
+ end
154
+
155
+ context "verify_replicas" do
156
+
157
+ before(:each) do
158
+ @request_params.merge!({
159
+ :source => "library",
160
+ :api_version => "1.5",
161
+ :request_token => "1234",
162
+ :class_name => "FakeGlobalSinkObject",
163
+ :checksum_type => @checksum_type,
164
+ :checksum_value => 123 })
165
+ end
166
+
167
+ it "should not start the synchronization process if the received sum matches the calculated" do
168
+ flexmock(@controller).should_receive(:verify_next_replica_range).never
169
+ @controller.verify_replicas.should be_true
170
+ end
171
+
172
+ it "should start the synchronization process if the received sum does not match the calculated" do
173
+ flexmock(@controller).should_receive(:verify_next_replica_range).once
174
+ @request_params[:checksum_value] = 999
175
+ @controller.verify_replicas.should be_true
176
+ end
177
+
178
+ it "should ignore requests for unknown objects" do
179
+ flexmock(@controller.logger).should_receive(:warn).with(/GlobalObjectReplica: Ignoring/).once
180
+ @request_params[:class_name] = "Server"
181
+ @controller.verify_replicas.should be_true
182
+ end
183
+ end
184
+
185
+ context "synchronize_replica_range" do
186
+
187
+ before(:each) do
188
+ @request_params.merge!({
189
+ :source => "library",
190
+ :api_version => "1.5",
191
+ :request_token => "1234",
192
+ :class_name => "FakeGlobalSinkObject",
193
+ :checksum_type => @checksum_type,
194
+ :max_id_at_start => 14114,
195
+ :begin_id => 1,
196
+ :end_id => 100,
197
+ :checksum_matched => true,
198
+ :has_more => false,
199
+ :records_to_synchronize => [] })
200
+ end
201
+
202
+ it "should update any received records" do
203
+ flexmock(FakeGlobalSinkObject).should_receive(:handle_global_object_change).with(1, 2, 3, {}).once
204
+ @request_params[:records_to_synchronize] = [{"id" => 1, "schema_version" => 2, "global_object_version" => 3, "attrs" => {}}]
205
+ @controller.synchronize_replica_range.should be_true
206
+ end
207
+
208
+ it "should continue synchronizing if has_more is true" do
209
+ flexmock(@controller).should_receive(:verify_next_replica_range).once
210
+ @request_params[:has_more] = true
211
+ @controller.synchronize_replica_range.should be_true
212
+ end
213
+
214
+ it "should stop synchronizing if has_more is false" do
215
+ flexmock(@replicator).should_receive(:verify_next_replica_range).never
216
+ @controller.synchronize_replica_range.should be_true
217
+ end
218
+
219
+ it "should ignore requests for unknown objects" do
220
+ flexmock(@controller.logger).should_receive(:warn).with(/GlobalObjectReplica: Ignoring/).once
221
+ @request_params[:class_name] = "Server"
222
+ @controller.synchronize_replica_range.should be_true
223
+ end
224
+ end
225
+
226
+ context "calculate_next_range_for_binary_sync" do
227
+
228
+ before(:each) do
229
+ @min_size = 250
230
+ end
231
+
232
+ context "context when max_id > min_size" do
233
+
234
+ before(:each) do
235
+ @max_id = 31021
236
+ @source = "library"
237
+ end
238
+
239
+ it "should return next min_size range if top level is in sync" do
240
+ @controller.send(:calculate_next_range_for_binary_sync, @source, @max_id, @min_size, 0, @max_id, true).
241
+ should == [@max_id + 1, @max_id + @min_size]
242
+ end
243
+
244
+ it "should return next min_size range if start is past max_id" do
245
+ start_id = @max_id + 50
246
+ end_id = @max_id + 100
247
+ @controller.send(:calculate_next_range_for_binary_sync, @source, @max_id, @min_size, start_id, end_id, true).
248
+ should == [end_id + 1, end_id + @min_size]
249
+ end
250
+
251
+ it "should return the 1st level left branch if top is not in sync" do
252
+ size, center = @controller.send(:calc_size_and_center, 0, @max_id)
253
+ @controller.send(:calculate_next_range_for_binary_sync, @source, @max_id, @min_size, 0, @max_id, false).
254
+ should == [0, center]
255
+ end
256
+
257
+ it "should return the 1st level right branch if 1st level left is in sync" do
258
+ size, center = @controller.send(:calc_size_and_center, 0, @max_id)
259
+ @controller.send(:calculate_next_range_for_binary_sync, @source, @max_id, @min_size, 0, center, true).
260
+ should == [center + 1, @max_id]
261
+ end
262
+
263
+ it "should return the 2nd level left-left branch if 1st level left is not in sync" do
264
+ size, center = @controller.send(:calc_size_and_center, 0, @max_id)
265
+ size, left_center = @controller.send(:calc_size_and_center, 0, center)
266
+ @controller.send(:calculate_next_range_for_binary_sync, @source, @max_id, @min_size, 0, center, false).
267
+ should == [0, left_center]
268
+ end
269
+
270
+ it "should return the 2nd level left-right branch if 2nd left-left is in sync" do
271
+ size, center = @controller.send(:calc_size_and_center, 0, @max_id)
272
+ size, left_center = @controller.send(:calc_size_and_center, 0, center)
273
+ @controller.send(:calculate_next_range_for_binary_sync, @source, @max_id, @min_size, 0, left_center, true).
274
+ should == [left_center + 1, center]
275
+ end
276
+
277
+ it "should return the 1st level right branch if 2nd left-right is in sync" do
278
+ size, center = @controller.send(:calc_size_and_center, 0, @max_id)
279
+ size, left_center = @controller.send(:calc_size_and_center, 0, center)
280
+ @controller.send(:calculate_next_range_for_binary_sync, @source, @max_id, @min_size, left_center + 1, center, true).
281
+ should == [center + 1, @max_id]
282
+ end
283
+
284
+ it "should return the 1st level right branch if 3rd left-right-right is in sync" do
285
+ size, center = @controller.send(:calc_size_and_center, 0, @max_id)
286
+ size, left_center = @controller.send(:calc_size_and_center, 0, center)
287
+ size, left_right_center = @controller.send(:calc_size_and_center, left_center + 1, center)
288
+ @controller.send(:calculate_next_range_for_binary_sync, @source, @max_id, @min_size, left_right_center + 1, center, true).
289
+ should == [center + 1, @max_id]
290
+ end
291
+
292
+ it "should return nils if 1st level right branch is in sync" do
293
+ size, center = @controller.send(:calc_size_and_center, 0, @max_id)
294
+ @controller.send(:calculate_next_range_for_binary_sync, @source, @max_id, @min_size, center + 1, @max_id, true).
295
+ should == [@max_id + 1, @max_id + @min_size]
296
+ end
297
+
298
+ it "should return nils if 2nd level right-right branch is in sync" do
299
+ size, center = @controller.send(:calc_size_and_center, 0, @max_id)
300
+ size, right_center = @controller.send(:calc_size_and_center, center + 1, @max_id)
301
+ @controller.send(:calculate_next_range_for_binary_sync, @source, @max_id, @min_size, right_center + 1, @max_id, true).should == [@max_id + 1, @max_id + @min_size]
302
+ end
303
+ end
304
+ end
305
+ end
@@ -0,0 +1,113 @@
1
+ #--
2
+ # Copyright (c) 2011-2013 RightScale, Inc, All Rights Reserved Worldwide.
3
+ #
4
+ # THIS PROGRAM IS CONFIDENTIAL AND PROPRIETARY TO RIGHTSCALE
5
+ # AND CONSTITUTES A VALUABLE TRADE SECRET. Any unauthorized use,
6
+ # reproduction, modification, or disclosure of this program is
7
+ # strictly prohibited. Any use of this program by an authorized
8
+ # licensee is strictly subject to the terms and conditions,
9
+ # including confidentiality obligations, set forth in the applicable
10
+ # License Agreement between RightScale.com, Inc. and the licensee.
11
+ #++
12
+
13
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
14
+
15
+ describe RightScale::GlobalObjectReplicatorSource do
16
+
17
+ include FlexMock::ArgumentTypes
18
+
19
+ # A pseudo HTTP controller applying GlobalObjectReplicatorSource
20
+ class GlobalObjectReplicatorSourceController
21
+ include RightScale::GlobalObjectReplicatorSource
22
+
23
+ attr_reader :logger
24
+ attr_reader :params
25
+
26
+ def initialize(params)
27
+ @params = params
28
+ @logger = RightScale::Log
29
+ end
30
+
31
+ def global_object_replicator_source
32
+ "library"
33
+ end
34
+
35
+ def render(hash)
36
+ nil
37
+ end
38
+ end
39
+
40
+ class FakeGlobalSourceObject
41
+ def self.calculate_global_object_checksum(checksum_type, schema_version, begin_id, end_id)
42
+ 123
43
+ end
44
+
45
+ def self.max_id
46
+ 101
47
+ end
48
+ end
49
+
50
+ context "verify_replica_range" do
51
+
52
+ before(:each) do
53
+ @request_params = {
54
+ :sink => "core",
55
+ :class_name => "FakeGlobalSourceObject",
56
+ :schema_version => 1,
57
+ :max_id_at_start => 100,
58
+ :begin_id => 1,
59
+ :end_id => 2,
60
+ :send_records_on_checksum_mismatch => true,
61
+ :checksum_value => FakeGlobalSourceObject.calculate_global_object_checksum("x", 1, 1, 2),
62
+ :shard_id => 9}
63
+ @controller = GlobalObjectReplicatorSourceController.new(@request_params)
64
+ flexmock(@controller).should_receive(:query).and_yield.by_default
65
+ flexmock(RightScale::RightHttpClient).should_receive(:push).by_default
66
+ end
67
+
68
+ it "gets records to synchronize if the received sum does not match the calculated sum" do
69
+ flexmock(FakeGlobalSourceObject).should_receive(:get_initialization_hashes).and_return([{:a => :b}]).once
70
+ @request_params[:checksum_value] = 999
71
+ @controller.verify_replica_range.should be_true
72
+ end
73
+
74
+ it "sends synchronize_replica_range request to sink with records to synchronize" do
75
+ flexmock(EM).should_receive(:next_tick).and_yield.once
76
+ flexmock(FakeGlobalSourceObject).should_receive(:get_initialization_hashes).and_return([{:a => :b}]).once
77
+ flexmock(RightScale::RightHttpClient).should_receive(:push).with("/replicator/synchronize_replica_range",
78
+ on { |arg| arg[:records_to_synchronize] == [{:a => :b}] }, :scope => {:shard => 9}).once
79
+ @request_params[:checksum_value] = 999
80
+ @controller.verify_replica_range.should be_true
81
+ end
82
+
83
+ it "does not get records to synchronize if send_records_on_checksum_mismatch is false" do
84
+ flexmock(EM).should_receive(:next_tick).and_yield.once
85
+ flexmock(FakeGlobalSourceObject).should_receive(:get_initialization_hashes).never
86
+ flexmock(RightScale::RightHttpClient).should_receive(:push).with("/replicator/synchronize_replica_range",
87
+ on { |arg| arg[:records_to_synchronize] == [] }, :scope => {:shard => 9}).once
88
+ @request_params[:checksum_value] = 999
89
+ @request_params[:send_records_on_checksum_mismatch] = false
90
+ @controller.verify_replica_range.should be_true
91
+ end
92
+
93
+ it "does not get records to synchronize if the received checksum matches the calculated checksum" do
94
+ flexmock(EM).should_receive(:next_tick).and_yield.once
95
+ flexmock(FakeGlobalSourceObject).should_receive(:get_initialization_hashes).never
96
+ flexmock(RightScale::RightHttpClient).should_receive(:push).with("/replicator/synchronize_replica_range",
97
+ on { |arg| arg[:records_to_synchronize] == [] }, :scope => {:shard => 9}).once
98
+ @controller.verify_replica_range.should be_true
99
+ end
100
+
101
+ it "logs exception if synchronize_replica_range request fails" do
102
+ flexmock(@controller.logger).should_receive(:error).with(/Failed to synchronize_replica_range.*failure/).once
103
+ flexmock(EM).should_receive(:next_tick).and_yield.once
104
+ flexmock(RightScale::RightHttpClient).should_receive(:push).and_raise(Exception, "failure").once
105
+ @controller.verify_replica_range.should be_true
106
+ end
107
+
108
+ it "raises exception if query is unsuccessful" do
109
+ flexmock(@controller).should_receive(:query).and_return(false).once
110
+ lambda { @controller.verify_replica_range }.should raise_error(RightScale::Exceptions::QueryFailure)
111
+ end
112
+ end
113
+ end