haproxy 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -2,3 +2,7 @@ source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in haproxy.gemspec
4
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/haproxy.gemspec CHANGED
@@ -4,13 +4,13 @@ require "haproxy/version"
4
4
 
5
5
  Gem::Specification.new do |s|
6
6
  s.name = "haproxy"
7
- s.version = Haproxy::VERSION
7
+ s.version = HAProxy::VERSION
8
8
  s.platform = Gem::Platform::RUBY
9
9
  s.authors = ["Leandro López (inkel)"]
10
10
  s.email = ["inkel.ar@gmail.com"]
11
- s.homepage = "http://rubygems.org/gems/haproxy"
12
- s.summary = %q{HAProxy interface for reading statistics or managing servers (requires HAProxy 1.4+)}
13
- s.description = %q{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}
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
14
 
15
15
  s.rubyforge_project = "haproxy"
16
16
 
@@ -6,6 +6,7 @@ module HAProxy
6
6
  class SocketReader < HAProxy::StatsReader
7
7
 
8
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)
9
10
  @path = path
10
11
  end
11
12
 
@@ -48,7 +49,7 @@ module HAProxy
48
49
 
49
50
  params[:proxy] = "-1" if params[:proxy].eql?(:all)
50
51
  params[:server] = "-1" if params[:server].eql?(:all)
51
-
52
+
52
53
  types = [types] unless types.is_a?(Array)
53
54
 
54
55
  params[:type] = case
@@ -1,3 +1,3 @@
1
- module Haproxy
2
- VERSION = "0.0.1"
1
+ module HAProxy
2
+ VERSION = "0.0.2"
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,118 @@
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
+
20
+ Thread.abort_on_exception = true
21
+
22
+ Process.fork do
23
+ # print "#{self} awaiting for connections in #{path}\n"
24
+ client = socket.accept
25
+ # print "#{client} connected\n"
26
+ cmd, data = client.recvfrom(1024)
27
+ cmd.chomp!
28
+ # print "#{self} received '#{cmd}' from #{client}/#{data}\n"
29
+ client.send self.send(action || :unknown), 0
30
+ client.close
31
+ # print "#{self} closing\n"
32
+ end
33
+
34
+ block.call(path)
35
+ end
36
+ end
37
+
38
+ def unknown
39
+ "Unknown command"
40
+ end
41
+
42
+ def info
43
+ """Name: HAProxy
44
+ Version: 1.3.25
45
+ Release_date: 2010/06/16
46
+ Nbproc: 1
47
+ Process_num: 1
48
+ Pid: 20518
49
+ Uptime: 6d 7h07m46s
50
+ Uptime_sec: 544066
51
+ Memmax_MB: 0
52
+ Ulimit-n: 80110
53
+ Maxsock: 80110
54
+ Maxconn: 40000
55
+ Maxpipes: 0
56
+ CurrConns: 28
57
+ PipesUsed: 0
58
+ PipesFree: 0
59
+ Tasks: 125
60
+ Run_queue: 1
61
+ node: A1441
62
+ description
63
+ """
64
+ end
65
+
66
+ def with_errors
67
+ '''
68
+ [09/Mar/2011:16:05:48.038] frontend http-in (#1): invalid request
69
+ src 193.173.111.185, session #25765333, backend <NONE> (#-1), server <NONE> (#-1)
70
+ request length 352 bytes, error at position 23:
71
+
72
+ 00000 POS LWqEQR/710 HTTP/1.1\x00\x00Content-Type: application/x-fcs\x00\x00
73
+ 00058+ User- HTTP/1.1\r\n
74
+ 00074 kwave Flash\r\n
75
+ 00087 Host: 77.74.49.24\x00\x00Content-Length: 1\x00\x00P\r\n
76
+ 00128 xy-Connection: Keep\r\n
77
+ 00149 live\x00\x00Pragm\r\n
78
+ 00162 no-cache\x00\x00X-NovINet: v1.2\x00\x00\x00\n
79
+ 00192 \r\n
80
+ 00194 \n
81
+ 00195 k\xC3\xD9\xFB\x02\x9F\x02\r\x08\xBC\xDB\x1E\xD3Z[\xD4]\xDD%O\x0FX\x01
82
+ 00218+ \xDD\x02\xB4\x98\xB4r\x87\xDB\x88\xD4\xBD\xA6\xBD\x80@\x15\x9D\x1A\xA7
83
+ 00237+ \xE8,\x8B\x01\x1Ft\x97\x80\xDB\xBF\x87\xAD\x15\r\xDE\x9E\x0CL\x13 FB
84
+ 00259+ \x822\xAA\xE0G\x10\t\xC3\xBC\e\xD0!\xC2dYh/\x01\x8A~\x85]z1\xC00\xBF
85
+ 00286+ \xC5\x14\xFA\x7F\x03\xF2\xC1\x06\x10\r\n
86
+ 00297 X\xA4Cqj0\xFC\x81\x14\xF2U\n
87
+ 00309 (0\xBF\xC12<\xA0:\xB2\x84\xC5\xA1\xFAa01\xD0-\xF2\x85\xC8\xB0\r\n
88
+ 00333 \x96$\x16\xB6\xB2\r,\x1D\x8B\x93\xAED\xBA\x9BE\r\n
89
+ 00350 \r\n
90
+ '''
91
+ end
92
+
93
+ def without_errors
94
+ ""
95
+ end
96
+
97
+ def with_sessions
98
+ '''0xa14a288: proto=tcpv4 src=74.125.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
99
+ 0x9745898: proto=tcpv4 src=86.89.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
100
+ 0xa26cff0: proto=tcpv4 src=194.151.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
101
+ 0x9f2dff0: proto=tcpv4 src=86.86.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
102
+ 0xa2cb998: proto=tcpv4 src=82.75.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
103
+ 0xa04b7e8: proto=tcpv4 src=80.254.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
104
+ 0x97d0b90: proto=tcpv4 src=82.75.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
105
+ 0x96b1220: proto=tcpv4 src=82.136.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
106
+ 0xa12e7a8: proto=tcpv4 src=190.98.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
107
+ 0x9b5f570: proto=tcpv4 src=82.95.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
108
+ 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
109
+ '''
110
+ end
111
+
112
+ def without_sessions
113
+ ""
114
+ end
115
+
116
+ end
117
+ end
118
+ end
metadata CHANGED
@@ -1,12 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: haproxy
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 0
8
- - 1
9
- version: 0.0.1
4
+ prerelease:
5
+ version: 0.0.2
10
6
  platform: ruby
