beetle 0.3.0.rc.7 → 0.3.0.rc.8

Sign up to get free protection for your applications and to get access to all the features.
data/beetle.gemspec CHANGED
@@ -32,19 +32,20 @@ Gem::Specification.new do |s|
32
32
  INFO
33
33
 
34
34
  s.specification_version = 3
35
- s.add_runtime_dependency("uuid4r", [">= 0.1.2"])
36
- s.add_runtime_dependency("bunny", ["= 0.7.8"])
37
- s.add_runtime_dependency("redis", ["= 2.2.2"])
38
- s.add_runtime_dependency("hiredis", ["= 0.3.2"])
39
- s.add_runtime_dependency("amq-client", ["= 0.8.3"])
40
- s.add_runtime_dependency("amq-protocol", ["= 0.8.1"])
41
- s.add_runtime_dependency("amqp", ["= 0.8.0"])
42
- s.add_runtime_dependency("activesupport", [">= 2.3.4"])
43
- s.add_runtime_dependency("daemons", [">= 1.0.10"])
44
- s.add_development_dependency("rake", [">= 0.8.7"])
45
- s.add_development_dependency("mocha", [">= 0"])
46
- s.add_development_dependency("rcov", [">= 0"])
47
- s.add_development_dependency("cucumber", [">= 0.7.2"])
48
- s.add_development_dependency("daemon_controller", [">= 0"])
35
+ s.add_runtime_dependency("uuid4r", [">= 0.1.2"])
36
+ s.add_runtime_dependency("bunny", ["= 0.7.8"])
37
+ s.add_runtime_dependency("redis", ["= 2.2.2"])
38
+ s.add_runtime_dependency("hiredis", ["= 0.3.2"])
39
+ s.add_runtime_dependency("amq-client", ["= 0.8.3"])
40
+ s.add_runtime_dependency("amq-protocol", ["= 0.8.1"])
41
+ s.add_runtime_dependency("amqp", ["= 0.8.0"])
42
+ s.add_runtime_dependency("activesupport", [">= 2.3.4"])
43
+ s.add_runtime_dependency("eventmachine_httpserver", [">= 0.2.1"])
44
+ s.add_runtime_dependency("daemons", [">= 1.0.10"])
45
+ s.add_development_dependency("rake", [">= 0.8.7"])
46
+ s.add_development_dependency("mocha", [">= 0"])
47
+ s.add_development_dependency("rcov", [">= 0"])
48
+ s.add_development_dependency("cucumber", [">= 0.7.2"])
49
+ s.add_development_dependency("daemon_controller", [">= 0"])
49
50
  end
50
51
 
@@ -103,3 +103,7 @@ Feature: Redis auto failover
103
103
  And an old redis master file for "rc-client-1" with master "redis-1" exists
104
104
  And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
105
105
  Then the redis master of "rc-client-1" should be "redis-1"
106
+
107
+ Scenario: Redis configuation server should embed a http server
108
+ Given a redis configuration server using redis servers "redis-1,redis-2" with clients "rc-client-1,rc-client-2" exists
109
+ Then the redis configuration server should answer http requests
@@ -132,3 +132,9 @@ Then /^a system notification for no slave available to become new master should
132
132
  text = "Redis master could not be switched, no slave available to become new master"
133
133
  assert_match /#{text}/, File.readlines(system_notification_log_path).last
134
134
  end
135
+
136
+ Then /^the redis configuration server should answer http requests$/ do
137
+ TestDaemons::RedisConfigurationServer.answers_text_requests?
138
+ TestDaemons::RedisConfigurationServer.answers_html_requests?
139
+ TestDaemons::RedisConfigurationServer.answers_json_requests?
140
+ end
@@ -1,4 +1,5 @@
1
1
  require 'daemon_controller'
2
+ require 'net/http'
2
3
 
3
4
  module TestDaemons
4
5
  class RedisConfigurationServer
