From 6f844edf8fed77ce20022f5f9a7b63714b802cf2 Mon Sep 17 00:00:00 2001 From: Antoine Toulme Date: Sat, 20 Apr 2024 09:34:52 -0700 Subject: [PATCH] [receiver/haproxy] fix the `show stat` command (#32366) **Description:** Fix an issue where we called `show stats` instead of `show stat`. This also fixes the situation where no records are present by properly trimming the data. **Link to tracking Issue:** Fixes #32291 **Testing:** Added a unit test. --- .chloggen/haproxy_stat.yaml | 27 ++++++++++++ receiver/haproxyreceiver/scraper.go | 9 ++-- receiver/haproxyreceiver/scraper_test.go | 42 ++++++++++++++++++- .../haproxyreceiver/testdata/empty_stats.txt | 3 ++ 4 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 .chloggen/haproxy_stat.yaml create mode 100644 receiver/haproxyreceiver/testdata/empty_stats.txt diff --git a/.chloggen/haproxy_stat.yaml b/.chloggen/haproxy_stat.yaml new file mode 100644 index 000000000000..f12a794e05cb --- /dev/null +++ b/.chloggen/haproxy_stat.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: bug_fix + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: haproxyreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Fix show stat command on unix socket + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [32291] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [] diff --git a/receiver/haproxyreceiver/scraper.go b/receiver/haproxyreceiver/scraper.go index beda74651d84..7af9e2509ee1 100644 --- a/receiver/haproxyreceiver/scraper.go +++ b/receiver/haproxyreceiver/scraper.go @@ -28,7 +28,7 @@ import ( ) var ( - showStatsCommand = []byte("show stats\n") + showStatsCommand = []byte("show stat\n") ) type scraper struct { @@ -71,13 +71,14 @@ func (s *scraper) scrape(ctx context.Context) (pmetric.Metrics, error) { return pmetric.NewMetrics(), err } buf := make([]byte, 4096) - _, err = c.Read(buf) + n, err := c.Read(buf) if err != nil { return pmetric.NewMetrics(), err } + buf = buf[0:n] records, err = s.readStats(buf) if err != nil { - return pmetric.NewMetrics(), err + return pmetric.NewMetrics(), fmt.Errorf("error reading stats: %w", err) } } @@ -262,7 +263,7 @@ func (s *scraper) scrape(ctx context.Context) (pmetric.Metrics, error) { } func (s *scraper) readStats(buf []byte) ([]map[string]string, error) { - reader := csv.NewReader(bytes.NewReader(buf)) + reader := csv.NewReader(bytes.NewReader(bytes.TrimSpace(buf))) headers, err := reader.Read() if err != nil { return nil, err diff --git a/receiver/haproxyreceiver/scraper_test.go b/receiver/haproxyreceiver/scraper_test.go index 9baaa61454e1..3be15ba662ac 100644 --- a/receiver/haproxyreceiver/scraper_test.go +++ b/receiver/haproxyreceiver/scraper_test.go @@ -36,7 +36,7 @@ func Test_scraper_readStats(t *testing.T) { data := string(buf[0:nr]) switch data { - case "show stats\n": + case "show stat\n": stats, err2 := os.ReadFile(filepath.Join("testdata", "stats.txt")) require.NoError(t, err2) _, err2 = c.Write(stats) @@ -79,7 +79,7 @@ func Test_scraper_readStatsWithIncompleteValues(t *testing.T) { data := string(buf[0:nr]) switch data { - case "show stats\n": + case "show stat\n": stats, err2 := os.ReadFile(filepath.Join("testdata", "30252_stats.txt")) require.NoError(t, err2) _, err2 = c.Write(stats) @@ -103,3 +103,41 @@ func Test_scraper_readStatsWithIncompleteValues(t *testing.T) { pmetrictest.IgnoreTimestamp(), pmetrictest.IgnoreResourceAttributeValue("haproxy.addr"), pmetrictest.IgnoreResourceMetricsOrder())) } + +func Test_scraper_readStatsWithNoValues(t *testing.T) { + f, err := os.MkdirTemp("", "haproxytest") + require.NoError(t, err) + socketAddr := filepath.Join(f, "testhaproxy.sock") + l, err := net.Listen("unix", socketAddr) + require.NoError(t, err) + defer l.Close() + + go func() { + c, err2 := l.Accept() + require.NoError(t, err2) + + buf := make([]byte, 512) + nr, err2 := c.Read(buf) + require.NoError(t, err2) + + data := string(buf[0:nr]) + switch data { + case "show stat\n": + stats, err2 := os.ReadFile(filepath.Join("testdata", "empty_stats.txt")) + require.NoError(t, err2) + _, err2 = c.Write(stats) + require.NoError(t, err2) + default: + require.Fail(t, fmt.Sprintf("invalid message: %v", data)) + } + }() + + haProxyCfg := newDefaultConfig().(*Config) + haProxyCfg.Endpoint = socketAddr + s := newScraper(haProxyCfg, receivertest.NewNopCreateSettings()) + m, err := s.scrape(context.Background()) + require.NoError(t, err) + require.NotNil(t, m) + + require.Equal(t, 0, m.MetricCount()) +} diff --git a/receiver/haproxyreceiver/testdata/empty_stats.txt b/receiver/haproxyreceiver/testdata/empty_stats.txt new file mode 100644 index 000000000000..6b9b4c06880e --- /dev/null +++ b/receiver/haproxyreceiver/testdata/empty_stats.txt @@ -0,0 +1,3 @@ +# 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,wrew,connect,reuse,cache_lookups,cache_hits,srv_icur,src_ilim,qtime_max,ctime_max,rtime_max,ttime_max,eint,idle_conn_cur,safe_conn_cur,used_conn_cur,need_conn_est,uweight,agg_server_status,agg_server_check_status,agg_check_status,srid,sess_other,h1sess,h2sess,h3sess,req_other,h1req,h2req,h3req,proto,-,ssl_sess,ssl_reused_sess,ssl_failed_handshake,h2_headers_rcvd,h2_data_rcvd,h2_settings_rcvd,h2_rst_stream_rcvd,h2_goaway_rcvd,h2_detected_conn_protocol_errors,h2_detected_strm_protocol_errors,h2_rst_stream_resp,h2_goaway_resp,h2_open_connections,h2_backend_open_streams,h2_total_connections,h2_backend_total_streams,h1_open_connections,h1_open_streams,h1_total_connections,h1_total_streams,h1_bytes_in,h1_bytes_out, + +