Skip to content
This repository has been archived by the owner on Mar 8, 2023. It is now read-only.

Commit

Permalink
enable optional use of an HTTP proxy (#237)
Browse files Browse the repository at this point in the history
* added --http.proxy-from-env (default false)

Signed-off-by: Thomas de Grenier de Latour <thomas.degrenierdelatour@orange.com>\
  • Loading branch information
thomasgl-orange committed May 3, 2022
1 parent 5a9e07f commit 87a48e7
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 16 deletions.
9 changes: 9 additions & 0 deletions README.md
Expand Up @@ -58,6 +58,15 @@ you can disable it using the `--no-haproxy.ssl-verify` flag:
haproxy_exporter --no-haproxy.ssl-verify --haproxy.scrape-uri="https://haproxy.example.com/haproxy?stats;csv"
```

If scraping a remote HAProxy must be done via an HTTP proxy, you can enable reading of the
standard [`$http_proxy` / `$https_proxy` / `$no_proxy` environment variables](https://pkg.go.dev/net/http#ProxyFromEnvironment) by using the
`--http.proxy-from-env` flag (these variables will be ignored otherwise):

```bash
export HTTP_PROXY="http://proxy:3128"
haproxy_exporter --http.proxy-from-env --haproxy.scrape-uri="http://haproxy.example.com/haproxy?stats;csv"
```

[basic auth]: https://cbonte.github.io/haproxy-dconv/configuration-1.6.html#4-stats%20auth

### Unix Sockets
Expand Down
12 changes: 8 additions & 4 deletions haproxy_exporter.go
Expand Up @@ -252,7 +252,7 @@ type Exporter struct {
}

// NewExporter returns an initialized Exporter.
func NewExporter(uri string, sslVerify bool, selectedServerMetrics map[int]metricInfo, excludedServerStates string, timeout time.Duration, logger log.Logger) (*Exporter, error) {
func NewExporter(uri string, sslVerify, proxyFromEnv bool, selectedServerMetrics map[int]metricInfo, excludedServerStates string, timeout time.Duration, logger log.Logger) (*Exporter, error) {
u, err := url.Parse(uri)
if err != nil {
return nil, err
Expand All @@ -262,7 +262,7 @@ func NewExporter(uri string, sslVerify bool, selectedServerMetrics map[int]metri
var fetchStat func() (io.ReadCloser, error)
switch u.Scheme {
case "http", "https", "file":
fetchStat = fetchHTTP(uri, sslVerify, timeout)
fetchStat = fetchHTTP(uri, sslVerify, proxyFromEnv, timeout)
case "unix":
fetchInfo = fetchUnix(u, showInfoCmd, timeout)
fetchStat = fetchUnix(u, showStatCmd, timeout)
Expand Down Expand Up @@ -331,8 +331,11 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
ch <- e.csvParseFailures
}

func fetchHTTP(uri string, sslVerify bool, timeout time.Duration) func() (io.ReadCloser, error) {
func fetchHTTP(uri string, sslVerify, proxyFromEnv bool, timeout time.Duration) func() (io.ReadCloser, error) {
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: !sslVerify}}
if proxyFromEnv {
tr.Proxy = http.ProxyFromEnvironment
}
client := http.Client{
Timeout: timeout,
Transport: tr,
Expand Down Expand Up @@ -566,6 +569,7 @@ func main() {
haProxyServerExcludeStates = kingpin.Flag("haproxy.server-exclude-states", "Comma-separated list of exported server states to exclude. See https://cbonte.github.io/haproxy-dconv/1.8/management.html#9.1, field 17 statuus").Default(excludedServerStates).String()
haProxyTimeout = kingpin.Flag("haproxy.timeout", "Timeout for trying to get stats from HAProxy.").Default("5s").Duration()
haProxyPidFile = kingpin.Flag("haproxy.pid-file", pidFileHelpText).Default("").String()
httpProxyFromEnv = kingpin.Flag("http.proxy-from-env", "Flag that enables using HTTP proxy settings from environment variables ($http_proxy, $https_proxy, $no_proxy)").Default("false").Bool()
)

promlogConfig := &promlog.Config{}
Expand All @@ -584,7 +588,7 @@ func main() {
level.Info(logger).Log("msg", "Starting haproxy_exporter", "version", version.Info())
level.Info(logger).Log("msg", "Build context", "context", version.BuildContext())

exporter, err := NewExporter(*haProxyScrapeURI, *haProxySSLVerify, selectedServerMetrics, *haProxyServerExcludeStates, *haProxyTimeout, logger)
exporter, err := NewExporter(*haProxyScrapeURI, *haProxySSLVerify, *httpProxyFromEnv, selectedServerMetrics, *haProxyServerExcludeStates, *haProxyTimeout, logger)
if err != nil {
level.Error(logger).Log("msg", "Error creating an exporter", "err", err)
os.Exit(1)
Expand Down
24 changes: 12 additions & 12 deletions haproxy_exporter_test.go
Expand Up @@ -74,7 +74,7 @@ func TestInvalidConfig(t *testing.T) {
h := newHaproxy([]byte("not,enough,fields"))
defer h.Close()

e, _ := NewExporter(h.URL, true, serverMetrics, excludedServerStates, 5*time.Second, log.NewNopLogger())
e, _ := NewExporter(h.URL, true, false, serverMetrics, excludedServerStates, 5*time.Second, log.NewNopLogger())

expectMetrics(t, e, "invalid_config.metrics")
}
Expand All @@ -83,7 +83,7 @@ func TestServerWithoutChecks(t *testing.T) {
h := newHaproxy([]byte("test,127.0.0.1:8080,0,0,0,0,0,0,0,0,,0,,0,0,0,0,no check,1,1,0,0,,,0,,1,1,1,,0,,2,0,,0,,,,0,0,0,0,0,0,0,,,,0,0,,,,,,,,,,,"))
defer h.Close()

e, _ := NewExporter(h.URL, true, serverMetrics, excludedServerStates, 5*time.Second, log.NewNopLogger())
e, _ := NewExporter(h.URL, true, false, serverMetrics, excludedServerStates, 5*time.Second, log.NewNopLogger())

expectMetrics(t, e, "server_without_checks.metrics")
}
Expand All @@ -101,7 +101,7 @@ foo,BACKEND,0,0,0,0,,0,0,0,,0,,0,0,0,0,UP,1,1,0,0,0,5007,0,,1,8,1,,0,,2,0,,0,L4O
h := newHaproxy([]byte(data))
defer h.Close()

e, _ := NewExporter(h.URL, true, serverMetrics, excludedServerStates, 5*time.Second, log.NewNopLogger())
e, _ := NewExporter(h.URL, true, false, serverMetrics, excludedServerStates, 5*time.Second, log.NewNopLogger())

expectMetrics(t, e, "server_broken_csv.metrics")
}
Expand All @@ -114,7 +114,7 @@ foo,BACKEND,0,0,0,0,,0,0,0,,0,,0,0,0,0,UP,1,1,0,0,0,5007,0,,1,8,1,,0,,2,
h := newHaproxy([]byte(data))
defer h.Close()

e, _ := NewExporter(h.URL, true, serverMetrics, excludedServerStates, 5*time.Second, log.NewNopLogger())
e, _ := NewExporter(h.URL, true, false, serverMetrics, excludedServerStates, 5*time.Second, log.NewNopLogger())

expectMetrics(t, e, "older_haproxy_versions.metrics")
}
Expand All @@ -123,7 +123,7 @@ func TestConfigChangeDetection(t *testing.T) {
h := newHaproxy([]byte(""))
defer h.Close()

e, _ := NewExporter(h.URL, true, serverMetrics, excludedServerStates, 5*time.Second, log.NewNopLogger())
e, _ := NewExporter(h.URL, true, false, serverMetrics, excludedServerStates, 5*time.Second, log.NewNopLogger())
ch := make(chan prometheus.Metric)

go func() {
Expand All @@ -150,7 +150,7 @@ func TestDeadline(t *testing.T) {
s.Close()
}()

e, err := NewExporter(s.URL, true, serverMetrics, excludedServerStates, 1*time.Second, log.NewNopLogger())
e, err := NewExporter(s.URL, true, false, serverMetrics, excludedServerStates, 1*time.Second, log.NewNopLogger())
if err != nil {
t.Fatal(err)
}
Expand All @@ -162,7 +162,7 @@ func TestNotFound(t *testing.T) {
s := httptest.NewServer(http.NotFoundHandler())
defer s.Close()

e, err := NewExporter(s.URL, true, serverMetrics, excludedServerStates, 1*time.Second, log.NewNopLogger())
e, err := NewExporter(s.URL, true, false, serverMetrics, excludedServerStates, 1*time.Second, log.NewNopLogger())
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -221,7 +221,7 @@ func TestUnixDomain(t *testing.T) {
}
defer srv.Close()

e, err := NewExporter("unix:"+testSocket, true, serverMetrics, excludedServerStates, 5*time.Second, log.NewNopLogger())
e, err := NewExporter("unix:"+testSocket, true, false, serverMetrics, excludedServerStates, 5*time.Second, log.NewNopLogger())
if err != nil {
t.Fatal(err)
}
Expand All @@ -238,7 +238,7 @@ func TestUnixDomainNotFound(t *testing.T) {
if err := os.Remove(testSocket); err != nil && !os.IsNotExist(err) {
t.Fatal(err)
}
e, _ := NewExporter("unix:"+testSocket, true, serverMetrics, excludedServerStates, 1*time.Second, log.NewNopLogger())
e, _ := NewExporter("unix:"+testSocket, true, false, serverMetrics, excludedServerStates, 1*time.Second, log.NewNopLogger())
expectMetrics(t, e, "unix_domain_not_found.metrics")
}

Expand Down Expand Up @@ -271,13 +271,13 @@ func TestUnixDomainDeadline(t *testing.T) {
}
}()

e, _ := NewExporter("unix:"+testSocket, true, serverMetrics, excludedServerStates, 1*time.Second, log.NewNopLogger())
e, _ := NewExporter("unix:"+testSocket, true, false, serverMetrics, excludedServerStates, 1*time.Second, log.NewNopLogger())

expectMetrics(t, e, "unix_domain_deadline.metrics")
}

func TestInvalidScheme(t *testing.T) {
e, err := NewExporter("gopher://gopher.quux.org", true, serverMetrics, excludedServerStates, 1*time.Second, log.NewNopLogger())
e, err := NewExporter("gopher://gopher.quux.org", true, false, serverMetrics, excludedServerStates, 1*time.Second, log.NewNopLogger())
if expect, got := (*Exporter)(nil), e; expect != got {
t.Errorf("expected %v, got %v", expect, got)
}
Expand Down Expand Up @@ -352,7 +352,7 @@ func BenchmarkExtract(b *testing.B) {
h := newHaproxy(config)
defer h.Close()

e, _ := NewExporter(h.URL, true, serverMetrics, excludedServerStates, 5*time.Second, log.NewNopLogger())
e, _ := NewExporter(h.URL, true, false, serverMetrics, excludedServerStates, 5*time.Second, log.NewNopLogger())

var before, after runtime.MemStats
runtime.GC()
Expand Down

0 comments on commit 87a48e7

Please sign in to comment.