haproxy-cluster 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -37,6 +37,13 @@ Maybe you'd like to know how many transactions per second your whole cluster is
37
37
  haproxy_cluster --eval 'poll{ puts members.map{|m|m.myapp.rate}.inject(:+) }' $load_balancers
38
38
  ```
39
39
 
40
+ Perhaps there's a particular server which needs to be removed from the cluster quickly and temporarily.
41
+
42
+ ```bash
43
+ haproxy_cluster --eval 'each_member{ myapp.servers.find("server3").disable! }' $load_balancers
44
+ ```
45
+
46
+
40
47
  Installation
41
48
  ------------
42
49
 
@@ -29,7 +29,12 @@ class HAProxyCluster
29
29
  each_member { poll! } unless first
30
30
  first = false
31
31
  yield
32
- sleep interval - (Time.now - start)
32
+ delay = Time.now - start
33
+ if delay >= interval
34
+ sleep interval
35
+ else
36
+ sleep interval - delay
37
+ end
33
38
  end
34
39
  end
35
40
 
@@ -52,7 +57,7 @@ class HAProxyCluster
52
57
  # * :condition, anything accepted by `check_condition`
53
58
  # * :interval, check interval (default 2 seconds, same as HA Proxy)
54
59
  # * :timeout, give up after this number of seconds
55
- # * :min_checks, require :condtion to pass this many times in a row
60
+ # * :min_checks, require :condition to pass this many times in a row
56
61
  #
57
62
  def wait_until (options = {}, &code)
58
63
  opts = {
@@ -1,29 +1,34 @@
1
1
  require 'haproxy_cluster/stats_container'
2
- require 'haproxy_cluster/server_collection'
3
2
 
4
3
  class HAProxyCluster
5
4
 
6
5
  class Backend < StatsContainer
6
+ TYPE_ID = 1
7
7
 
8
- def initialize
9
- @servers = ServerCollection.new
10
- super
8
+ def initialize(stats,member = nil)
9
+ @member = member
10
+ @servers = Hash.new { |h,k| h[k] = Server.new({},@member) }
11
+ super stats
11
12
  end
12
13
 
13
14
  attr_accessor :servers
15
+ attr_reader :member
14
16
 
15
17
  def name
16
18
  self.pxname
17
19
  end
18
20
 
19
21
  def rolling_restartable? (enough = 80)
20
- up_servers = @servers.map{ |s| s.ok? }.count
22
+ up_servers = @servers.map{ |name,server| s.ok? }.count
21
23
  if up_servers == 0
22
- return true # All servers are down; can't hurt!
24
+ @member.log.warn { "All servers are down; can't hurt!" }
25
+ return true
23
26
  elsif Rational(up_servers,@servers.count) >= Rational(enough,100)
24
- return true # Minumum % is satisfied
27
+ @member.log.info { "#{up_servers}/#{@servers.count} is at least #{enough}%!" }
28
+ return true
25
29
  else
26
- return false # Not enough servers are up to handle restarting #{number_to_restart} at a time.
30
+ @member.log.warn { "Insufficient capacity to handle a rolling restart at this time." }
31
+ return false
27
32
  end
28
33
  end
29
34
 
@@ -7,6 +7,8 @@ require 'timeout'
7
7
  require 'haproxy_cluster/version'
8
8
  require 'haproxy_cluster'
9
9
 
10
+ Signal.trap("INT"){exit 1}
11
+
10
12
  options = OpenStruct.new
11
13
  OptionParser.new do |opts|
12
14
  opts.banner = "Usage: #{File.basename $0} ARGS URL [URL] [...]"
@@ -31,7 +33,6 @@ OptionParser.new do |opts|
31
33
  end.parse!
32
34
  options.urls = ARGV
33
35
 
34
-
35
36
  if options.code_string
36
37
 
37
38
  if options.timeout
@@ -1,24 +1,33 @@
1
1
  require 'csv'
2
+ require 'logger'
2
3
  require 'rest-client'
3
4
  require 'haproxy_cluster/backend'
4
5
  require 'haproxy_cluster/server'
5
6
 
6
7
  class HAProxyCluster
7
8
  class Member
8
- BACKEND = 1
9
- SERVER = 2
10
9
 
11
10
  def initialize(source)
12
11
  @source = source
13
- @backends = Hash.new { |h,k| h[k] = Backend.new }
12
+ @log = Logger.new(STDOUT)
13
+ original_formatter = Logger::Formatter.new
14
+ @log.formatter = proc { |severity,datetime,progname,msg|
15
+ original_formatter.call(severity,datetime,"HAProxyCluster",msg)
16
+ }
17
+ @backends = Hash.new { |h,k| h[k] = Backend.new({},self) }
14
18
  if source =~ /https?:/
15
19
  @type = :url
20
+ @name = URI.parse(@source).host
16
21
  else
17
22
  @type = :file
23
+ @name = @source
18
24
  end
19
25
  poll!
20
26
  end
21
27
 
28
+ attr_accessor :backends, :source, :type, :name, :log
29
+ def to_s; @name; end
30
+
22
31
  def poll!
23
32
  csv = case @type
24
33
  when :url
@@ -28,19 +37,14 @@ class HAProxyCluster
28
37
  end
29
38
  CSV.parse(csv.gsub(/^# /,'').gsub(/,$/,''), { :headers => :first_row, :converters => :all, :header_converters => [:downcase,:symbol] } ) do |row|
30
39
  case row[:type]
31
- when BACKEND
32
- @backends[ row[:pxname].to_sym ].stats.merge! row.to_hash
33
- when SERVER
34
- @backends[ row[:pxname].to_sym ].servers << Server.new(row.to_hash, self)
40
+ when Backend::TYPE_ID
41
+ @backends[ row[:pxname].to_sym ].stats = row.to_hash
42
+ when Server::TYPE_ID
43
+ @backends[ row[:pxname].to_sym ].servers[ row[:svname] ].stats = row.to_hash
35
44
  end
36
45
  end
37
46
  end
38
47
 
39
- attr_accessor :backends, :source, :type
40
-
41
- def get_binding; binding; end
42
- def to_s; @source; end
43
-
44
48
  # Allow Backends to be accessed by dot-notation
45
49
  def method_missing(m, *args, &block)
46
50
  if @backends.has_key? m
@@ -3,11 +3,13 @@ require 'haproxy_cluster/stats_container'
3
3
  class HAProxyCluster
4
4
 
5
5
  class Server < StatsContainer
6
+ TYPE_ID = 2
6
7
 
7
- def initialize (stats,member)
8
+ def initialize(stats,member = nil)
8
9
  @member = member
9
10
  super stats
10
11
  end
12
+ attr_reader :member
11
13
 
12
14
  def name
13
15
  self.svname
@@ -7,10 +7,27 @@ class HAProxyCluster
7
7
 
8
8
  def initialize(stats = {})
9
9
  @stats = stats
10
+ @monitor_fields = [:status]
10
11
  end
11
12
 
12
13
  attr_accessor :stats
13
14
 
15
+ def monitor(field)
16
+ @monitor_fields << field.to_sym
17
+ end
18
+
19
+ def stats=(new)
20
+ old = @stats
21
+ @stats = new
22
+ @monitor_fields.each do |field|
23
+ if new.has_key? field and not old.has_key? field
24
+ @member.log.info { "#{self.member.name} believes #{name} #{field} is #{new[field]}" }
25
+ elsif old[field] != new[field]
26
+ @member.log.info { "#{self.member.name} noticed a #{field} transition on #{name}: #{old[field]} -> #{new[field]}" }
27
+ end
28
+ end
29
+ end
30
+
14
31
  def method_missing(m, *args, &block)
15
32
  if @stats.has_key? m
16
33
  @stats[m]
@@ -1,3 +1,3 @@
1
1
  class HAProxyCluster
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: haproxy-cluster
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-31 00:00:00.000000000 Z
12
+ date: 2012-08-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rest-client
16
- requirement: &70230919355820 !ruby/object:Gem::Requirement
16
+ requirement: &70259727274420 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70230919355820
24
+ version_requirements: *70259727274420
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: test-unit
27
- requirement: &70230919354840 !ruby/object:Gem::Requirement
27
+ requirement: &70259727299520 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70230919354840
35
+ version_requirements: *70259727299520
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: webmock
38
- requirement: &70230919353540 !ruby/object:Gem::Requirement
38
+ requirement: &70259727295540 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70230919353540
46
+ version_requirements: *70259727295540
47
47
  description: ! " Gem and command line tool for quickly answering questions like,
48
48
  \"Can we\n survive a rolling restart?\", \"How many transactions per second am
49
49
  I seeing?\",\n \"What's my session backlog?\". Intended for use within continuous
@@ -61,7 +61,6 @@ files:
61
61
  - lib/haproxy_cluster/cli.rb
62
62
  - lib/haproxy_cluster/member.rb
63
63
  - lib/haproxy_cluster/server.rb
64
- - lib/haproxy_cluster/server_collection.rb
65
64
  - lib/haproxy_cluster/stats_container.rb
66
65
  - lib/haproxy_cluster/version.rb
67
66
  - lib/haproxy_cluster.rb
@@ -1,9 +0,0 @@
1
- class HAProxyCluster
2
- class ServerCollection < Array
3
- def find(string)
4
- self.select do |s|
5
- s.name == string
6
- end.first
7
- end
8
- end
9
- end