11
7
  authors:
12
8
  - "Leandro L\xC3\xB3pez (inkel)"
@@ -14,7 +10,7 @@ autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
12
 
17
- date: 2010-11-29 00:00:00 -03:00
13
+ date: 2011-03-09 00:00:00 -03:00
18
14
  default_executable:
19
15
  dependencies: []
20
16
 
@@ -30,6 +26,7 @@ extra_rdoc_files: []
30
26
  files:
31
27
  - .gitignore
32
28
  - Gemfile
29
+ - README.md
33
30
  - Rakefile
34
31
  - haproxy.gemspec
35
32
  - lib/haproxy.rb
@@ -37,8 +34,11 @@ files:
37
34
  - lib/haproxy/socket_reader.rb
38
35
  - lib/haproxy/stats_reader.rb
39
36
  - lib/haproxy/version.rb
37
+ - spec/haproxy_spec.rb
38
+ - spec/socket_reader_spec.rb
39
+ - spec/spec_helper.rb
40
40
  has_rdoc: true
41
- homepage: http://rubygems.org/gems/haproxy
41
+ homepage: https://github.com/inkel/haproxy-ruby
42
42
  licenses: []
43
43
 
44
44
  post_install_message:
@@ -51,21 +51,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- segments:
55
- - 0
56
54
  version: "0"
57
55
  required_rubygems_version: !ruby/object:Gem::Requirement
58
56
  none: false
59
57
  requirements:
60
58
  - - ">="
61
59
  - !ruby/object:Gem::Version
62
- segments:
63
- - 0
64
60
  version: "0"
65
61
  requirements: []
66
62
 
67
63
  rubyforge_project: haproxy
68
- rubygems_version: 1.3.7
64
+ rubygems_version: 1.6.1
69
65
  signing_key:
70
66
  specification_version: 3
71
67
  summary: HAProxy interface for reading statistics or managing servers (requires HAProxy 1.4+)