From da675d4788922820c67e2d871c51b6198b17f66b Mon Sep 17 00:00:00 2001 From: Yauhen Shulitski Date: Fri, 17 Feb 2023 15:17:32 +0100 Subject: [PATCH] feat(inputs.haproxy): Add support for tcp endpoints in haproxy plugin (#12680) --- etc/telegraf.conf | 26 ++++++++++----------- etc/telegraf_windows.conf | 26 ++++++++++----------- plugins/inputs/haproxy/README.md | 28 ++++++++++------------- plugins/inputs/haproxy/haproxy.go | 20 ++++++++++++----- plugins/inputs/haproxy/haproxy_test.go | 31 ++++++++++++++++++++++++++ plugins/inputs/haproxy/sample.conf | 28 ++++++++++------------- 6 files changed, 95 insertions(+), 64 deletions(-) diff --git a/etc/telegraf.conf b/etc/telegraf.conf index 888ab8738..794129d7e 100644 --- a/etc/telegraf.conf +++ b/etc/telegraf.conf @@ -4730,25 +4730,25 @@ # # insecure_skip_verify = false -# # Read metrics of HAProxy, via socket or HTTP stats page +# # Read metrics of HAProxy, via stats socket or http endpoints # [[inputs.haproxy]] -# ## An array of address to gather stats about. Specify an ip on hostname -# ## with optional port. ie localhost, 10.10.3.33:1936, etc. -# ## Make sure you specify the complete path to the stats endpoint -# ## including the protocol, ie http://10.10.3.33:1936/haproxy?stats +# ## List of stats endpoints. Metrics can be collected from both http and socket +# ## endpoints. Examples of valid endpoints: +# ## - http://myhaproxy.com:1936/haproxy?stats +# ## - https://myhaproxy.com:8000/stats +# ## - socket:/run/haproxy/admin.sock +# ## - /run/haproxy/*.sock +# ## - tcp://127.0.0.1:1936 +# ## +# ## Server addresses not starting with 'http://', 'https://', 'tcp://' will be +# ## treated as possible sockets. When specifying local socket, glob patterns are +# ## supported. +# servers = ["http://myhaproxy.com:1936/haproxy?stats"] # # ## Credentials for basic HTTP authentication # # username = "admin" # # password = "admin" # -# ## If no servers are specified, then default to 127.0.0.1:1936/haproxy?stats -# servers = ["http://myhaproxy.com:1936/haproxy?stats"] -# -# ## You can also use local socket with standard wildcard globbing. -# ## Server address not starting with 'http' will be treated as a possible -# ## socket, so both examples below are valid. -# # servers = ["socket:/run/haproxy/admin.sock", "/run/haproxy/*.sock"] -# # ## By default, some of the fields are renamed from what haproxy calls them. # ## Setting this option to true results in the plugin keeping the original # ## field names. diff --git a/etc/telegraf_windows.conf b/etc/telegraf_windows.conf index 1e7fbb80a..270de392f 100644 --- a/etc/telegraf_windows.conf +++ b/etc/telegraf_windows.conf @@ -4656,25 +4656,25 @@ # # insecure_skip_verify = false -# # Read metrics of HAProxy, via socket or HTTP stats page +# # Read metrics of HAProxy, via stats socket or http endpoints # [[inputs.haproxy]] -# ## An array of address to gather stats about. Specify an ip on hostname -# ## with optional port. ie localhost, 10.10.3.33:1936, etc. -# ## Make sure you specify the complete path to the stats endpoint -# ## including the protocol, ie http://10.10.3.33:1936/haproxy?stats +# ## List of stats endpoints. Metrics can be collected from both http and socket +# ## endpoints. Examples of valid endpoints: +# ## - http://myhaproxy.com:1936/haproxy?stats +# ## - https://myhaproxy.com:8000/stats +# ## - socket:/run/haproxy/admin.sock +# ## - /run/haproxy/*.sock +# ## - tcp://127.0.0.1:1936 +# ## +# ## Server addresses not starting with 'http://', 'https://', 'tcp://' will be +# ## treated as possible sockets. When specifying local socket, glob patterns are +# ## supported. +# servers = ["http://myhaproxy.com:1936/haproxy?stats"] # # ## Credentials for basic HTTP authentication # # username = "admin" # # password = "admin" # -# ## If no servers are specified, then default to 127.0.0.1:1936/haproxy?stats -# servers = ["http://myhaproxy.com:1936/haproxy?stats"] -# -# ## You can also use local socket with standard wildcard globbing. -# ## Server address not starting with 'http' will be treated as a possible -# ## socket, so both examples below are valid. -# # servers = ["socket:/run/haproxy/admin.sock", "/run/haproxy/*.sock"] -# # ## By default, some of the fields are renamed from what haproxy calls them. # ## Setting this option to true results in the plugin keeping the original # ## field names. diff --git a/plugins/inputs/haproxy/README.md b/plugins/inputs/haproxy/README.md index 57589340c..0ea1d96c1 100644 --- a/plugins/inputs/haproxy/README.md +++ b/plugins/inputs/haproxy/README.md @@ -19,25 +19,21 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details. ## Configuration ```toml @sample.conf -# Read metrics of HAProxy, via socket or HTTP stats page +# Read metrics of HAProxy, via stats socket or http endpoints [[inputs.haproxy]] - ## An array of address to gather stats about. Specify an ip on hostname - ## with optional port. ie localhost, 10.10.3.33:1936, etc. - ## Make sure you specify the complete path to the stats endpoint - ## including the protocol, ie http://10.10.3.33:1936/haproxy?stats - - ## Credentials for basic HTTP authentication - # username = "admin" - # password = "admin" - - ## If no servers are specified, then default to 127.0.0.1:1936/haproxy?stats + ## List of stats endpoints. Metrics can be collected from both http and socket + ## endpoints. Examples of valid endpoints: + ## - http://myhaproxy.com:1936/haproxy?stats + ## - https://myhaproxy.com:8000/stats + ## - socket:/run/haproxy/admin.sock + ## - /run/haproxy/*.sock + ## - tcp://127.0.0.1:1936 + ## + ## Server addresses not starting with 'http://', 'https://', 'tcp://' will be + ## treated as possible sockets. When specifying local socket, glob patterns are + ## supported. servers = ["http://myhaproxy.com:1936/haproxy?stats"] - ## You can also use local socket with standard wildcard globbing. - ## Server address not starting with 'http' will be treated as a possible - ## socket, so both examples below are valid. - # servers = ["socket:/run/haproxy/admin.sock", "/run/haproxy/*.sock"] - ## By default, some of the fields are renamed from what haproxy calls them. ## Setting this option to true results in the plugin keeping the original ## field names. diff --git a/plugins/inputs/haproxy/haproxy.go b/plugins/inputs/haproxy/haproxy.go index 7870c82bc..3002d3814 100644 --- a/plugins/inputs/haproxy/haproxy.go +++ b/plugins/inputs/haproxy/haproxy.go @@ -49,7 +49,7 @@ func (h *haproxy) Gather(acc telegraf.Accumulator) error { endpoints := make([]string, 0, len(h.Servers)) for _, endpoint := range h.Servers { - if strings.HasPrefix(endpoint, "http") { + if strings.HasPrefix(endpoint, "http://") || strings.HasPrefix(endpoint, "https://") || strings.HasPrefix(endpoint, "tcp://") { endpoints = append(endpoints, endpoint) continue } @@ -85,21 +85,29 @@ func (h *haproxy) Gather(acc telegraf.Accumulator) error { } func (h *haproxy) gatherServerSocket(addr string, acc telegraf.Accumulator) error { - socketPath := getSocketAddr(addr) + var network string + var address string + if strings.HasPrefix(addr, "tcp://") { + network = "tcp" + address = strings.TrimPrefix(addr, "tcp://") + } else { + network = "unix" + address = getSocketAddr(addr) + } - c, err := net.Dial("unix", socketPath) + c, err := net.Dial(network, address) if err != nil { - return fmt.Errorf("could not connect to socket '%s': %s", addr, err) + return fmt.Errorf("could not connect to '%s://%s': %s", network, address, err) } _, errw := c.Write([]byte("show stat\n")) if errw != nil { - return fmt.Errorf("could not write to socket '%s': %s", addr, errw) + return fmt.Errorf("could not write to socket '%s://%s': %s", network, address, errw) } - return h.importCsvResult(c, acc, socketPath) + return h.importCsvResult(c, acc, address) } func (h *haproxy) gatherServer(addr string, acc telegraf.Accumulator) error { diff --git a/plugins/inputs/haproxy/haproxy_test.go b/plugins/inputs/haproxy/haproxy_test.go index 8119c675c..b5cfa0b04 100644 --- a/plugins/inputs/haproxy/haproxy_test.go +++ b/plugins/inputs/haproxy/haproxy_test.go @@ -170,6 +170,37 @@ func TestHaproxyGeneratesMetricsUsingSocket(t *testing.T) { require.NotEmpty(t, acc.Errors) } +func TestHaproxyGeneratesMetricsUsingTcp(t *testing.T) { + l, err := net.Listen("tcp", "localhost:8192") + if err != nil { + t.Fatal(err) + } + defer l.Close() + + s := statServer{} + go s.serverSocket(l) + + r := &haproxy{ + Servers: []string{"tcp://" + l.Addr().String()}, + } + + var acc testutil.Accumulator + require.NoError(t, r.Gather(&acc)) + + fields := HaproxyGetFieldValues() + + tags := map[string]string{ + "server": l.Addr().String(), + "proxy": "git", + "sv": "www", + "type": "server", + } + + acc.AssertContainsTaggedFields(t, "haproxy", fields, tags) + + require.NoError(t, r.Gather(&acc)) +} + // 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) { diff --git a/plugins/inputs/haproxy/sample.conf b/plugins/inputs/haproxy/sample.conf index 6efe33f9d..a6b7a241b 100644 --- a/plugins/inputs/haproxy/sample.conf +++ b/plugins/inputs/haproxy/sample.conf @@ -1,22 +1,18 @@ -# Read metrics of HAProxy, via socket or HTTP stats page +# Read metrics of HAProxy, via stats socket or http endpoints [[inputs.haproxy]] - ## An array of address to gather stats about. Specify an ip on hostname - ## with optional port. ie localhost, 10.10.3.33:1936, etc. - ## Make sure you specify the complete path to the stats endpoint - ## including the protocol, ie http://10.10.3.33:1936/haproxy?stats - - ## Credentials for basic HTTP authentication - # username = "admin" - # password = "admin" - - ## If no servers are specified, then default to 127.0.0.1:1936/haproxy?stats + ## List of stats endpoints. Metrics can be collected from both http and socket + ## endpoints. Examples of valid endpoints: + ## - http://myhaproxy.com:1936/haproxy?stats + ## - https://myhaproxy.com:8000/stats + ## - socket:/run/haproxy/admin.sock + ## - /run/haproxy/*.sock + ## - tcp://127.0.0.1:1936 + ## + ## Server addresses not starting with 'http://', 'https://', 'tcp://' will be + ## treated as possible sockets. When specifying local socket, glob patterns are + ## supported. servers = ["http://myhaproxy.com:1936/haproxy?stats"] - ## You can also use local socket with standard wildcard globbing. - ## Server address not starting with 'http' will be treated as a possible - ## socket, so both examples below are valid. - # servers = ["socket:/run/haproxy/admin.sock", "/run/haproxy/*.sock"] - ## By default, some of the fields are renamed from what haproxy calls them. ## Setting this option to true results in the plugin keeping the original ## field names.