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 +7 -0
- data/lib/haproxy_cluster.rb +7 -2
- data/lib/haproxy_cluster/backend.rb +13 -8
- data/lib/haproxy_cluster/cli.rb +2 -1
- data/lib/haproxy_cluster/member.rb +16 -12
- data/lib/haproxy_cluster/server.rb +3 -1
- data/lib/haproxy_cluster/stats_container.rb +17 -0
- data/lib/haproxy_cluster/version.rb +1 -1
- metadata +8 -9
- data/lib/haproxy_cluster/server_collection.rb +0 -9
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
|
|
data/lib/haproxy_cluster.rb
CHANGED
@@ -29,7 +29,12 @@ class HAProxyCluster
|
|
29
29
|
each_member { poll! } unless first
|
30
30
|
first = false
|
31
31
|
yield
|
32
|
-
|
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 :
|
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
|
-
@
|
10
|
-
|
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{ |
|
22
|
+
up_servers = @servers.map{ |name,server| s.ok? }.count
|
21
23
|
if up_servers == 0
|
22
|
-
|
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
|
-
|
27
|
+
@member.log.info { "#{up_servers}/#{@servers.count} is at least #{enough}%!" }
|
28
|
+
return true
|
25
29
|
else
|
26
|
-
|
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
|
|
data/lib/haproxy_cluster/cli.rb
CHANGED
@@ -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
|
-
@
|
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
|
32
|
-
@backends[ row[:pxname].to_sym ].stats
|
33
|
-
when
|
34
|
-
@backends[ row[:pxname].to_sym ].servers
|
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
|
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]
|
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.
|
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-
|
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: &
|
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: *
|
24
|
+
version_requirements: *70259727274420
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: test-unit
|
27
|
-
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: *
|
35
|
+
version_requirements: *70259727299520
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: webmock
|
38
|
-
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: *
|
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
|