pax_global_header00006660000000000000000000000064130130653010014502gustar00rootroot0000000000000052 comment=75d1ebbb66ce9ff74d2311828b02e3fb94a7a87f haproxyctl-1.4.3/000077500000000000000000000000001301306530100137045ustar00rootroot00000000000000haproxyctl-1.4.3/.gitignore000066400000000000000000000002321301306530100156710ustar00rootroot00000000000000*.gem *.rbc .bundle .config .yardoc Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp haproxyctl-1.4.3/.rubocop.yml000066400000000000000000000000371301306530100161560ustar00rootroot00000000000000inherit_from: rubocop-todo.yml haproxyctl-1.4.3/Gemfile000066400000000000000000000001371301306530100152000ustar00rootroot00000000000000source 'https://rubygems.org' # Specify your gem's dependencies in haproxyctl.gemspec gemspec haproxyctl-1.4.3/LICENSE000066400000000000000000000023471301306530100147170ustar00rootroot00000000000000Copyright (C) 2010-2013 author: Carlo Flores contributors: Scott Gonyea, John A. Barbuto, Ben Lovett, Till Klampaeckel, Erik Osterman, Martin Hald, DeniedBoarding, Aaron Blew, Nick Griffiths Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. haproxyctl-1.4.3/README.md000066400000000000000000000417421301306530100151730ustar00rootroot00000000000000HAProxyCTL ========== This is a simple wrapper to make life with HAProxy a little more convenient. * Acts as an init script for start, stop, reload, restart, etc * Leverages 'socket' to enable and disable servers on the fly * Formats server weight and backends in a readable way * Provides Nagios and Cloudkick health checks * chkconfig/service-able for Redhat folk [Here](http://lo.ladevops.org/haproxyctl) is a presentation about it. Hit space to advance slides. Installation ------------ On most UNIX, assuming HAProxy is in the $PATH:
git clone git@github.com:flores/haproxyctl.git
ln -s haproxyctl/haproxyctl /etc/init.d/haproxyctl
For chkconfig/RedHat/Centos, add:
chkconfig --add haproxyctl
Or if have RubyGems, just gem install it!
gem install haproxyctl
Or if you are on Debian, just install haproxy with apt-get!
apt-get install haproxyctl
There is also an HAProxy source installation script. This installs not only the steps above but also HAProxy itself. Options -----------------
# ./haproxyctl help
usage: ./haproxyctl 
  where argument can be:
    start: start haproxy unless it is already running
    stop: stop an existing haproxy
    restart: immediately shutdown and restart
    reload: gracefully terminate existing connections, reload /etc/haproxy/haproxy.cfg
    status: is haproxy running?  on what ports per lsof?
    configcheck: check /etc/haproxy/haproxy.cfg
    nagios: nagios-friendly status for running process and listener
    cloudkick: cloudkick.com-friendly status and metric for connected users
    show health: show status of all frontends and backend servers
    show backends: show status of backend pools of servers
    enable all : re-enable a server previously in maint mode on multiple backends
    disable all : disable a server from every backend it exists
    enable all EXCEPT : like 'enable all', but re-enables every backend except for 
    disable all EXCEPT : like 'disable all', but disables every backend except for 
    clear counters: clear max statistics counters (add 'all' for all counters)
    help: this message
    prompt: toggle interactive mode with prompt
    quit: disconnect
    show info: report information about the running process
    show stat [counter...]: report counters for each proxy and server
    show errors: report last request and response errors for each proxy
    show sess [id]: report the list of current sessions or dump this session
    get weight: report a server's current weight
    set weight: change a server's weight
    set timeout: change a timeout setting
    disable server: set a server in maintenance mode
    enable server: re-enable a server that was previously in maintenance mode
Examples -------- ## Status check
  ./haproxyctl status
  haproxy is running on pid 23162.
  these ports are used and guys are connected:
  173.255.194.115:www->98.154.245.132:52025 (ESTABLISHED)
  173.255.194.115:www->97.89.32.126:52043 (ESTABLISHED)
  *:www (LISTEN)
  *:53093 
  173.255.194.115:www->83.39.69.106:19338 (ESTABLISHED)