@@ -25,7 +26,7 @@ module TestDaemons
25
26
  DaemonController.new(
26
27
  :identifier => "Redis configuration test server",
27
28
  :start_command => "ruby bin/beetle configuration_server start -- -v --redis-master-file #{redis_master_file} --redis-servers #{@@redis_servers} #{clients_parameter_string} --redis-retry-interval 1 --pid-dir #{tmp_path} --amqp-servers 127.0.0.1:5672",
28
- :ping_command => lambda{ true },
29
+ :ping_command => lambda{ answers_text_requests? },
29
30
  :pid_file => pid_file,
30
31
  :log_file => log_file,
31
32
  :start_timeout => 5
@@ -48,5 +49,39 @@ module TestDaemons
48
49
  File.expand_path(File.dirname(__FILE__) + "/../../../tmp")
49
50
  end
50
51
 
52
+ def self.answers_text_requests?
53
+ response = get_status("/.txt", "text/plain")
54
+ response.code == '200' &&
55
+ response.content_type == "text/plain"
56
+ rescue
57
+ false
58
+ end
59
+
60
+ def self.answers_json_requests?
61
+ response = get_status("/.json", "application/json")
62
+ response.code == '200' &&
63
+ response.content_type == "application/json"
64
+ rescue
65
+ false
66
+ end
67
+
68
+ def self.answers_html_requests?
69
+ response = get_status("/", "text/html")
70
+ response.code == '200' &&
71
+ response.content_type == "txt/html"
72
+ rescue
73
+ false
74
+ end
75
+
76
+ def self.get_status(path, content_type)
77
+ uri = URI.parse("http://127.0.0.1:8080#{path}")
78
+ http = Net::HTTP.new(uri.host, uri.port)
79
+ request = Net::HTTP::Get.new(uri.request_uri)
80
+ request['Accept'] = content_type
81
+ response = http.request(request)
82
+ # $stderr.puts response.content_type
83
+ # $stderr.puts response.body
84
+ response
85
+ end
51
86
  end
52
87
  end
data/lib/beetle.rb CHANGED
@@ -7,6 +7,7 @@ require 'active_support'
7
7
  require 'active_support/core_ext'
8
8
  require 'set'
9
9
  require 'socket'
10
+ require 'beetle/version'
10
11
 
11
12
  module Beetle
12
13
  Timer = if RUBY_VERSION < "1.9"
@@ -77,7 +77,12 @@ module Beetle
77
77
  end
78
78
 
79
79
  Daemons.run_proc("redis_configuration_server", :log_output => true, :dir_mode => dir_mode, :dir => dir) do
80
- Beetle::RedisConfigurationServer.new.start
80
+ config_server = Beetle::RedisConfigurationServer.new
81
+ Beetle::RedisConfigurationHttpServer.config_server = config_server
82
+ EM.run do
83
+ config_server.start
84
+ EM.start_server '0.0.0.0', 8080, Beetle::RedisConfigurationHttpServer
85
+ end
81
86
  end
82
87
  end
83
88
  end
@@ -0,0 +1,98 @@
1
+ require 'evma_httpserver'
2
+
3
+ module Beetle
4
+ class RedisConfigurationHttpServer < EM::Connection
5
+ include EM::HttpServer
6
+
7
+ def post_init
8
+ super
9
+ no_environment_strings
10
+ end
11
+
12
+ cattr_accessor :config_server
13
+
14
+ def process_http_request
15
+ # the http request details are available via the following instance variables:
16
+ # @http_protocol
17
+ # @http_request_method
18
+ # @http_cookie
19
+ # @http_if_none_match
20
+ # @http_content_type
21
+ # @http_path_info
22
+ # @http_request_uri
23
+ # @http_query_string
24
+ # @http_post_content
25
+ # @http_headers
26
+ response = EM::DelegatedHttpResponse.new(self)
27
+ # headers = @http_headers.split("\0").inject({}){|h, s| (s =~ /^([^:]+): (.*)$/ && (h[$1] = $2)); h }
28
+
29
+ case @http_request_uri
30
+ when '/', '/.html'
31
+ response.content_type 'text/html'
32
+ server_status(response, "html")
33
+ when "/.json"
34
+ response.content_type 'application/json'
35
+ server_status(response, "json")
36
+ when "/.txt"
37
+ response.content_type 'text/plain'
38
+ server_status(response, "plain")
39
+ else
40
+ not_found(response)
41
+ end
42
+ response.send_response
43
+ end
44
+
45
+ def server_status(response, type)
46
+ response.status = 200
47
+ status = config_server.status
48
+ response.content =
49
+ case type
50
+ when "plain"
51
+ plain_text_response(status)
52
+ when "json"
53
+ status.to_json
54
+ when "html"
55
+ html_response(status)
56
+ end
57
+ end
58
+
59
+ def plain_text_response(status)
60
+ status.keys.sort_by{|k| k.to_s}.map do |k|
61
+ name = k.to_s # .split('_').join(" ")
62
+ if (value = status[k]).is_a?(Array)
63
+ value = value.join(", ")
64
+ end
65
+ "#{name}: #{value}"
66
+ end.join("\n")
67
+ end
68
+
69
+ def html_response(status)
70
+ b = "<!doctype html>\n"
71
+ b << "<html><head><title>Beetle Configuration Server Status</title>#{html_styles(status)}</head>"
72
+ b << "<body><h1>Beetle Configuration Server Status</h1><table cellspacing=0>\n"
73
+ plain_text_response(status).split("\n").compact.each do |row|
74
+ row =~/(^[^:]+): (.*)$/
75
+ b << "<tr><td>#{$1}</td><td>#{$2}</td></tr>\n"
76
+ end
77
+ b << "</table></body></html>"
78
+ end
79
+
80
+ def html_styles(status)
81
+ warn_color = status[:redis_master_available?] ? "#5780b2" : "#A52A2A"
82
+ <<"EOS"
83
+ <style media="screen" type="text/css">
84
+ html { font: 1.25em/1.5 arial, sans-serif;}
85
+ body { margin: 1em; }
86
+ table tr:nth-child(2n+1){ background:#fff; }
87
+ td { padding: 0.1em 0.2em; }
88
+ h1 { color: #{warn_color}; margin-bottom: 0.2em;}
89
+ </style>
90
+ EOS
91
+ end
92
+
93
+ def not_found(response)
94
+ response.content_type 'text/plain'
95
+ response.status = 404
96
+ end
97
+ end
98
+ end
@@ -21,6 +21,9 @@ module Beetle
21
21
  # the current token used to detect correct message order
22
22
  attr_reader :current_token
23
23
 
24
+ # the list of known client ids
25
+ attr_reader :client_ids
26
+
24
27
  def initialize #:nodoc:
25
28
  @client_ids = Set.new(config.redis_configuration_client_ids.split(","))
26
29
  @current_token = (Time.now.to_f * 1000).to_i
@@ -43,6 +46,19 @@ module Beetle
43
46
  beetle.config
44
47
  end
45
48
 
49
+ # returns a hash describing the current server status
50
+ def status
51
+ {
52
+ :beetle_version => Beetle::VERSION,
53
+ :configured_brokers => config.servers.split(/\s*,\s*/),
54
+ :configured_client_ids => client_ids.to_a.sort,
55
+ :configured_redis_servers => config.redis_servers.split(/\s*,\s*/),
56
+ :redis_master => current_master.try(:server).to_s,
57
+ :redis_master_available? => master_available?,
58
+ :redis_slaves_available => available_slaves.map(&:server),
59
+ }
60
+ end
61
+
46
62
  # start watching redis
47
63
  def start
48
64
  verify_redis_master_file_string
@@ -126,6 +142,11 @@ module Beetle
126
142
  redis.masters.include?(current_master)
127
143
  end
128
144
 
145
+ # list of available redis slaves
146
+ def available_slaves
147
+ redis.slaves
148
+ end
149
+
129
150
  private
130
151
 
131
152
  def check_redis_configuration
@@ -52,12 +52,13 @@ module Beetle
52
52
  masters.first
53
53
  end
54
54
 
55
- private
56
-
55
+ # check whether all redis servers are up and configured
57
56
  def master_and_slaves_reachable?
58
57
  masters.size == 1 && slaves.size == instances.size - 1
59
58
  end
60
59
 
60
+ private
61
+
61
62
  def reset
62
63
  @server_info = Hash.new {|h,k| h[k]= []}
63
64
  end
@@ -1,3 +1,3 @@
1
1
  module Beetle
2
- VERSION = "0.3.0.rc.7"
2
+ VERSION = "0.3.0.rc.8"
3
3
  end
data/script/console~ ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.expand_path("../lib",__FILE__))
@@ -59,6 +59,10 @@ module Beetle
59
59
  @server.beetle.expects(:publish).with(:reconfigure, payload)
60
60
  @server.master_available!
61
61
  end
62
+
63
+ test "should be able to report current status" do
64
+ assert @server.status.is_a?(Hash)
65
+ end
62
66
  end
63
67
 
64
68
  class RedisConfigurationServerInvalidationTest < Test::Unit::TestCase
@@ -68,11 +68,17 @@ module Beetle
68
68
 
69
69
  # if this test fails after upgrading redis, we can probably remove
70
70
  # our custom shutdown method from redis_ext.rb
71
- test "redis shutdown implementation is broken" do
71
+ test "orginal redis shutdown implementation is broken" do
72
72
  @r.client.expects(:call_without_reply).with([:shutdown]).once
73
73
  @r.client.expects(:disconnect).never
74
74
  @r.broken_shutdown
75
75
  end
76
+
77
+ test "patched redis shutdown implementation should call :shutdown and rescue Errno::ECONNREFUSED" do
78
+ @r.client.expects(:call).with([:shutdown]).once.raises(Errno::ECONNREFUSED)
79
+ @r.client.expects(:disconnect).once
80
+ @r.shutdown
81
+ end
76
82
  end
77
83
 
78
84
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: beetle
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15424043
4
+ hash: 15424053
5
5
  prerelease: 6
6
6
  segments:
7
7
  - 0
8
8
  - 3
9
9
  - 0
10
10
  - rc
11
- - 7
12
- version: 0.3.0.rc.7
11
+ - 8
12
+ version: 0.3.0.rc.8
13
13
  platform: ruby
14
14
  authors:
15
15
  - Stefan Kaes
@@ -21,7 +21,7 @@ autorequire:
21
21
  bindir: bin
22
22
  cert_chain: []
23
23
 
24
- date: 2011-10-24 00:00:00 +02:00
24
+ date: 2011-10-29 00:00:00 +02:00
25
25
  default_executable: beetle
26
26
  dependencies:
27
27
  - !ruby/object:Gem::Dependency
@@ -153,9 +153,25 @@ dependencies:
153
153
  type: :runtime
154
154
  version_requirements: *id008
155
155
  - !ruby/object:Gem::Dependency
156
- name: daemons
156
+ name: eventmachine_httpserver
157
157
  prerelease: false
158
158
  requirement: &id009 !ruby/object:Gem::Requirement
159
+ none: false
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ hash: 21
164
+ segments:
165
+ - 0
166
+ - 2
167
+ - 1
168
+ version: 0.2.1
169
+ type: :runtime
170
+ version_requirements: *id009
171
+ - !ruby/object:Gem::Dependency
172
+ name: daemons
173
+ prerelease: false
174
+ requirement: &id010 !ruby/object:Gem::Requirement
159
175
  none: false
160
176
  requirements:
161
177
  - - ">="
@@ -167,11 +183,11 @@ dependencies:
167
183
  - 10
168
184
  version: 1.0.10
169
185
  type: :runtime
170
- version_requirements: *id009
186
+ version_requirements: *id010
171
187
  - !ruby/object:Gem::Dependency
172
188
  name: rake
173
189
  prerelease: false
174
- requirement: &id010 !ruby/object:Gem::Requirement
190
+ requirement: &id011 !ruby/object:Gem::Requirement
175
191
  none: false
176
192
  requirements:
177
193
  - - ">="
@@ -183,11 +199,11 @@ dependencies:
183
199
  - 7
184
200
  version: 0.8.7
185
201
  type: :development
186
- version_requirements: *id010
202
+ version_requirements: *id011
187
203
  - !ruby/object:Gem::Dependency
188
204
  name: mocha
189
205
  prerelease: false
190
- requirement: &id011 !ruby/object:Gem::Requirement
206
+ requirement: &id012 !ruby/object:Gem::Requirement
191
207
  none: false
192
208
  requirements:
193
209
  - - ">="
@@ -197,11 +213,11 @@ dependencies:
197
213
  - 0
198
214
  version: "0"
199
215
  type: :development
200
- version_requirements: *id011
216
+ version_requirements: *id012
201
217
  - !ruby/object:Gem::Dependency
202
218
  name: rcov
203
219
  prerelease: false
204
- requirement: &id012 !ruby/object:Gem::Requirement
220
+ requirement: &id013 !ruby/object:Gem::Requirement
205
221
  none: false
206
222
  requirements:
207
223
  - - ">="
@@ -211,11 +227,11 @@ dependencies:
211
227
  - 0
212
228
  version: "0"
213
229
  type: :development
214
- version_requirements: *id012
230
+ version_requirements: *id013
215
231
  - !ruby/object:Gem::Dependency
216
232
  name: cucumber
217
233
  prerelease: false
218
- requirement: &id013 !ruby/object:Gem::Requirement
234
+ requirement: &id014 !ruby/object:Gem::Requirement
219
235
  none: false
220
236
  requirements:
221
237
  - - ">="
@@ -227,11 +243,11 @@ dependencies:
227
243
  - 2
228
244
  version: 0.7.2
229
245
  type: :development
230
- version_requirements: *id013
246
+ version_requirements: *id014
231
247
  - !ruby/object:Gem::Dependency
232
248
  name: daemon_controller
233
249
  prerelease: false
234
- requirement: &id014 !ruby/object:Gem::Requirement
250
+ requirement: &id015 !ruby/object:Gem::Requirement
235
251
  none: false
236
252
  requirements:
237
253
  - - ">="
@@ -241,7 +257,7 @@ dependencies:
241
257
  - 0
242
258
  version: "0"
243
259
  type: :development
244
- version_requirements: *id014
260
+ version_requirements: *id015
245
261
  description: A highly available, reliable messaging infrastructure
246
262
  email: opensource@xing.com
247
263
  executables:
@@ -266,7 +282,6 @@ files:
266
282
  - examples/redundant.rb
267
283
  - examples/rpc.rb
268
284
  - examples/simple.rb
269
- - examples/test_publisher.rb
270
285
  - lib/beetle/base.rb
271
286
  - lib/beetle/client.rb
272
287
  - lib/beetle/commands/configuration_client.rb
@@ -280,6 +295,7 @@ files:
280
295
  - lib/beetle/publisher.rb
281
296
  - lib/beetle/r_c.rb
282
297
  - lib/beetle/redis_configuration_client.rb
298
+ - lib/beetle/redis_configuration_http_server.rb
283
299
  - lib/beetle/redis_configuration_server.rb
284
300
  - lib/beetle/redis_ext.rb
285
301
  - lib/beetle/redis_master_file.rb
@@ -298,6 +314,7 @@ files:
298
314
  - features/support/test_daemons/redis_configuration_client.rb
299
315
  - features/support/test_daemons/redis_configuration_server.rb
300
316
  - script/console
317
+ - script/console~
301
318
  - script/start_rabbit
302
319
  - beetle.gemspec
303
320
  - Rakefile
@@ -1,32 +0,0 @@
1
- # attempts.rb
2
- # this example shows you how to use the exception limiting feature of beetle
3
- # it allows you to control the number of retries your handler will go through
4
- # with one message before giving up on it
5
- #
6
- # ! check the examples/README.rdoc for information on starting your redis/rabbit !
7
- #
8
- # start it with ruby attempts.rb
9
-
10
- require "rubygems"
11
- require File.expand_path("../lib/beetle", File.dirname(__FILE__))
12
- require "eventmachine"
13
-
14
- # set Beetle log level to info, less noisy than debug
15
- Beetle.config.logger.level = Logger::INFO
16
-
17
- # setup client
18
- client = Beetle::Client.new
19
- client.register_message(:test)
20
-
21
- n = 0
22
- EM.run do
23
- EM.add_periodic_timer(0.1) do
24
- data = (n+=1)
25
- client.logger.info "publishing #{data}"
26
- client.publish(:test, data)
27
- end
28
- trap("INT") do
29
- client.stop_publishing
30
- EM.stop_event_loop
31
- end
32
- end