haproxy-decklin 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in haproxy.gemspec
4
+ gemspec
5
+
6
+ group :development, :test do
7
+ gem 'rspec'
8
+ end
data/README.md ADDED
@@ -0,0 +1,133 @@
1
+ # HAProxy RubyGem
2
+
3
+ This gem aims to provide an interface to query HAProxy for statistics,
4
+ and eventually management through the sockets API.
5
+
6
+ ## What's HAProxy?
7
+
8
+ [HAProxy](http://haproxy.1wt.eu/) is the _Reliable, High Performance
9
+ TCP/HTTP Load Balancer_.
10
+
11
+ ## Why this gem?
12
+
13
+ * I use HAProxy at my work.
14
+ * I wanted to know how to create gems.
15
+ * I wanted to contribute with OSS.
16
+ * Why not?
17
+ * All of the above.
18
+
19
+ ## Example of use
20
+
21
+ #! /usr/bin/env ruby
22
+ require 'haproxy'
23
+ require 'pp'
24
+
25
+ haproxy = HAProxy.read_stats '/path/to/haproxy.stats.socket'
26
+
27
+ pp haproxy.info
28
+
29
+ # {:name=>"HAProxy",
30
+ # :version=>"1.3.22",
31
+ # :release_date=>"2009/10/14",
32
+ # :nbproc=>"1",
33
+ # :process_num=>"1",
34
+ # :pid=>"10222",
35
+ # :uptime=>"0d 0h33m12s",
36
+ # :uptime_sec=>"1992",
37
+ # :memmax_mb=>"0",
38
+ # :ulimit_n=>"4013",
39
+ # :maxsock=>"4013",
40
+ # :maxconn=>"2000",
41
+ # :maxpipes=>"0",
42
+ # :currconns=>"1",
43
+ # :pipesused=>"0",
44
+ # :pipesfree=>"0",
45
+ # :tasks=>"1",
46
+ # :run_queue=>"1",
47
+ # :node=>"roke",
48
+ # :"description:"=>nil}
49
+
50
+ pp haproxy.stats
51
+
52
+ # [{:pxname=>"app1",
53
+ # :svname=>"thin1",
54
+ # :qcur=>"0",
55
+ # :qmax=>"0",
56
+ # :scur=>"0",
57
+ # :smax=>"0",
58
+ # :slim=>"",
59
+ # :stot=>"0",
60
+ # :bin=>"0",
61
+ # :bout=>"0",
62
+ # :dreq=>"",
63
+ # :dresp=>"0",
64
+ # :ereq=>"",
65
+ # :econ=>"0",
66
+ # :eresp=>"0",
67
+ # :wretr=>"0",
68
+ # :wredis=>"0",
69
+ # :status=>"no check",
70
+ # :weight=>"1",
71
+ # :act=>"1",
72
+ # :bck=>"0",
73
+ # :chkfail=>"",
74
+ # :chkdown=>"",
75
+ # :lastchg=>"",
76
+ # :downtime=>"",
77
+ # :qlimit=>"",
78
+ # :pid=>"1",
79
+ # :iid=>"1",
80
+ # :sid=>"1",
81
+ # :throttle=>"",
82
+ # :lbtot=>"0",
83
+ # :tracked=>"",
84
+ # :type=>"2",
85
+ # :rate=>"0",
86
+ # :rate_lim=>"",
87
+ # :rate_max=>"0"},...]
88
+
89
+ #### HAProxy sample configuration
90
+
91
+ global
92
+ stats socket haproxy
93
+
94
+ defaults
95
+ mode http
96
+ option httplog
97
+ option httpclose
98
+ retries 3
99
+ option redispatch
100
+ maxconn 2000
101
+ contimeout 5000
102
+ clitimeout 50000
103
+ srvtimeout 50000
104
+ stats uri /haproxy
105
+
106
+ listen app1 0.0.0.0:10000
107
+ balance roundrobin
108
+
109
+ server thin1 127.0.0.1:10001
110
+ server thin1 127.0.0.1:10002
111
+ server thin1 127.0.0.1:10003
112
+ server thin1 127.0.0.1:10004
113
+ server thin1 127.0.0.1:10005
114
+
115
+ frontend app2
116
+ bind 0.0.0.0:10011
117
+ default_backend app2
118
+
119
+ backend app2
120
+ balance roundrobin
121
+ server thin1 127.0.0.1:10006
122
+ server thin1 127.0.0.1:10007
123
+ server thin1 127.0.0.1:10008
124
+ server thin1 127.0.0.1:10009
125
+ server thin1 127.0.0.1:10010
126
+
127
+ ## Roadmap
128
+
129
+ * Improve documentation
130
+ * Improve CSV parsing
131
+ * Add tests
132
+ * Add examples
133
+ * Read stats from HTTP and CSV files
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/haproxy.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ # -*- mode: ruby; encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "haproxy/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "haproxy-decklin"
7
+ s.version = HAProxy::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Leandro López (inkel)"]
10
+ s.email = ["inkel.ar@gmail.com"]
11
+ s.homepage = "https://github.com/inkel/haproxy-ruby"
12
+ s.summary = 'HAProxy interface for reading statistics or managing servers (requires HAProxy 1.4+)'
13
+ s.description = 'This gem is intended for use as a HAProxy interface when you need to read statistics or if you like to manage proxies thru Ruby'
14
+
15
+ s.rubyforge_project = "haproxy"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+ end
data/lib/haproxy.rb ADDED
@@ -0,0 +1,21 @@
1
+ libdir = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
3
+
4
+ require 'uri'
5
+ Dir["#{libdir}/haproxy/*.rb"].each do |file|
6
+ require file
7
+ end
8
+
9
+ module HAProxy
10
+
11
+ def self.read_stats(from)
12
+ uri = URI.parse(from)
13
+
14
+ if uri.is_a?(URI::Generic) and File.socket?(uri.path)
15
+ HAProxy::SocketReader.new(uri.path)
16
+ else
17
+ raise NotImplementedError, "Currently only sockets are implemented"
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,32 @@
1
+ module HAProxy
2
+ class CSVParser
3
+
4
+ # Uses CSV header from HAProxy 1.5, which is backwards compatible
5
+ COLUMNS = "pxname,svname,qcur,qmax,scur,smax,slim,stot,bin,bout,dreq,dresp,ereq,econ,eresp,wretr,wredis,status,weight,act,bck,chkfail,chkdown,lastchg,downtime,qlimit,pid,iid,sid,throttle,lbtot,tracked,type,rate,rate_lim,rate_max,check_status,check_code,check_duration,hrsp_1xx,hrsp_2xx,hrsp_3xx,hrsp_4xx,hrsp_5xx,hrsp_other,hanafail,req_rate,req_rate_max,req_tot,cli_abrt,srv_abrt".split(",").map(&:to_sym)
6
+ LIMIT = COLUMNS.length
7
+
8
+ def self.parse(line)
9
+ line.strip!
10
+ unless line.start_with? "#"
11
+ data = line.split(',')
12
+ pxname = data.shift
13
+
14
+ stats = { :pxname => pxname }
15
+
16
+ data.each_with_index do |value, i|
17
+ if i < LIMIT
18
+ stats[COLUMNS[i + 1]] = value
19
+ else
20
+ stats[:extra] = Array.new if stats[:extra].nil?
21
+ stats[:extra] << value
22
+ end
23
+ end
24
+
25
+ stats
26
+ else
27
+ raise ArgumentError, "Line is a comment"
28
+ end
29
+ end
30
+ end
31
+
32
+ end
@@ -0,0 +1,94 @@
1
+ require 'socket'
2
+ require 'haproxy/stats_reader'
3
+ require 'haproxy/csv_parser'
4
+
5
+ module HAProxy
6
+ class SocketReader < HAProxy::StatsReader
7
+
8
+ def initialize(path)
9
+ raise ArgumentError, "Socket #{path} doesn't exists or is not a UNIX socket" unless File.exists?(path) and File.socket?(path)
10
+ @path = path
11
+ end
12
+
13
+ def info
14
+ returning({}) do |info|
15
+ send_cmd "show info" do |line|
16
+ key, value = line.split(': ')
17
+ info[key.downcase.gsub('-', '_').to_sym] = value
18
+ end
19
+ end
20
+ end
21
+
22
+ def errors
23
+ returning("") do |errors|
24
+ send_cmd "show errors" do |line|
25
+ errors << line
26
+ end
27
+ end
28
+ end
29
+
30
+ def sessions
31
+ returning([]) do |sess|
32
+ send_cmd "show sess" do |line|
33
+ sess << line
34
+ end
35
+ end
36
+ end
37
+
38
+ TYPES = {
39
+ :frontend => 1,
40
+ :backend => 2,
41
+ :server => 4
42
+ }
43
+
44
+ def stats(types=[:all], options={})
45
+ params = {
46
+ :proxy => :all,
47
+ :server => :all
48
+ }.merge(options)
49
+
50
+ params[:proxy] = "-1" if params[:proxy].eql?(:all)
51
+ params[:server] = "-1" if params[:server].eql?(:all)
52
+
53
+ types = [types] unless types.is_a?(Array)
54
+
55
+ params[:type] = case
56
+ when types.eql?([:all])
57
+ "-1"
58
+ else
59
+ types.map{ |type| TYPES[type] }.inject(:+)
60
+ end
61
+
62
+ cmd = "show stat #{params[:proxy]} #{params[:type]} #{params[:server]}"
63
+
64
+ returning([]) do |stats|
65
+ send_cmd(cmd) do |line|
66
+ stats << CSVParser.parse(line) unless line.start_with?('#')
67
+ end
68
+ end
69
+ end
70
+
71
+ def frontends
72
+ stats :frontend, :proxy => :all, :server => :all
73
+ end
74
+
75
+ def backends
76
+ stats :frontend, :proxy => :all, :server => :all
77
+ end
78
+
79
+ def servers
80
+ stats :server, :proxy => :all, :server => :all
81
+ end
82
+
83
+ protected
84
+
85
+ def send_cmd(cmd, &block)
86
+ socket = UNIXSocket.new(@path)
87
+ socket.write(cmd + ';')
88
+ socket.each do |line|
89
+ yield(line.strip)
90
+ end
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,51 @@
1
+ libdir = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
3
+
4
+ module HAProxy
5
+
6
+ class StatsReader
7
+
8
+ def info
9
+ raise NotImplementedError
10
+ end
11
+
12
+ def errors
13
+ raise NotImplementedError
14
+ end
15
+
16
+ def sessions
17
+ raise NotImplementedError
18
+ end
19
+
20
+ def stats
21
+ raise NotImplementedError
22
+ end
23
+
24
+ def frontends
25
+ raise NotImplementedError
26
+ end
27
+
28
+ def backends
29
+ raise NotImplementedError
30
+ end
31
+
32
+ def servers
33
+ raise NotImplementedError
34
+ end
35
+
36
+ protected
37
+
38
+ # Borrowed from Rails 3
39
+ def returning(value)
40
+ yield(value)
41
+ value
42
+ end
43
+
44
+ private
45
+
46
+ def initialize
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,3 @@
1
+ module HAProxy
2
+ VERSION = "0.0.3"
3
+ end
@@ -0,0 +1,6 @@
1
+ #require File.expand_path('../spec_helper.rb', __FILE__)
2
+ require File.expand_path('../../lib/haproxy', __FILE__)
3
+
4
+ describe HAProxy do
5
+
6
+ end
@@ -0,0 +1,59 @@
1
+ require File.expand_path('../spec_helper.rb', __FILE__)
2
+ require File.expand_path('../../lib/haproxy', __FILE__)
3
+
4
+ describe HAProxy::SocketReader do
5
+
6
+ it "should accept an existing socket file" do
7
+ HAProxy::Test.with_socket do |path|
8
+ HAProxy::SocketReader.new(path).should_not be_nil
9
+ end
10
+ end
11
+
12
+ it "should fail without an existing socket file" do
13
+ Tempfile.new('invalid-file') do |file|
14
+ file.path.should_not be_socket
15
+ HAProxy::SocketReader.new(file.path).should_raise ArgumentError
16
+ end.close!
17
+ end
18
+
19
+ it "should return a Hash with proxy information" do
20
+ HAProxy::Test.with_socket(:info) do |path|
21
+ reader = HAProxy::SocketReader.new(path)
22
+ reader.info.class.should == Hash
23
+ end
24
+ end
25
+
26
+ it "should return a String when asking for errors" do
27
+ HAProxy::Test.with_socket(:without_errors) do |path|
28
+ reader = HAProxy::SocketReader.new(path)
29
+ reader.errors.class.should == String
30
+ end
31
+
32
+ HAProxy::Test.with_socket(:with_errors) do |path|
33
+ reader = HAProxy::SocketReader.new(path)
34
+ reader.errors.class.should == String
35
+ end
36
+ end
37
+
38
+ it "should return a String when asking for sessions" do
39
+ HAProxy::Test.with_socket(:without_sessions) do |path|
40
+ reader = HAProxy::SocketReader.new(path)
41
+ reader.sessions.class.should == Array
42
+ end
43
+
44
+ HAProxy::Test.with_socket(:with_sessions) do |path|
45
+ reader = HAProxy::SocketReader.new(path)
46
+ reader.sessions.class.should == Array
47
+ end
48
+ end
49
+
50
+ [ :frontends, :backends, :servers ].each do |type|
51
+ it "should return an Array with #{type} information" do
52
+ HAProxy::Test.with_socket do |path|
53
+ reader = HAProxy::SocketReader.new(path)
54
+ reader.send(type).class.should == Array
55
+ end
56
+ end
57
+ end
58
+
59
+ end
@@ -0,0 +1,114 @@
1
+ require 'tempfile'
2
+
3
+ module HAProxy
4
+
5
+ module Test
6
+
7
+ class << self
8
+ def temp_file_path
9
+ tmp = Tempfile.new('haproxy-ruby-mocksock')
10
+ path = tmp.path
11
+ tmp.close!
12
+ path
13
+ end
14
+
15
+ def with_socket action = nil, &block
16
+ path = self.temp_file_path
17
+
18
+ UNIXServer.open(path) do |socket|
19
+ pid = Process.fork do
20
+ client = socket.accept
21
+ cmd, data = client.recvfrom(1024)
22
+ cmd.chomp!
23
+ client.send self.send(action || :unknown), 0
24
+ client.close
25
+ exit
26
+ end
27
+
28
+ block.call(path)
29
+
30
+ Process.kill "TERM", pid rescue nil
31
+ end
32
+ end
33
+
34
+ def unknown
35
+ "Unknown command"
36
+ end
37
+
38
+ def info
39
+ """Name: HAProxy
40
+ Version: 1.3.25
41
+ Release_date: 2010/06/16
42
+ Nbproc: 1
43
+ Process_num: 1
44
+ Pid: 20518
45
+ Uptime: 6d 7h07m46s
46
+ Uptime_sec: 544066
47
+ Memmax_MB: 0
48
+ Ulimit-n: 80110
49
+ Maxsock: 80110
50
+ Maxconn: 40000
51
+ Maxpipes: 0
52
+ CurrConns: 28
53
+ PipesUsed: 0
54
+ PipesFree: 0
55
+ Tasks: 125
56
+ Run_queue: 1
57
+ node: A1441
58
+ description
59
+ """
60
+ end
61
+
62
+ def with_errors
63
+ '''
64
+ [09/Mar/2011:16:05:48.038] frontend http-in (#1): invalid request
65
+ src 10.0.111.185, session #25765333, backend <NONE> (#-1), server <NONE> (#-1)
66
+ request length 352 bytes, error at position 23:
67
+
68
+ 00000 POS LWqEQR/710 HTTP/1.1\x00\x00Content-Type: application/x-fcs\x00\x00
69
+ 00058+ User- HTTP/1.1\r\n
70
+ 00074 kwave Flash\r\n
71
+ 00087 Host: 10.0.49.24\x00\x00Content-Length: 1\x00\x00P\r\n
72
+ 00128 xy-Connection: Keep\r\n
73
+ 00149 live\x00\x00Pragm\r\n
74
+ 00162 no-cache\x00\x00X-NovINet: v1.2\x00\x00\x00\n
75
+ 00192 \r\n
76
+ 00194 \n
77
+ 00195 k\xC3\xD9\xFB\x02\x9F\x02\r\x08\xBC\xDB\x1E\xD3Z[\xD4]\xDD%O\x0FX\x01
78
+ 00218+ \xDD\x02\xB4\x98\xB4r\x87\xDB\x88\xD4\xBD\xA6\xBD\x80@\x15\x9D\x1A\xA7
79
+ 00237+ \xE8,\x8B\x01\x1Ft\x97\x80\xDB\xBF\x87\xAD\x15\r\xDE\x9E\x0CL\x13 FB
80
+ 00259+ \x822\xAA\xE0G\x10\t\xC3\xBC\e\xD0!\xC2dYh/\x01\x8A~\x85]z1\xC00\xBF
81
+ 00286+ \xC5\x14\xFA\x7F\x03\xF2\xC1\x06\x10\r\n
82
+ 00297 X\xA4Cqj0\xFC\x81\x14\xF2U\n
83
+ 00309 (0\xBF\xC12<\xA0:\xB2\x84\xC5\xA1\xFAa01\xD0-\xF2\x85\xC8\xB0\r\n
84
+ 00333 \x96$\x16\xB6\xB2\r,\x1D\x8B\x93\xAED\xBA\x9BE\r\n
85
+ 00350 \r\n
86
+ '''
87
+ end
88
+
89
+ def without_errors
90
+ ""
91
+ end
92
+
93
+ def with_sessions
94
+ '''0xa14a288: proto=tcpv4 src=10.0.16.1:56772 fe=http-in be=gelderlander_textlink srv=mongrel-10306 as=0 ts=08 age=49s calls=5 rq[f=00f0a0h,l=0,an=00h,rx=,wx=,ax=] rp[f=001000h,l=0,an=10h,rx=4s,wx=,ax=] s0=[7,0h,fd=13,ex=] s1=[7,0h,fd=16,ex=] exp=4s
95
+ 0x9745898: proto=tcpv4 src=10.0.73.31:2778 fe=http-in be=gelderlander_textlink srv=mongrel-10315 as=0 ts=08 age=42s calls=4 rq[f=009080h,l=0,an=00h,rx=,wx=,ax=] rp[f=001000h,l=0,an=10h,rx=8s,wx=,ax=] s0=[7,0h,fd=19,ex=] s1=[7,8h,fd=20,ex=] exp=8s
96
+ 0xa26cff0: proto=tcpv4 src=10.0.221.60:19546 fe=http-in be=limburger_textlink srv=mongrel-10708 as=0 ts=08 age=40s calls=4 rq[f=009080h,l=0,an=00h,rx=,wx=,ax=] rp[f=001000h,l=0,an=10h,rx=10s,wx=,ax=] s0=[7,0h,fd=25,ex=] s1=[7,8h,fd=26,ex=] exp=10s
97
+ 0x9f2dff0: proto=tcpv4 src=10.0.45.76:59090 fe=http-in be=tctubantia_textlink srv=mongrel-10507 as=0 ts=08 age=38s calls=5 rq[f=00f0a0h,l=0,an=00h,rx=,wx=,ax=] rp[f=001000h,l=0,an=10h,rx=11s,wx=,ax=] s0=[7,0h,fd=23,ex=] s1=[7,0h,fd=24,ex=] exp=11s
98
+ 0xa2cb998: proto=tcpv4 src=10.0.108.32:1734 fe=http-in be=tctubantia_textlink srv=mongrel-10505 as=0 ts=08 age=37s calls=3 rq[f=009080h,l=0,an=00h,rx=,wx=,ax=] rp[f=001000h,l=0,an=10h,rx=12s,wx=,ax=] s0=[7,0h,fd=28,ex=] s1=[7,8h,fd=29,ex=] exp=12s
99
+ 0xa04b7e8: proto=tcpv4 src=10.0.148.59:1158 fe=http-in be=destentor_textlink srv=mongrel-10403 as=0 ts=08 age=34s calls=4 rq[f=009080h,l=0,an=00h,rx=,wx=,ax=] rp[f=001000h,l=0,an=10h,rx=16s,wx=,ax=] s0=[7,0h,fd=32,ex=] s1=[7,8h,fd=33,ex=] exp=16s
100
+ 0x97d0b90: proto=tcpv4 src=10.0.133.111:50836 fe=http-in be=tctubantia_textlink srv=mongrel-10510 as=0 ts=08 age=21s calls=4 rq[f=009080h,l=0,an=00h,rx=,wx=,ax=] rp[f=001000h,l=0,an=10h,rx=29s,wx=,ax=] s0=[7,0h,fd=1,ex=] s1=[7,8h,fd=8,ex=] exp=29s
101
+ 0x96b1220: proto=tcpv4 src=10.0.217.27:14445 fe=http-in be=gelderlander_textlink srv=mongrel-10310 as=0 ts=08 age=19s calls=4 rq[f=009080h,l=0,an=00h,rx=,wx=,ax=] rp[f=001000h,l=0,an=10h,rx=30s,wx=,ax=] s0=[7,0h,fd=21,ex=] s1=[7,8h,fd=22,ex=] exp=30s
102
+ 0xa12e7a8: proto=tcpv4 src=10.0.34.84:49421 fe=http-in be=gelderlander_textlink srv=mongrel-10303 as=0 ts=08 age=16s calls=4 rq[f=009080h,l=0,an=00h,rx=,wx=,ax=] rp[f=001000h,l=0,an=10h,rx=33s,wx=,ax=] s0=[7,0h,fd=15,ex=] s1=[7,8h,fd=17,ex=] exp=33s
103
+ 0x9b5f570: proto=tcpv4 src=10.0.242.177:50124 fe=http-in be=limburger_textlink srv=mongrel-10701 as=0 ts=08 age=14s calls=4 rq[f=009080h,l=0,an=00h,rx=,wx=,ax=] rp[f=001000h,l=0,an=10h,rx=35s,wx=,ax=] s0=[7,0h,fd=38,ex=] s1=[7,8h,fd=40,ex=] exp=35s
104
+ 0x9fda668: proto=unix_stream as=2 ts=09 age=0s calls=2 rq[f=00e042h,l=10,an=20h,rx=10s,wx=,ax=] rp[f=048060h,l=2499,an=00h,rx=,wx=10s,ax=] s0=[7,0h,fd=2,ex=] s1=[0,0h,fd=-1,ex=] exp=10s
105
+ '''
106
+ end
107
+
108
+ def without_sessions
109
+ ""
110
+ end
111
+
112
+ end
113
+ end
114
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: haproxy-decklin
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Leandro López (inkel)
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-07 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: This gem is intended for use as a HAProxy interface when you need to
15
+ read statistics or if you like to manage proxies thru Ruby
16
+ email:
17
+ - inkel.ar@gmail.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - .gitignore
23
+ - Gemfile
24
+ - README.md
25
+ - Rakefile
26
+ - haproxy.gemspec
27
+ - lib/haproxy.rb
28
+ - lib/haproxy/csv_parser.rb
29
+ - lib/haproxy/socket_reader.rb
30
+ - lib/haproxy/stats_reader.rb
31
+ - lib/haproxy/version.rb
32
+ - spec/haproxy_spec.rb
33
+ - spec/socket_reader_spec.rb
34
+ - spec/spec_helper.rb
35
+ homepage: https://github.com/inkel/haproxy-ruby
36
+ licenses: []
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ! '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ requirements: []
54
+ rubyforge_project: haproxy
55
+ rubygems_version: 1.8.11
56
+ signing_key:
57
+ specification_version: 3
58
+ summary: HAProxy interface for reading statistics or managing servers (requires HAProxy
59
+ 1.4+)
60
+ test_files:
61
+ - spec/haproxy_spec.rb
62
+ - spec/socket_reader_spec.rb
63
+ - spec/spec_helper.rb