package haproxy import ( "crypto/rand" "encoding/binary" "fmt" "net" "net/http" "net/http/httptest" "strings" "testing" "github.com/influxdata/telegraf/testutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) type statServer struct{} func (s statServer) serverSocket(l net.Listener) { for { conn, err := l.Accept() if err != nil { return } go func(c net.Conn) { buf := make([]byte, 1024) n, _ := c.Read(buf) data := buf[:n] if string(data) == "show stat\n" { c.Write([]byte(csvOutputSample)) c.Close() } }(conn) } } func TestHaproxyGeneratesMetricsWithAuthentication(t *testing.T) { //We create a fake server to return test data ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { username, password, ok := r.BasicAuth() if !ok { w.WriteHeader(http.StatusNotFound) fmt.Fprint(w, "Unauthorized") return } if username == "user" && password == "password" { fmt.Fprint(w, csvOutputSample) } else { w.WriteHeader(http.StatusNotFound) fmt.Fprint(w, "Unauthorized") } })) defer ts.Close() //Now we tested again above server, with our authentication data r := &haproxy{ Servers: []string{strings.Replace(ts.URL, "http://", "http://user:password@", 1)}, } var acc testutil.Accumulator err := r.Gather(&acc) require.NoError(t, err) tags := map[string]string{ "server": ts.Listener.Addr().String(), "proxy": "git", "sv": "www", "type": "server", } fields := HaproxyGetFieldValues() acc.AssertContainsTaggedFields(t, "haproxy", fields, tags) //Here, we should get error because we don't pass authentication data r = &haproxy{ Servers: []string{ts.URL}, } r.Gather(&acc) require.NotEmpty(t, acc.Errors) } func TestHaproxyGeneratesMetricsWithoutAuthentication(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, csvOutputSample) })) defer ts.Close() r := &haproxy{ Servers: []string{ts.URL}, } var acc testutil.Accumulator err := r.Gather(&acc) require.NoError(t, err) tags := map[string]string{ "server": ts.Listener.Addr().String(), "proxy": "git", "sv": "www", "type": "server", } fields := HaproxyGetFieldValues() acc.AssertContainsTaggedFields(t, "haproxy", fields, tags) } func TestHaproxyGeneratesMetricsUsingSocket(t *testing.T) { var randomNumber int64 var sockets [5]net.Listener _globmask := "/tmp/test-haproxy*.sock" _badmask := "/tmp/test-fail-haproxy*.sock" for i := 0; i < 5; i++ { binary.Read(rand.Reader, binary.LittleEndian, &randomNumber) sockname := fmt.Sprintf("/tmp/test-haproxy%d.sock", randomNumber) sock, err := net.Listen("unix", sockname) if err != nil { t.Fatal("Cannot initialize socket ") } sockets[i] = sock defer sock.Close() s := statServer{} go s.serverSocket(sock) } r := &haproxy{ Servers: []string{_globmask}, } var acc testutil.Accumulator err := r.Gather(&acc) require.NoError(t, err) fields := HaproxyGetFieldValues() for _, sock := range sockets { tags := map[string]string{ "server": sock.Addr().String(), "proxy": "git", "sv": "www", "type": "server", } acc.AssertContainsTaggedFields(t, "haproxy", fields, tags) } // This mask should not match any socket r.Servers = []string{_badmask} r.Gather(&acc) require.NotEmpty(t, acc.Errors) } //When not passing server config, we default to localhost //We just want to make sure we did request stat from localhost func TestHaproxyDefaultGetFromLocalhost(t *testing.T) { r := &haproxy{} var acc testutil.Accumulator err := r.Gather(&acc) require.Error(t, err) assert.Contains(t, err.Error(), "127.0.0.1:1936/haproxy?stats/;csv") } func TestHaproxyKeepFieldNames(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, csvOutputSample) })) defer ts.Close() r := &haproxy{ Servers: []string{ts.URL}, KeepFieldNames: true, } var acc testutil.Accumulator err := r.Gather(&acc) require.NoError(t, err) tags := map[string]string{ "server": ts.Listener.Addr().String(), "pxname": "git", "svname": "www", "type": "server", } fields := HaproxyGetFieldValues() fields["act"] = fields["active_servers"] delete(fields, "active_servers") fields["bck"] = fields["backup_servers"] delete(fields, "backup_servers") fields["cli_abrt"] = fields["cli_abort"] delete(fields, "cli_abort") fields["srv_abrt"] = fields["srv_abort"] delete(fields, "srv_abort") fields["hrsp_1xx"] = fields["http_response.1xx"] delete(fields, "http_response.1xx") fields["hrsp_2xx"] = fields["http_response.2xx"] delete(fields, "http_response.2xx") fields["hrsp_3xx"] = fields["http_response.3xx"] delete(fields, "http_response.3xx") fields["hrsp_4xx"] = fields["http_response.4xx"] delete(fields, "http_response.4xx") fields["hrsp_5xx"] = fields["http_response.5xx"] delete(fields, "http_response.5xx") fields["hrsp_other"] = fields["http_response.other"] delete(fields, "http_response.other") acc.AssertContainsTaggedFields(t, "haproxy", fields, tags) } func HaproxyGetFieldValues() map[string]interface{} { fields := map[string]interface{}{ "active_servers": uint64(1), "backup_servers": uint64(0), "bin": uint64(5228218), "bout": uint64(303747244), "check_code": uint64(200), "check_duration": uint64(3), "check_fall": uint64(3), "check_health": uint64(4), "check_rise": uint64(2), "check_status": "L7OK", "chkdown": uint64(84), "chkfail": uint64(559), "cli_abort": uint64(690), "ctime": uint64(1), "downtime": uint64(3352), "dresp": uint64(0), "econ": uint64(0), "eresp": uint64(21), "http_response.1xx": uint64(0), "http_response.2xx": uint64(5668), "http_response.3xx": uint64(8710), "http_response.4xx": uint64(140), "http_response.5xx": uint64(0), "http_response.other": uint64(0), "iid": uint64(4), "last_chk": "OK", "lastchg": uint64(1036557), "lastsess": int64(1342), "lbtot": uint64(9481), "mode": "http", "pid": uint64(1), "qcur": uint64(0), "qmax": uint64(0), "qtime": uint64(1268), "rate": uint64(0), "rate_max": uint64(2), "rtime": uint64(2908), "sid": uint64(1), "scur": uint64(0), "slim": uint64(2), "smax": uint64(2), "srv_abort": uint64(0), "status": "UP", "stot": uint64(14539), "ttime": uint64(4500), "weight": uint64(1), "wredis": uint64(0), "wretr": uint64(0), } return fields } // Can obtain from official haproxy demo: 'http://demo.haproxy.org/;csv' const csvOutputSample = ` # 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,comp_in,comp_out,comp_byp,comp_rsp,lastsess,last_chk,last_agt,qtime,ctime,rtime,ttime,agent_status,agent_code,agent_duration,check_desc,agent_desc,check_rise,check_fall,check_health,agent_rise,agent_fall,agent_health,addr,cookie,mode,algo,conn_rate,conn_rate_max,conn_tot,intercepted,dcon,dses, http-in,FRONTEND,,,3,100,100,2639994,813557487,65937668635,505252,0,47567,,,,,OPEN,,,,,,,,,1,2,0,,,,0,1,0,157,,,,0,1514640,606647,136264,496535,14948,,1,155,2754255,,,36370569635,17435137766,0,642264,,,,,,,,,,,,,,,,,,,,,http,,1,157,2649922,339471,0,0, http-in,IPv4-direct,,,3,41,100,349801,57445827,1503928881,269899,0,287,,,,,OPEN,,,,,,,,,1,2,1,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,http,,,,,,0,0, http-in,IPv4-cached,,,0,33,100,1786155,644395819,57905460294,60511,0,1,,,,,OPEN,,,,,,,,,1,2,2,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,http,,,,,,0,0, http-in,IPv6-direct,,,0,100,100,325619,92414745,6205208728,3399,0,47279,,,,,OPEN,,,,,,,,,1,2,3,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,http,,,,,,0,0, http-in,local,,,0,0,100,0,0,0,0,0,0,,,,,OPEN,,,,,,,,,1,2,4,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,http,,,,,,0,0, http-in,local-https,,,0,5,100,188347,19301096,323070732,171443,0,0,,,,,OPEN,,,,,,,,,1,2,5,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,http,,,,,,0,0, www,www,0,0,0,20,20,1719698,672044109,64806076656,,0,,0,5285,22,0,UP,1,1,0,561,84,1036557,3356,,1,3,1,,1715117,,2,0,,45,L7OK,200,5,671,1144889,481714,87038,4,0,,,,,105016,167,,,,,5,OK,,0,5,16,1167,,,,Layer7 check passed,,2,3,4,,,,,,http,,,,,,,, www,bck,0,0,0,10,10,1483,537137,7544118,,0,,0,0,0,0,UP,1,0,1,4,0,5218087,0,,1,3,2,,1371,,2,0,,17,L7OK,200,2,0,629,99,755,0,0,,,,,16,0,,,,,1036557,OK,,756,1,13,1184,,,,Layer7 check passed,,2,5,6,,,,,,http,,,,,,,, www,BACKEND,0,25,0,46,100,1721835,674684790,64813732170,314,0,,130,5285,22,0,UP,1,1,1,,0,5218087,0,,1,3,0,,1716488,,1,0,,45,,,,0,1145518,481813,88664,5719,121,,,,1721835,105172,167,35669268059,17250148556,0,556042,5,,,0,5,16,1167,,,,,,,,,,,,,,http,,,,,,,, git,www,0,0,0,2,2,14539,5228218,303747244,,0,,0,21,0,0,UP,1,1,0,559,84,1036557,3352,,1,4,1,,9481,,2,0,,2,L7OK,200,3,0,5668,8710,140,0,0,,,,,690,0,,,,,1342,OK,,1268,1,2908,4500,,,,Layer7 check passed,,2,3,4,,,,,,http,,,,,,,, git,bck,0,0,0,0,2,0,0,0,,0,,0,0,0,0,UP,1,0,1,2,0,5218087,0,,1,4,2,,0,,2,0,,0,L7OK,200,2,0,0,0,0,0,0,,,,,0,0,,,,,-1,OK,,0,0,0,0,,,,Layer7 check passed,,2,3,4,,,,,,http,,,,,,,, git,BACKEND,0,6,0,8,2,14541,8082393,303747668,0,0,,2,21,0,0,UP,1,1,1,,0,5218087,0,,1,4,0,,9481,,1,0,,7,,,,0,5668,8710,140,23,0,,,,14541,690,0,133458298,38104818,0,4379,1342,,,1268,1,2908,4500,,,,,,,,,,,,,,http,,,,,,,, demo,BACKEND,0,0,1,5,20,24063,7876647,659864417,48,0,,1,0,0,0,UP,0,0,0,,0,5218087,,,1,17,0,,0,,1,1,,26,,,,0,23983,21,0,1,57,,,,24062,111,0,567843278,146884392,0,1083,0,,,2706,0,0,887,,,,,,,,,,,,,,http,,,,,,,, `