## Errors to the backend servers
  ./haproxyctl "show errors"
  [04/Feb/2011:21:05:59.542] frontend http (#1): invalid request
    src 209.59.188.205, session #39574, backend  (#-1), server  (#-1)
    request length 125 bytes, error at position 27:
 
    00000  GET /logs/images/stuff/someurl
    00070+  HTTP/1.1\r\n
    00081  Host: wet.biggiantnerds.com\r\n
    00110  Accept: */*\r\n
    00123  \r\n
## Human readable health check
  ./haproxyctl "show health"
    pxname      svname       status  weight
  http        FRONTEND                  OPEN       
  sinatra     sinatra_downoi            DOWN    1  
  sinatra     sinatra_rindica           DOWN    1  
  sinatra     sinatra_guinea            UP      1  
  sinatra     BACKEND                   UP      1  
  ei          guinea                    UP      1  
  ei          belem                     UP      1  
  ei          BACKEND                   UP      1  
  drop        guinea                    UP      1  
  drop        belem                     UP      1  
  drop        BACKEND                   UP      1  
  apache      guinea                    UP      1  
  apache      belem                     UP      1  
  apache      BACKEND                   UP      1  
  static      ngnix_downoi              UP      1  
  static      ngnix_petite              UP      1  
  static      ngnix_rindica             UP      1  
  static      nginx_stellatus           UP      1  
  static      nginx_belem               UP      1  
  static      nginx_petite              DOWN    1  
  static      apache_guinea             UP      1  
  static      BACKEND                   UP      6  
  ssh         localhost                 UP      1  
  ssh         BACKEND                   UP      1  

  ./haproxyctl "show backends"
  contact     BACKEND                   UP      1
  alpha       BACKEND                   DOWN    0
  sinatra     BACKEND                   DOWN    0
  python      BACKEND                   UP      1
  mobile      BACKEND                   DOWN    0
  ei          BACKEND                   UP      1
  showoff     BACKEND                   UP      1
  drop        BACKEND                   UP      1
  cheap       BACKEND                   UP      1
  apache      BACKEND                   UP      1
  static      BACKEND                   UP      1
  ssh         BACKEND                   UP      1
## Disable servers on the fly
  ./haproxyctl "disable server static/nginx_belem"
  
  ./haproxyctl "show health" |grep nginx_belem
  static      nginx_belem               MAINT   1 
## Graceful reloads
  ./haproxyctl reload
  gracefully stopping connections on pid 23162...
  checking if connections still alive on 23162...
  reloaded haproxy on pid 1119
## Cloudkick/Nagios checks with graph-friendly output for queue size, total connections, etc
  ./haproxyctl cloudkick    
  status ok haproxy is running
  metric connections int 12
  metric http_FRONTEND_request_rate int 45
  metric http_FRONTEND_health_check_duration int 45
  metric sinatra_sinatra_guinea_health_check_duration int 4
  metric sinatra_BACKEND_health_check_duration int 4
  metric mobile_sinatra_mobile_health_check_duration int 2
  metric mobile_BACKEND_health_check_duration int 2
  metric ei_guinea_health_check_duration int 4
  metric ei_BACKEND_health_check_duration int 4
  metric drop_guinea_total_requests gauge 1
  metric drop_guinea_health_check_duration int 6
  metric drop_BACKEND_total_requests gauge 1
  metric drop_BACKEND_health_check_duration int 6
  metric apache_guinea_health_check_duration int 41
  metric apache_BACKEND_health_check_duration int 41
  metric static_ngnix_downoi_total_requests gauge 472
  metric static_ngnix_downoi_health_check_duration int 7
  metric static_ngnix_petite_total_requests gauge 475
  metric static_ngnix_petite_health_check_duration int 8
  metric static_ngnix_rindica_total_requests gauge 457
  metric static_ngnix_rindica_health_check_duration int 8
  metric static_nginx_stellatus_total_requests gauge 470
  metric static_nginx_stellatus_health_check_duration int 7
  metric static_nginx_belem_total_requests gauge 460
  metric static_nginx_belem_health_check_duration int 8
  metric static_apache_guinea_total_requests gauge 449
  metric static_apache_guinea_health_check_duration int 14
  metric static_BACKEND_total_requests gauge 2783
  metric static_BACKEND_health_check_duration int 45
## does normal things like checks if a process is running before starting it...
  ./haproxyctl start    
  ./haproxyctl:35: haproxy is already running on pid 20317! (RuntimeError)
  
  ./haproxyctl restart
  stopping existing haproxy on pid 20317...
  waiting a ms...
  checking if haproxy is still running...
  starting haproxy...
  done.  running on pid 20348
## keeps all the regular UNIX socket stuff
  ./haproxyctl "show stat"
  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,
  http,FRONTEND,,,3,82,2000,39585,47067637,12818945246,0,0,1465,,,,,OPEN,,,,,,,,,1,1,0,,,,0,0,0,59,,,,0,91460,13125,4115,305,73,,0,131,109078,,,
  sinatra,sinatra_downoi,0,0,0,1,,791,452469,2258353,,0,,0,0,0,0,UP,1,1,0,60,13,304106,59545,,1,2,1,,791,,2,0,,1,L4OK,,46,0,736,0,40,15,0,0,,,,0,0,
  sinatra,sinatra_rindica,0,0,0,1,,795,450488,2333534,,0,,0,0,3,1,UP,1,1,0,68,10,347679,52849,,1,2,2,,792,,2,0,,1,L4OK,,46,0,753,0,28,10,0,0,,,,0,0,
  sinatra,sinatra_guinea,0,0,0,7,,638,360994,1046343,,0,,0,258,1,0,UP,1,1,0,4,4,1892969,72241,,1,2,3,,637,,2,0,,3,L4OK,,0,0,317,0,13,11,0,0,,,,0,0,
  sinatra,BACKEND,0,0,0,7,0,2219,1263951,5638230,0,0,,0,299,4,1,UP,3,3,0,,0,2144680,0,,1,2,0,,2220,,1,0,,3,,,,0,1806,0,81,291,41,,,,,0,0,
  ei,guinea,0,0,0,4,,3514,2067456,68408884,,0,,0,0,0,0,UP,1,1,0,6,1,2142278,70,,1,3,1,,3514,,2,0,,11,L4OK,,0,0,3060,323,131,0,0,0,,,,3,0,
  ei,belem,0,0,0,0,,0,0,0,,0,,0,0,0,0,UP,1,0,1,28,7,259858,1274,,1,3,2,,0,,2,0,,0,L4OK,,43,0,0,0,0,0,0,0,,,,0,0,
  ei,BACKEND,0,0,0,4,0,3514,2067456,68408884,0,0,,0,0,0,0,UP,1,1,1,,0,2144680,0,,1,3,0,,3514,,1,0,,11,,,,0,3060,323,131,0,0,,,,,3,0,
  drop,guinea,0,0,0,2,,1042,634412,15327695,,0,,0,0,0,0,UP,1,1,0,5,1,2142277,70,,1,4,1,,1042,,2,0,,5,L4OK,,0,0,935,28,79,0,0,0,,,,2,0,
  drop,belem,0,0,0,0,,0,0,0,,0,,0,0,0,0,UP,1,0,1,42,7,259855,958,,1,4,2,,0,,2,0,,0,L4OK,,44,0,0,0,0,0,0,0,,,,0,0,
  drop,BACKEND,0,0,0,2,0,1042,634412,15327695,0,0,,0,0,0,0,UP,1,1,1,,0,2144680,0,,1,4,0,,1042,,1,0,,5,,,,0,935,28,79,0,0,,,,,2,0,
  apache,guinea,0,0,0,3,,3781,3733003,19959026,,0,,0,0,0,0,UP,1,1,0,4,1,2142276,70,,1,5,1,,3781,,2,0,,5,L4OK,,0,0,3267,304,208,2,0,0,,,,2,0,
  apache,belem,0,0,0,1,,1,379,528,,0,,0,0,0,0,UP,1,0,1,41,7,259854,1023,,1,5,2,,1,,2,0,,1,L4OK,,43,0,0,0,1,0,0,0,,,,0,0,
  apache,BACKEND,0,0,0,3,0,3782,3733382,19959554,0,0,,0,0,0,0,UP,1,1,1,,0,2144680,0,,1,5,0,,3782,,1,0,,5,,,,0,3267,304,209,2,0,,,,,2,0,
  static,ngnix_downoi,0,0,0,10,,12665,4970818,1883260969,,0,,0,4,25,5,UP,1,1,0,72,10,303928,61648,,1,6,1,,12640,,2,0,,10,L4OK,,46,0,10671,1656,307,0,0,0,,,,1167,4,
  static,ngnix_petite,0,0,0,10,,13052,5141468,2033386644,,0,,1,5,13,3,UP,1,1,0,63,6,347401,11776,,1,6,2,,13039,,2,0,,10,L4OK,,46,0,10988,1694,352,0,0,0,,,,1223,4,
  static,ngnix_rindica,0,0,0,10,,12736,5007655,2002399557,,0,,0,8,20,5,UP,1,1,0,64,10,347499,55375,,1,6,3,,12716,,2,0,,10,L4OK,,45,0,10736,1649,321,0,0,0,,,,1146,3,
  static,nginx_stellatus,0,0,0,10,,15142,6017327,2194578425,,0,,0,7,0,0,UP,1,1,0,8,1,1555595,786,,1,6,4,,15142,,2,0,,10,L4OK,,42,0,12932,1844,364,0,0,0,,,,1253,8,
  static,nginx_belem,0,0,0,10,,15227,6075157,2231761586,,0,,0,5,1,0,UP,1,1,0,10,1,1555573,787,,1,6,5,,15226,,2,0,,12,L4OK,,44,0,12981,1882,362,0,0,0,,,,1227,4,
  static,nginx_petite,0,0,0,0,,0,0,0,,0,,0,0,0,0,DOWN,1,1,0,0,1,2144610,2144610,,1,6,6,,0,,2,0,,0,L4CON,,21000,0,0,0,0,0,0,0,,,,0,0,
  static,apache_guinea,0,0,0,10,,24091,9895320,2263895160,,0,,0,0,0,0,UP,1,1,0,2,0,2144680,0,,1,6,7,,24091,,2,0,,100,L4OK,,0,0,20593,3038,459,0,0,0,,,,1241,0,
  static,BACKEND,0,0,0,60,0,92841,37107745,12609282341,0,0,,1,29,59,13,UP,6,6,0,,0,2144680,0,,1,6,0,,92854,,1,0,,131,,,,0,78901,11763,2165,12,0,,,,,7257,23,
  ssh,localhost,0,0,0,3,,122,54524,291662,,0,,0,0,0,0,UP,1,1,0,0,0,2144680,0,,1,7,1,,122,,2,0,,10,L4OK,,0,0,121,0,1,0,0,0,,,,0,0,
  ssh,BACKEND,0,0,0,3,0,122,54524,291662,0,0,,0,0,0,0,UP,1,1,0,,0,2144680,0,,1,7,0,,122,,1,0,,10,,,,0,121,0,1,0,0,,,,,0,0,
### Extends stat command to print only counters supplied as arguments
  ./haproxyctl "show stat qcur qmax"
  http,FRONTEND,,
  sinatra,sinatra_downoi,0,0
  sinatra,sinatra_rindica,0,0
  sinatra,sinatra_guinea,0,0
  sinatra,BACKEND,0,0
  ei,guinea,0,0
  ei,belem,0,0
  ei,BACKEND,0,0
  drop,guinea,0,0
  drop,belem,0,0
  drop,BACKEND,0,0
  apache,guinea,0,0
  apache,belem,0,0
  apache,BACKEND,0,0
  static,ngnix_downoi,0,0
  static,ngnix_petite,0,0
  static,ngnix_rindica,0,0
  static,nginx_stellatus,0,0
  static,nginx_belem,0,0
  static,nginx_petite,0,0
  static,apache_guinea,0,0
  static,BACKEND,0,0
  ssh,localhost,0,0
  ssh,BACKEND,0,0
## Enable or disable a target server from every backend it appears.
  ./haproxyctl "show health"
  # pxname        svname               status  weight
  http            FRONTEND             OPEN       
  sinatra         sinatra_downoi       DOWN    1  
  sinatra         sinatra_rindica      DOWN    1  
  sinatra         sinatra_guinea       UP      1  
  sinatra         BACKEND              UP      1  
  ei              guinea               UP      1  
  ei              BACKEND              UP      1  
  drop            guinea               UP      1  
  drop            BACKEND              UP      1  
  apache          guinea               UP      1  
  apache          BACKEND              UP      1  
  static          ngnix_downoi         UP      1  
  static          ngnix_petite         UP      1  
  static          ngnix_rindica        UP      1  
  static          nginx_stellatus      UP      1  
  static          nginx_belem          UP      1  
  static          nginx_petite         MAINT   1  
  static          apache_guinea        UP      1  
  static          BACKEND              UP      6  
  ssh             localhost            UP      1  
  ssh             BACKEND              UP      1  
  
                                                 
  ./haproxyctl "disable all guinea"
  ./haproxyctl "show health"
    pxname        svname               status  weight
  http            FRONTEND             OPEN       
  sinatra         sinatra_downoi       DOWN    1  
  sinatra         sinatra_rindica      DOWN    1  
  sinatra         sinatra_guinea       UP      1  
  sinatra         BACKEND              UP      1  
  ei              guinea               MAINT   1  
  ei              BACKEND              DOWN    0  
  drop            guinea               MAINT   1  
  drop            BACKEND              DOWN    0  
  apache          guinea               MAINT   1  
  apache          BACKEND              DOWN    0  
  static          ngnix_downoi         UP      1  
  static          ngnix_petite         UP      1  
  static          ngnix_rindica        UP      1  
  static          nginx_stellatus      UP      1  
  static          nginx_belem          UP      1  
  static          nginx_petite         UP      1  
  static          apache_guinea        UP      1  
  static          BACKEND              UP      1  
  ssh             localhost            UP      1  
  ssh             BACKEND              UP      1  
## Has an EXCEPT flag, too
  ./haproxyctl "enable all EXCEPT apache_guinea"
  ./haproxyctl "show health"
    pxname        svname               status  weight
  http            FRONTEND             OPEN       
  sinatra         sinatra_downoi       DOWN    1  
  sinatra         sinatra_rindica      DOWN    1  
  sinatra         sinatra_guinea       UP      1  
  sinatra         BACKEND              UP      1  
  ei              guinea               UP      1  
  ei              BACKEND              UP      1  
  drop            guinea               UP      1  
  drop            BACKEND              UP      1  
  apache          guinea               UP      1  
  apache          BACKEND              UP      1  
  static          ngnix_downoi         UP 1/2  1  
  static          ngnix_petite         UP 1/2  1  
  static          ngnix_rindica        UP 1/2  1  
  static          nginx_stellatus      UP 1/2  1  
  static          nginx_belem          UP 1/2  1  
  static          nginx_petite         UP 1/2  1  
  static          apache_guinea        UP      1  
  static          BACKEND              UP      7  
  ssh             localhost            UP      1  
  ssh             BACKEND              UP      1 
Contributors ------------ - [flores aka `flores`](https://github.com/flores) - [Scott Gonyea aka `sgonyea`](https://github.com/sgonyea) - [Ben Lovett aka `blovett`](https://github.com/blovett) - [John A. Barbuto aka `jbarbuto`](https://github.com/jbarbuto) - [Till Klampaeckel aka `till`](https://github.com/till) - [Erik Osterman aka `osterman`](https://github.com/osterman) - [Martin Hald aka `mhald`](https://github.com/mhald) - [deniedboarding](https://github.com/deniedboarding) - [Aaron Blew aka `blewa`](https://github.com/blewa) - [Nick Griffiths aka `nicobrevin`](https://github.com/nicobrevin) - [Florian Holzhauer aka `fh`](https://github.com/fh) - [Jonas Genannt aka `hggh`](https://github.com/hggh) - [Grant Shively aka `gshively11`](https://github.com/gshively11) Non-current HAProxy versions ------------ Be aware that HAProxy below version 1.4 does not support many of the options of haproxyctl. License ----------------- This code is released under the MIT License. You should feel free to do whatever you want with it. haproxyctl-1.4.3/Rakefile000066400000000000000000000000601301306530100153450ustar00rootroot00000000000000#!/usr/bin/env rake require "bundler/gem_tasks" haproxyctl-1.4.3/bin/000077500000000000000000000000001301306530100144545ustar00rootroot00000000000000haproxyctl-1.4.3/bin/haproxyctl000077500000000000000000000145311301306530100166030ustar00rootroot00000000000000#!/usr/bin/env ruby # # HAProxy control script to start, stop, restart, configcheck, etc, as # well as communicate to the stats socket. # # See https://github.com/flores/haproxyctl/README # # This line here is just for Redhat users who like "service haproxyctl blah" # chkconfig: 2345 80 30 # description: HAProxy is a fast and reliable load balancer for UNIX systems # HAProxyctl is an easy way to do init shit and talk to its stats socket # require 'pathname' lib = File.join(File.dirname(Pathname.new(__FILE__).realpath), '../lib') $LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib) require 'haproxyctl' include HAProxyCTL argument = ARGV.join(' ') unless has_exec? puts usage if argument =~ /help/ || ARGV.length < 1 fail 'Cannot find haproxy executable. Please ensure it is in your $PATH, or set $HAPROXY_BIN environment variable.' end display_usage! if argument =~ /help/ || ARGV.length < 1 begin case argument when 'start' if pidof fail("haproxy is already running on pid(s) #{pidof.join(', ')}!") else start end when 'stop' stop(check_running) when 'restart' if pidof stop(pidof) stillpidof = check_running while stillpidof == pidof puts "still haven't killed old pid. waiting 3s for existing connections to die... (ctrl+c to stop)" sleep 3 stillpidof = check_running || 0 end start else puts 'haproxy was not running. starting...' start end when 'reload' if pidof reload(pidof) else puts 'haproxy not running. starting...' start end when 'status' if pidof puts "haproxy is running on pid(s) #{pidof.join(', ')}.\nthese ports are used and guys are connected:" pidof.each do |pid| puts "Showing lsof for PID: #{pid}" system("lsof -ln -i |awk \'$2 ~ /#{pid}/ {print $8\" \"$9}\'") end else puts 'haproxy is not running' end when 'configcheck' puts `#{exec} -c -f #{config_path}` when 'nagios' if pidof puts 'OK' exit else puts 'CRITICAL: HAProxy is not running!' exit(2) end when 'cloudkick' puts 'Not supported, need to refactor after pidof changes.' # if pidof # puts 'status ok haproxy is running' # conn = `lsof -ln -i |grep -c #{pidof}`.chomp.to_i # # removes the listener # conn = conn - 1 # puts "metric connections int #{conn}" # status = unixsock('show stat') # status.each do |line| # line = line.split(',') # if line[0] !~ /^#/ # host = "#{line[0]}_#{line[1]}" # puts "metric #{host}_request_rate int #{line[47]}" if line[47].to_i > 0 # puts "metric #{host}_total_requests gauge #{line[49]}" if line[49].to_i > 0 # puts "metric #{host}_health_check_duration int #{line[35]}" if line[35].to_i > 0 # puts "metric ${host}_current_queue int #{line[3]}" if line[3].to_i > 0 # end # end # else # puts 'status err haproxy is not running!' # end when "statsd" if pidof status=unixsock("show stat") line=status[0].gsub!(/# /,'') HEADERS=line.split(',')[0..-2] CONFIG_INSTANCE_FILE = '/etc/haproxy/haproxyctl/instance-name' CONFIG_INSTANCE_DEFAULT_NAME = 'testing.localhost' INSTANCE = File.read(CONFIG_INSTANCE_FILE).chomp if File.exists?(CONFIG_INSTANCE_FILE) INSTANCE ||= CONFIG_INSTANCE_DEFAULT_NAME status.shift status.each do |line| if not line.chomp == "" stats=Hash[HEADERS.zip(line.split(','))] %w(scur smax ereq econ rate act chkfail chkdown ctime rtime ttime).each do |statname| next unless stats['svname'] == 'BACKEND' puts "HAProxy.#{INSTANCE}.#{stats['pxname']}.#{stats['svname']}.#{statname}:#{stats[statname]}|g" end end end end when 'show health' status = unixsock('show stat') status.each do |line| data = line.split(',') printf "%-30s %-30s %-7s %3s\n", data[0], data[1], data[17], data[18] end when /show backend(s?)/ status = unixsock('show stat').grep(/BACKEND/) status.each do |line| data = line.split(',') printf "%-30s %-30s %-7s %3s\n", data[0], data[1], data[17], data[18] end when /disable all EXCEPT (.+)/ servername = Regexp.last_match[ 1] status = unixsock('show stat') backend = status.grep(/#{servername}/) backend.each do |line| backend_group = line.split(',') status.each do |pool| data = pool.split(',') if (data[0] == backend_group[0]) && ( data[1] !~ /#{servername}|BACKEND|FRONTEND/) && ( data[17] == 'UP') unixsock("disable server #{data[0]}/#{data[1]}") end end end when /disable all (.+)/ servername = Regexp.last_match[ 1] status = unixsock('show stat') status.each do |line| data = line.split(',') if ( data[1] == servername) && ( data[17] == 'UP') unixsock("disable server #{data[0]}/#{servername}") end end when /enable all EXCEPT (.+)/ servername = Regexp.last_match[ 1] status = unixsock('show stat') backend = status.grep(/#{servername}/) backend.each do |line| backend_group = line.split(',') status.each do |pool| data = pool.split(',') if (data[0] == backend_group[0]) && ( data[1] !~ /#{servername}|BACKEND|FRONTEND/) && ( data[17] =~ /Down|MAINT/i) unixsock("enable server #{data[0]}/#{data[1]}") end end end when /show stat (.+)/ fieldnames = Regexp.last_match[ 1] status = unixsock('show stat') indices = fieldnames.split(' ').map do |name| status.first.split(',').index(name) || begin $stderr.puts("no such field: #{name}") $stderr.puts(" #{status.first}") exit 1 end end status[1..-1].each do |line| row = line.split(',') filtered = indices.map { |index| row[index] } puts (row[0...2] + filtered).compact.join(',') end when /enable all (.+)/ servername = Regexp.last_match[ 1] status = unixsock('show stat') status.each do |line| data = line.split(',') if ( data[1] == servername) && ( data[17] =~ /Down|MAINT/i) unixsock("enable server #{data[0]}/#{servername}") end end when 'version' version else puts unixsock(argument) end rescue Errno::ENOENT => e STDERR.puts e exit 1 end haproxyctl-1.4.3/haproxyctl000077700000000000000000000000001301306530100207072bin/haproxyctlustar00rootroot00000000000000haproxyctl-1.4.3/haproxyctl.gemspec000066400000000000000000000022141301306530100174450ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require File.expand_path('../lib/haproxyctl/version', __FILE__) Gem::Specification.new do |gem| gem.authors = ["Carlo Flores"] gem.email = ["github@petalphile.com"] gem.description = %q{This is a simple wrapper to make life with HAProxy a little more convenient. Acts as an init script for start, stop, reload, restart, etc. Leverages 'socket' to enable and disable servers on the fly. Formats server weight and backends in a readable way. Provides Nagios and Cloudkick health checks. Compatible with RHEL chkconfig/service.} gem.summary = %q{Wrapper to talk to the HAProxy socket, as well as regular init (start|stop|status|etc)} gem.homepage = "https://github.com/flores/haproxyctl" gem.rubyforge_project = "haproxyctl" gem.license = "MIT" gem.files = `git ls-files`.split($\) gem.files.reject! { |fn| fn.include? "rhapr" } gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.name = "haproxyctl" gem.require_paths = ["lib"] gem.version = HAProxyCTL::VERSION end haproxyctl-1.4.3/install-haproxy/000077500000000000000000000000001301306530100170425ustar00rootroot00000000000000haproxyctl-1.4.3/install-haproxy/haproxy_src_install.sh000077500000000000000000000053641301306530100235000ustar00rootroot00000000000000#!/bin/bash # # This installs latest HAProxy from source along with HAProxyCTL # # It will clobber files and stuff and is only meant as a very # quick and dirty (but sometimes handy) installer. # HAPROXYVER="1.5.3" MD5EXPECTED="e999a547d57445d5a5ab7eb6a06df9a1" STARTINGDIR=$PWD # make sure we have make, pcre and junk if [[ -e /etc/redhat-release ]]; then OS=redhat; elif [[ -e /etc/debian_version ]]; then OS=debian; fi if [[ -n $OS ]]; then if [[ ${OS} == 'redhat' ]]; then yum install -y pcre-devel make gcc libgcc git; elif [[ ${OS} == 'debian' ]]; then apt-get update; apt-get install -y libpcre3 libpcre3-dev build-essential libgcc1 git; fi else echo -e "I only understand Debian/RedHat/CentOS and this box does not appear to be any.\nExiting."; exit 2; fi # grab last stable. HAProxy's site versions nicely - these will still be here after the next update echo "I will try to build in /usr/local/src" mkdir /usr/local/src || echo "Oops, /usr/local/src exists!" cd /usr/local/src || exit 2 if [[ -e /usr/local/src/haproxy-${HAPROXYVER}.tar.gz ]]; then echo "using the existing haproxy-$HAPROXYVER.tar.gz. If you have problems maybe rm it and we will grab it again" else echo "I am grabbing ${HAPROXYVER} and will expand it under /usr/local/src/haproxy-${HAPROXYVER}" wget http://haproxy.1wt.eu/download/1.5/src/haproxy-$HAPROXYVER.tar.gz fi # check the checksum echo "Verifying the md5" MD5CHECK=$(md5sum /usr/local/src/haproxy-${HAPROXYVER}.tar.gz |awk '{print $1}') if [[ ${MD5CHECK} != ${MD5EXPECTED} ]] ; then echo -e "MD5s do not match!\nBailing."; exit 2; fi tar xvfz haproxy-${HAPROXYVER}.tar.gz rm haproxy-${HAPROXYVER}.tar.gz cd haproxy-${HAPROXYVER} echo "Making it!" if uname -a | grep x86_64 ; then make TARGET=linux26 CPU=x86_64 USE_PCRE=1 || exit 2 else make TARGET=linux26 CPU=686 USE_PCRE=1 || exit 2 fi if [[ -e /usr/local/haproxy ]]; then echo "Removing old haproxy from /usr/local/haproxy" rm -fr /usr/local/haproxy fi echo "Make installing!" make install if [[ -e /usr/sbin/haproxy ]]; then echo "Removing /usr/sbin/haproxy" rm -f /usr/sbin/haproxy fi echo "Symlinking /usr/local/sbin/haproxy to /usr/sbin/haproxy" ln -s /usr/local/sbin/haproxy /usr/sbin/haproxy echo "Grabbing latest haproxyctl" if [[ -e /usr/local/haproxyctl ]]; then cd /usr/local/haproxyctl; git pull; else cd /usr/local git clone https://github.com/flores/haproxyctl.git fi echo "dropping it into /etc/init.d/haproxyctl" ln -s /usr/local/haproxyctl/haproxyctl /etc/init.d/haproxyctl || exit 2 echo "removing make and gcc" if [[ ${OS} == 'redhat' ]]; then chkconfig --add haproxyctl; yum remove -y gcc make elif [[ ${OS} == 'debian' ]]; then apt-get purge -y build-essential fi cd $STARTINGDIR echo "I think it is all done!" haproxyctl-1.4.3/lib/000077500000000000000000000000001301306530100144525ustar00rootroot00000000000000haproxyctl-1.4.3/lib/haproxyctl.rb000066400000000000000000000102761301306530100172020ustar00rootroot00000000000000require 'haproxyctl/version' require 'haproxyctl/environment' require 'socket' module HAProxyCTL include Environment def start puts 'starting haproxy...' system("#{exec} -f #{config_path} -D -p #{pidfile}") newpids = check_running if newpids.all? {|newpid| newpid =~ /^\d+$/} puts "haproxy is running on pid #{newpids.join(', ')}" return true else puts 'error. haproxy did not start!' return nil end end def stop(pids) if pids puts "stopping haproxy on pids #{pids.join(', ')}..." pids.each { |pid| system("kill #{pid}") || system("kill -9 #{pid}") } puts '... stopped' else puts 'haproxy is not running!' end end def reload(pids) if pids puts "gracefully stopping connections on pids #{pids.join(', ')}..." system("#{exec} -D -f #{config_path} -p #{pidfile} -sf $(cat #{pidfile})") puts "checking if connections still alive on #{pids.join(', ')}..." nowpids = check_running while pids == nowpids puts "still haven't killed old pids. waiting 2s for existing connections to die... (ctrl+c to stop this check)" sleep 2 nowpids = check_running || 0 end puts "reloaded haproxy on pids #{nowpids.join(', ')}" else puts 'haproxy is not running!' end end def unixsock(command) output = [] runs = 0 begin ctl = UNIXSocket.open(socket) if ctl ctl.write "#{command}\r\n" else puts "cannot talk to #{socket}" end rescue Errno::EPIPE ctl.close sleep 0.5 runs += 1 if runs < 4 retry else puts "the unix socket at #{socket} closed before we could complete this request" exit end end while (line = ctl.gets) unless line =~ /Unknown command/ output << line end end ctl.close output end def display_usage! puts usage exit end def usage <<-USAGE usage: #{$PROGRAM_NAME} where can be: start : start haproxy unless it is already running stop : stop an existing haproxy restart : immediately shutdown and restart reload : gracefully terminate existing connections, reload #{config_path} status : is haproxy running? on what ports per lsof? configcheck : check #{config_path} nagios : nagios-friendly status for running process and listener cloudkick : cloudkick.com-friendly status and metric for connected users show health : show status of all frontends and backend servers show backends : show status of backend pools of servers enable all : re-enable a server previously in maint mode on multiple backends disable all : disable a server from every backend it exists enable all EXCEPT : like 'enable all', but re-enables every backend except for disable all EXCEPT : like 'disable all', but disables every backend except for clear counters : clear max statistics counters (add 'all' for all counters) help : this message prompt : toggle interactive mode with prompt quit : disconnect show info : report information about the running process show stat : report counters for each proxy and server show errors : report last request and response errors for each proxy show sess [id] : report the list of current sessions or dump this session get weight : report a server's current weight set weight : change a server's weight set timeout : change a timeout setting disable server : set a server in maintenance mode enable server : re-enable a server that was previously in maintenance mode version : version of this script USAGE end end haproxyctl-1.4.3/lib/haproxyctl/000077500000000000000000000000001301306530100166475ustar00rootroot00000000000000haproxyctl-1.4.3/lib/haproxyctl/environment.rb000066400000000000000000000044211301306530100215410ustar00rootroot00000000000000module HAProxyCTL module Environment attr_accessor :pidof, :config_path, :config, :exec def version puts "HAProxyCTL #{HAProxyCTL::VERSION}" end def config_path @config_path ||= ENV['HAPROXY_CONFIG'] || '/etc/haproxy/haproxy.cfg' end def config @config ||= File.read(config_path) end def has_exec? !exec.nil? end def exec return(@exec) if @exec @exec = ENV['HAPROXY_BIN'] @exec ||= `which haproxy`.chomp if @exec.empty? begin `haproxy -v 2>/dev/null` @exec = 'haproxy' rescue Errno::ENOENT => e @exec = nil end end (@exec) end def nbproc @nbproc ||= begin config.match /nbproc \s*(\d*)\s*/ begin Regexp.last_match[1].to_i || 1 rescue 1 end end end def socket @socket ||= begin # If the haproxy config is using nbproc > 1, we assume that all cores # except for 1 do not need commands sent to their sockets (if they exist). # This is a poor assumption, so TODO: improve CLI to accept argument for # processes to target. if nbproc > 1 config.match /stats\s+socket \s*([^\s]*) \s*.*process \s*1[\d^]?/ else config.match /stats\s+socket \s*([^\s]*)/ end Regexp.last_match[1] || fail("Expecting 'stats socket ' in #{config_path}") end end def pidfile if config.match(/pidfile \s*([^\s]*)/) @pidfile = Regexp.last_match[1] else std_pid = '/var/run/haproxy.pid' if File.exists?(std_pid) @pidfile = std_pid else fail("Expecting 'pidfile ' in #{config_path} or a pid file in #{std_pid}") end end end # @return [Array, nil] Returns the PIDs of HAProxy as an Array, if running. Nil otherwise. def check_running if File.exists?(pidfile) pid = File.read(pidfile) pids = pid.strip.split("\n") end # verify these pid(s) exists and are haproxy if pids and pids.all? { |pid| pid =~ /^\d+$/ and `ps -p #{pid} -o cmd=` =~ /#{exec}/ } return pids end end alias_method :pidof, :check_running end end haproxyctl-1.4.3/lib/haproxyctl/version.rb000066400000000000000000000000521301306530100206560ustar00rootroot00000000000000module HAProxyCTL VERSION = '1.4.3' end haproxyctl-1.4.3/rhapr/000077500000000000000000000000001301306530100150205ustar00rootroot00000000000000haproxyctl-1.4.3/rhapr/.gitignore000066400000000000000000000000411301306530100170030ustar00rootroot00000000000000*.gem .bundle Gemfile.lock pkg/* haproxyctl-1.4.3/rhapr/.rspec000066400000000000000000000000051301306530100161300ustar00rootroot00000000000000-cfs haproxyctl-1.4.3/rhapr/Gemfile000066400000000000000000000001311301306530100163060ustar00rootroot00000000000000source 'http://rubygems.org' # Specify your gem's dependencies in rhapr.gemspec gemspec haproxyctl-1.4.3/rhapr/Rakefile000066400000000000000000000004301301306530100164620ustar00rootroot00000000000000require 'bundler/gem_tasks' require 'rspec' require 'rspec/core' require 'rspec/core/rake_task' task release: :spec desc 'Run Specs' RSpec::Core::RakeTask.new(:spec) do |spec| spec.pattern = 'spec/**/*_spec.rb' spec.verbose = true spec.rspec_opts = ['--color'] end haproxyctl-1.4.3/rhapr/lib/000077500000000000000000000000001301306530100155665ustar00rootroot00000000000000haproxyctl-1.4.3/rhapr/lib/rhapr.rb000066400000000000000000000001571301306530100172320ustar00rootroot00000000000000require 'rhapr/version' require 'rhapr/environment' module Rhapr autoload :Interface, 'rhapr/interface' end haproxyctl-1.4.3/rhapr/lib/rhapr/000077500000000000000000000000001301306530100167025ustar00rootroot00000000000000haproxyctl-1.4.3/rhapr/lib/rhapr/environment.rb000066400000000000000000000067331301306530100216040ustar00rootroot00000000000000require 'socket' module Rhapr module Environment attr_reader :haproxy_pid, :config_path, :config, :exec, :socket_path # @return [String, nil] The path to the HAProxy configuration file, or nil if not found. Set the ENV variable $HAPROXY_CONFIG to override defaults. def config_path return(@config_path) if @config_path if ENV['HAPROXY_CONFIG'] @config_path = ENV['HAPROXY_CONFIG'] else config_paths = %w{/etc/haproxy/haproxy.cfg /etc/haproxy.cfg /usr/local/etc/haproxy.cfg} config_paths.select! { |cfg| File.exists?(cfg) } @config_path = config_paths.first end (@config_path) end # @return [String] The raw contents of the HAProxy configuration file. # @raise [RuntimeError] If it cannot read the contents of #config_path. def config @config ||= begin File.read(config_path) rescue Errno::ENOENT => e raise RuntimeError.new("Error openning file '#{config_path}'. Exception from File.read: #{e.exception}") end end # @return [true, false] Whether or not the HAProxy executable can be found. # @see Rhapr::Environment#exec def has_exec? !exec.nil? end # @return [String, nil] The path to the HAProxy executable will be returned, if found. Set ENV variable $HAPROXY_BIN to override def exec return(@exec) if @exec @exec = ENV['HAPROXY_BIN'] @exec ||= `which haproxy` if @exec.empty? begin `haproxy -v` @exec = 'haproxy' rescue Errno::ENOENT => e @exec = nil end end (@exec) end # @return [UNIXSocket] A connection to the HAProxy Socket # @raise [RuntimeError] Raised if a socket connection could not be established def socket begin UNIXSocket.open(socket_path) rescue Errno::EACCES => e raise RuntimeError.new("Could not open a socket with HAProxy. Error message: #{e.message}") end end # @return [String] The path to the HAProxy stats socket. # @raise [RuntimeError] Raised if no stats socket has been specified, in the HAProxy configuration. # @todo: Should there be an ENV var for this? Perhaps allow config-less runs of rhapr? def socket_path @socket_path ||= begin config.match /stats\s+socket\s+([^\s]*)/ Regexp.last_match[1] || fail(RuntimeError.new "Expecting 'stats socket ' in #{config_path}") end end # @return [String] Returns the path to the pidfile, specified in the HAProxy configuration. Returns an assumption, if not found. # @todo: Should there even be an assumption? Does HAProxy create a pid file, if not told to by the configuration? # @todo: Should there be an ENV var for this? Perhaps allow config-less runs of rhapr? def pid @pid ||= begin config.match /pidfile ([^\s]*)/ Regexp.last_match[1] || '/var/run/haproxy.pid' end end # @return [String, nil] Returns the PID of HAProxy as a string, if running. Nil otherwise. # @todo: Look for something other than pidof, for searching the process list. # Could read from the pid file, but there's potential that it will go stale. def check_running pidof = `pidof haproxy` pidof.strip! return pidof unless pidof.empty? end alias_method :pidof, :check_running end end haproxyctl-1.4.3/rhapr/lib/rhapr/interface.rb000066400000000000000000000056431301306530100211770ustar00rootroot00000000000000require 'csv' module Rhapr class Interface include Rhapr::Environment EMPTY = "\n" # @param [String, #to_s] message The message to be sent to HAProxy # return [Array] All of the output from HAProxy, read in. # @see Rhapr::Interface#write, Rhapr::Interface#read_full def send(message) sock = socket write(sock, message) read_full(sock) end # @return [true, false] Whether or not the 'clear counters' command was successful def clear_counters resp = send 'clear counters' resp == EMPTY end alias_method :clear, :clear_counters # @return [Hash{String => String}] The 'show info' attributes, from HAProxy, parsed into a Hash. def show_info resp = send 'show info' attrs = resp.split("\n") attrs.map! do|line| _attr, *_val = line.split(/: /) [_attr, _val.join] end Hash[ attrs] end alias_method :info, :show_info # @return [Array String}>] The 'show stat' response, from HAProxy, parsed into an Array of Hashes. def show_stat resp = send 'show stat' resp.gsub!(/^# /, '') csv = CSV.parse(resp, headers: true) out = csv.map(&:to_a) out.map! { |row| Hash[ row] } (out) end alias_method :stat, :show_stat # @todo: Implement. I do not know the possible errors that may be present, nor how HAProxy will render them. def show_errors end alias_method :errors, :show_errors # @todo: Implement. Not sure how this should look. It's likely that I will want to 'interpret' the data that is spit out. def show_sess(id) end alias_method :session, :show_sess # @return [Array] An Array with Two Elements: the Current Weight and the Initial Weight. # @todo: Allow the numeric id to be used as a parameter? def get_weight(backend, server) resp = send "get weight #{backend}/#{server}" resp.match /([[:digit:]]+) \(initial ([[:digit:]]+)\)/ weight, initial = Regexp.last_match[1], Regexp.last_match[2] return [weight.to_i, initial.to_i] if weight and initial fail ArgumentError.new("HAProxy did not recognize the specified Backend/Server. Response from HAProxy: #{resp}") end # @todo: Implement. # @todo: Allow the numeric id to be used as a parameter? def set_weight(backend, server, weight) end # @todo: Implement. def disable(backend, server) end # @todo: Implement. def enable(backend, server) end protected # @param [UNIXSocket] # @param [String] # @return [nil] def write(socket, message) socket.puts message end # @return [String] def read(socket) socket.gets end # @return [Array] All of the output from HAProxy, read in. # @see Rhapr::Interface#read def read_full(socket) output = [] output << read(socket) until sock.eof? end end end haproxyctl-1.4.3/rhapr/lib/rhapr/version.rb000066400000000000000000000000451301306530100207130ustar00rootroot00000000000000module Rhapr VERSION = '0.0.1' end haproxyctl-1.4.3/rhapr/rhapr.gemspec000066400000000000000000000014451301306530100175050ustar00rootroot00000000000000# -*- encoding: utf-8 -*- $LOAD_PATH.push File.expand_path('../lib', __FILE__) require 'rhapr/version' Gem::Specification.new do |s| s.name = 'rhapr' s.version = Rhapr::VERSION s.authors = ['Scott Gonyea'] s.email = ['me@sgonyea.com'] s.homepage = 'https://github.com/sgonyea/rhapr' s.summary = %q{Rhapr wraps around HAProxy} s.description = %q{Rhapr is a ruby lib that wraps around HAProxy, enabling you to sanely decomission a process.} s.add_dependency 'yard', '~>0.6' s.add_development_dependency 'rspec', '~>2.4' s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) } s.require_paths = ['lib'] end haproxyctl-1.4.3/rhapr/spec/000077500000000000000000000000001301306530100157525ustar00rootroot00000000000000haproxyctl-1.4.3/rhapr/spec/config_fixtures/000077500000000000000000000000001301306530100211505ustar00rootroot00000000000000haproxyctl-1.4.3/rhapr/spec/config_fixtures/basic_haproxy.cfg000066400000000000000000000016531301306530100244710ustar00rootroot00000000000000global daemon maxconn 1024 # quiet pidfile /var/run/haproxy.pid stats socket /tmp/haproxy level admin uid 501 group staff defaults log global mode http option httplog option dontlognull stats enable stats uri /proxystats # and this guy for statistics stats auth webreport:areallysecretsupersecurepassword stats refresh 5s listen thrift :9090 mode tcp balance roundrobin option tcplog option redispatch retries 3 contimeout 5000 clitimeout 40000 srvtimeout 7000 server thrift1 localhost:9091 maxconn 20 check inter 20000 server thrift2 localhost:9092 maxconn 20 check inter 20000 server thrift3 localhost:9093 maxconn 20 check inter 20000 server thrift4 localhost:9094 maxconn 20 check inter 20000 server thrift5 localhost:9095 maxconn 20 check inter 20000 server thrift6 localhost:9096 maxconn 20 check inter 20000 haproxyctl-1.4.3/rhapr/spec/config_fixtures/crappy_haproxy.cfg000066400000000000000000000000541301306530100247000ustar00rootroot00000000000000global daemon maxconn 100 quiet haproxyctl-1.4.3/rhapr/spec/config_fixtures/pid_test_haproxy.cfg000066400000000000000000000002011301306530100252070ustar00rootroot00000000000000global daemon maxconn 1024 # quiet pidfile /some/other/run/haproxy.pid stats socket /tmp/haproxy level admin haproxyctl-1.4.3/rhapr/spec/quality_spec.rb000066400000000000000000000004111301306530100207750ustar00rootroot00000000000000require 'spec_helper' IGNORE = /\.(gitmodules|png$|tar$|gz$|rbc$|gem$|pdf$)/ describe 'The application itself' do it 'has no malformed whitespace' do files = `git ls-files`.split("\n").select { |fn| fn !~ IGNORE } files.should be_well_formed end end haproxyctl-1.4.3/rhapr/spec/rhapr/000077500000000000000000000000001301306530100170665ustar00rootroot00000000000000haproxyctl-1.4.3/rhapr/spec/rhapr/environment_spec.rb000066400000000000000000000077301301306530100230000ustar00rootroot00000000000000require 'spec_helper' describe Rhapr::Environment do class EnvTest include Rhapr::Environment end before(:each) do @env_test = EnvTest.new end describe '#config_path' do it 'should set to the ENV variable, if present' do ENV['HAPROXY_CONFIG'] = '/some/path.cfg' @env_test.config_path.should == '/some/path.cfg' # Clean up. ENV.delete 'HAPROXY_CONFIG' end it 'should go down a list of pre-defined file names' do File.stub!(:exists?).and_return(false) File.should_receive(:exists?).with('/etc/haproxy.cfg').and_return(true) @env_test.config_path.should == '/etc/haproxy.cfg' end it 'should select the first configuration found, from the pre-defined list' do File.stub!(:exists?).and_return(false) File.should_receive(:exists?).with('/etc/haproxy/haproxy.cfg').and_return(true) File.should_receive(:exists?).with('/etc/haproxy.cfg').and_return(true) @env_test.config_path.should == '/etc/haproxy/haproxy.cfg' end it 'should be nil if config files do not exist and $HAPROXY_CONFIG is not set' do File.stub!(:exists?).and_return(false) @env_test.config_path.should be_nil end end describe '#config' do before(:each) do File.stub!(:exists?).and_return(false) File.should_receive(:exists?).with('/etc/haproxy.cfg').and_return(true) end it 'should raise an exception if it cannot read from #config_path' do File.should_receive(:read).and_raise(Errno::ENOENT) lambda do @env_test.config end.should raise_error(RuntimeError) end it 'should read and return the contents of a file' do File.should_receive(:read).and_return { "I can haz cfg ?\n" } @env_test.config.should == "I can haz cfg ?\n" end end describe '#exec' do it 'should set to the ENV variable, if present' do ENV['HAPROXY_BIN'] = '/usr/local/bin/haproxy' @env_test.exec.should == '/usr/local/bin/haproxy' # Clean up. ENV.delete 'HAPROXY_BIN' end it 'should call out to the `which` command to find haproxy, if the ENV var is not set' do @env_test.should_receive(:`).with('which haproxy').and_return('/opt/bin/haproxy') @env_test.exec.should == '/opt/bin/haproxy' end it 'should call out to haproxy directly, if all else fails' do @env_test.should_receive(:`).with('which haproxy').and_return('') @env_test.should_receive(:`).with('haproxy -v').and_return("HA-Proxy version 1.4.15 2011/04/08\nCopyright 2000-2010 Willy Tarreau \n\n") @env_test.exec.should == 'haproxy' end it 'should be nil if none of the above worked' do @env_test.should_receive(:`).with('which haproxy').and_return('') @env_test.should_receive(:`).with('haproxy -v').and_raise(Errno::ENOENT) @env_test.exec.should be_nil end end describe '#socket' do it 'should establish a socket connection with HAProxy' end describe '#socket_path' do it 'should parse out the io socket from the config file' do @env_test.should_receive(:config).and_return { config_for(:basic_haproxy) } @env_test.socket_path.should == '/tmp/haproxy' end it 'should raise an error if it cannot derive an io socket from the config file' do @env_test.should_receive(:config).and_return { config_for(:crappy_haproxy) } lambda do @env_test.socket_path end.should raise_error(RuntimeError) end end describe '#pid' do it 'should parse out the pidfile from the config file' do @env_test.should_receive(:config).and_return { config_for(:pid_test_haproxy) } @env_test.pid.should == '/some/other/run/haproxy.pid' end it 'should return a default path if it cannot derive an io socket from the config file' do @env_test.should_receive(:config).and_return { config_for(:crappy_haproxy) } @env_test.pid.should == '/var/run/haproxy.pid' end end describe '#check_running, #pidof' do pending 'TBD' end end haproxyctl-1.4.3/rhapr/spec/rhapr/interface_spec.rb000066400000000000000000000062341301306530100223720ustar00rootroot00000000000000require 'spec_helper' describe Rhapr::Interface do let(:basic_info) do "Name: HAProxy\nVersion: 1.4.15\nRelease_date: 2011/04/08\nNbproc: 1\nProcess_num: 1\nPid: 97413\nUptime: 0d 18h43m53s\n" << "Uptime_sec: 67433\nMemmax_MB: 0\nUlimit-n: 2066\nMaxsock: 2066\nMaxconn: 1024\nMaxpipes: 0\nCurrConns: 1\nPipesUsed: 0\n" << "PipesFree: 0\nTasks: 7\nRun_queue: 1\nnode: skg.local\ndescription: \n" end let(:basic_stat) do '# 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,\nsrv,FRONTEND," << ",,0,0,2000,0,0,0,0,0,0,,,,,OPEN,,,,,,,,,1,1,0,,,,0,0,0,0,,,,,,,,,,,0,0,0,,,\nsrv,srv1,0,0,0,0,20,0,0,0,,0,,0,0,0,0,DOWN,1,1,0," << "0,1,72468,72468,,1,1,1,,0,,2,0,,0,L4CON,,0,,,,,,,0,,,,0,0,\nsrv,srv2,0,0,0,0,20,0,0,0,,0,,0,0,0,0,DOWN,1,1,0,0,1,72465,72465,," << "1,1,2,,0,,2,0,,0,L4CON,,0,,,,,,,0,,,,0,0,\n" end subject { Rhapr::Interface.new } describe '#clear_counters' do it 'should send the "clear counters" message to HAProxy' do subject.should_receive(:send).with('clear counters').and_return("\n") subject.clear_counters.should be_true end end describe '#show_info' do it 'should parse and return a Hash of HAProxy\'s info attributes' do subject.should_receive(:send).with('show info').and_return(basic_info) subject.show_info.should be_a(Hash) end it 'should normalize the attribute names into lower-case and underscore-ized form' end describe '#show_stat' do before(:each) do subject.should_receive(:send).with('show stat').and_return(basic_stat) end it 'should return an Array of Hashes, returned from HAProxy\'s "show stats" request' do stats = subject.show_stat stats.should be_a(Array) stats.each { |stat| stat.should be_a(Hash) } end it 'should strip the "# " from the beginning of the headers, before calling CSV.parse' do stats = subject.show_stat stats.first.should_not have_key('# pxname') stats.first.should have_key('pxname') end end describe '#show_errors' describe '#show_sess' describe '#get_weight' do it 'should parse the weight into an Array, with two elements: The weight and the initial weight' do subject.should_receive(:send).with('get weight test/test1').and_return('1 (initial 1)') subject.get_weight('test', 'test1').should == [1, 1] end it 'should raise an error if the specific backend+server is not known to HAProxy' do subject.should_receive(:send).with('get weight test/test9').and_return('No such server.') lambda do subject.get_weight('test', 'test9') end.should raise_error(ArgumentError, 'HAProxy did not recognize the specified Backend/Server. Response from HAProxy: No such server.') end end describe '#set_weight' describe '#disable' describe '#enable' end haproxyctl-1.4.3/rhapr/spec/spec_helper.rb000066400000000000000000000003511301306530100205670ustar00rootroot00000000000000require 'bundler/setup' Bundler.require :default Bundler.require :development Dir[ Bundler.root.join('spec/support/**/*.rb')].each { |f| require f } RSpec.configure do |c| c.include CustomMatchers c.include ConfigFixtures end haproxyctl-1.4.3/rhapr/spec/support/000077500000000000000000000000001301306530100174665ustar00rootroot00000000000000haproxyctl-1.4.3/rhapr/spec/support/config_fixtures.rb000066400000000000000000000021471301306530100232150ustar00rootroot00000000000000require 'bundler' module ConfigFixtures attr_reader :config_fixtures # @param [Symbol] # @return [String] def fixture_for(sym) config_fixtures[sym][:fixture] end alias_method :config_for, :fixture_for def path_for(sym) config_fixtures[sym][:path] end # @see ConfigFixtures#create_fixture_hash def config_fixtures @config_fixtures ||= begin hash = Hash.new { |k, v| k[v] = {} } hash.merge!(create_fixture_hash) end end # @return [Hash{Symbol => String}] def create_fixture_hash Hash[ find_fixtures.map { |fpath| map_fixture(fpath) }] end # @param [String] # @return [Array] def map_fixture(fpath) [symbolize_filename(fpath), { path: fpath, fixture: read_file(fpath) }] end # @return [Array] def find_fixtures Dir.glob Bundler.root.join('spec/config_fixtures/**.cfg') end # @param [String] # @return [Symbol] def symbolize_filename(fpath) fname = File.basename(fpath) fname.split(/\W/).shift.to_sym end # @param [String] # @return [String] def read_file(fpath) File.read(fpath) end end haproxyctl-1.4.3/rhapr/spec/support/custom_matchers.rb000066400000000000000000000024311301306530100232130ustar00rootroot00000000000000module CustomMatchers class BeWellFormed def matches?(files) @errors = files.map do|filename| [ check_for_tabs(filename), excessive_spacing(filename), newline_precedes_eof(filename) ] end.flatten.compact @errors.empty? end def failure_message_for_should @errors.join("\n") end private def check_for_tabs(filename) bad_lines = File.readlines(filename).each_with_index.map do |line, line_no| line_no + 1 if line["\t"] and line !~ /^\s+#.*\s+\n$/ end.flatten.compact "#{filename} has tab characters on lines #{bad_lines.join(', ')}" if bad_lines.any? end def excessive_spacing(filename) bad_lines = File.readlines(filename).each_with_index.map do |line, line_no| line_no + 1 if line =~ /\s+\n$/ and line !~ /^\s+#.*\s+\n$/ end.flatten.compact "#{filename} has spaces on the EOL on lines #{bad_lines.join(', ')}" if bad_lines.any? end def newline_precedes_eof(filename) "#{filename} does not have a newline (\\n) before EOF" if File.read(filename) !~ /\n$/ end end def be_well_formed BeWellFormed.new end end haproxyctl-1.4.3/rubocop-todo.yml000066400000000000000000000025641301306530100170520ustar00rootroot00000000000000# This configuration was generated by `rubocop --auto-gen-config` # on 2014-02-04 10:23:58 +0100 using RuboCop version 0.18.1. # The point is for the user to remove these configuration records # one by one as the offences are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. # Offence count: 3 AmbiguousRegexpLiteral: Enabled: false # Offence count: 3 # Cop supports --auto-correct. AndOr: Enabled: false # Offence count: 3 BlockAlignment: Enabled: false # Offence count: 8 BlockNesting: Max: 4 # Offence count: 10 Documentation: Enabled: false # Offence count: 1 EmptyLinesAroundAccessModifier: Enabled: false # Offence count: 2 IfUnlessModifier: Enabled: false # Offence count: 80 LineLength: Max: 152 # Offence count: 5 # Configuration parameters: CountComments. MethodLength: Max: 32 # Offence count: 2 ParenthesesAsGroupedExpression: Enabled: false # Offence count: 2 # Configuration parameters: NamePrefixBlacklist. PredicateName: Enabled: false # Offence count: 3 # Configuration parameters: SupportedStyles. RaiseArgs: EnforcedStyle: compact # Offence count: 1 RedundantBegin: Enabled: false # Offence count: 2 Syntax: Enabled: false # Offence count: 2 UselessAssignment: Enabled: false # Offence count: 2 Void: Enabled: false