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

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