haproxy-cluster 0.0.5 → 0.0.6

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/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