You've already forked nginx_exporter
mirror of
https://github.com/nginxinc/nginx-prometheus-exporter.git
synced 2025-08-08 05:02:04 +03:00
Fix nginxRetries and timeout accepts a negative value
* Bug introduced in: b499ca793a
This commit is contained in:
78
exporter.go
78
exporter.go
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@@ -26,16 +27,16 @@ func getEnv(key, defaultValue string) string {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEnvInt(key string, defaultValue int) int {
|
func getEnvUint(key string, defaultValue uint) uint {
|
||||||
value, ok := os.LookupEnv(key)
|
value, ok := os.LookupEnv(key)
|
||||||
if !ok {
|
if !ok {
|
||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
i, err := strconv.ParseInt(value, 10, 64)
|
i, err := strconv.ParseUint(value, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Environment variable value for %s must be an int: %v", key, err)
|
log.Fatalf("Environment variable value for %s must be an uint: %v", key, err)
|
||||||
}
|
}
|
||||||
return int(i)
|
return uint(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEnvBool(key string, defaultValue bool) bool {
|
func getEnvBool(key string, defaultValue bool) bool {
|
||||||
@@ -50,23 +51,53 @@ func getEnvBool(key string, defaultValue bool) bool {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEnvDuration(key string, defaultValue time.Duration) time.Duration {
|
func getEnvPositiveDuration(key string, defaultValue time.Duration) positiveDuration {
|
||||||
value, ok := os.LookupEnv(key)
|
value, ok := os.LookupEnv(key)
|
||||||
if !ok {
|
if !ok {
|
||||||
return defaultValue
|
return positiveDuration{defaultValue}
|
||||||
}
|
}
|
||||||
d, err := time.ParseDuration(value)
|
|
||||||
|
posDur, err := parsePositiveDuration(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Environment variable value for %s must be a duration: %v", key, err)
|
log.Fatalf("Environment variable value for %s must be a positive duration: %v", key, err)
|
||||||
}
|
}
|
||||||
return d
|
return posDur
|
||||||
}
|
}
|
||||||
|
|
||||||
func createClientWithRetries(getClient func() (interface{}, error), retries int, retryInterval time.Duration) (interface{}, error) {
|
// positiveDuration is a wrapper of time.Duration to ensure only positive values are accepted
|
||||||
|
type positiveDuration struct{ time.Duration }
|
||||||
|
|
||||||
|
func (pd *positiveDuration) Set(s string) error {
|
||||||
|
dur, err := parsePositiveDuration(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pd.Duration = dur.Duration
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePositiveDuration(s string) (positiveDuration, error) {
|
||||||
|
dur, err := time.ParseDuration(s)
|
||||||
|
if err != nil {
|
||||||
|
return positiveDuration{}, err
|
||||||
|
}
|
||||||
|
if dur < 0 {
|
||||||
|
return positiveDuration{}, fmt.Errorf("negative duration %v is not valid", dur)
|
||||||
|
}
|
||||||
|
return positiveDuration{dur}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createPositiveDurationFlag(name string, value positiveDuration, usage string) *positiveDuration {
|
||||||
|
flag.Var(&value, name, usage)
|
||||||
|
return &value
|
||||||
|
}
|
||||||
|
|
||||||
|
func createClientWithRetries(getClient func() (interface{}, error), retries uint, retryInterval time.Duration) (interface{}, error) {
|
||||||
var err error
|
var err error
|
||||||
var nginxClient interface{}
|
var nginxClient interface{}
|
||||||
|
|
||||||
for i := retries; i >= 0; i-- {
|
for i := 0; i <= int(retries); i++ {
|
||||||
nginxClient, err = getClient()
|
nginxClient, err = getClient()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nginxClient, nil
|
return nginxClient, nil
|
||||||
@@ -90,9 +121,9 @@ var (
|
|||||||
defaultNginxPlus = getEnvBool("NGINX_PLUS", false)
|
defaultNginxPlus = getEnvBool("NGINX_PLUS", false)
|
||||||
defaultScrapeURI = getEnv("SCRAPE_URI", "http://127.0.0.1:8080/stub_status")
|
defaultScrapeURI = getEnv("SCRAPE_URI", "http://127.0.0.1:8080/stub_status")
|
||||||
defaultSslVerify = getEnvBool("SSL_VERIFY", true)
|
defaultSslVerify = getEnvBool("SSL_VERIFY", true)
|
||||||
defaultTimeout = getEnvDuration("TIMEOUT", time.Second*5)
|
defaultTimeout = getEnvPositiveDuration("TIMEOUT", time.Second*5)
|
||||||
defaultNginxRetries = getEnvInt("NGINX_RETRIES", 0)
|
defaultNginxRetries = getEnvUint("NGINX_RETRIES", 0)
|
||||||
defaultNginxRetryInterval = getEnvDuration("NGINX_RETRY_INTERVAL", time.Second*5)
|
defaultNginxRetryInterval = getEnvPositiveDuration("NGINX_RETRY_INTERVAL", time.Second*5)
|
||||||
|
|
||||||
// Command-line flags
|
// Command-line flags
|
||||||
listenAddr = flag.String("web.listen-address",
|
listenAddr = flag.String("web.listen-address",
|
||||||
@@ -111,13 +142,16 @@ var (
|
|||||||
sslVerify = flag.Bool("nginx.ssl-verify",
|
sslVerify = flag.Bool("nginx.ssl-verify",
|
||||||
defaultSslVerify,
|
defaultSslVerify,
|
||||||
"Perform SSL certificate verification. The default value can be overwritten by SSL_VERIFY environment variable.")
|
"Perform SSL certificate verification. The default value can be overwritten by SSL_VERIFY environment variable.")
|
||||||
timeout = flag.Duration("nginx.timeout",
|
nginxRetries = flag.Uint("nginx.retries",
|
||||||
defaultTimeout,
|
|
||||||
"A timeout for scraping metrics from NGINX or NGINX Plus. The default value can be overwritten by TIMEOUT environment variable.")
|
|
||||||
nginxRetries = flag.Int("nginx.retries",
|
|
||||||
defaultNginxRetries,
|
defaultNginxRetries,
|
||||||
"A number of retries the exporter will make on start to connect to the NGINX stub_status page/NGINX Plus API before exiting with an error. The default value can be overwritten by NGINX_RETRIES environment variable.")
|
"A number of retries the exporter will make on start to connect to the NGINX stub_status page/NGINX Plus API before exiting with an error. The default value can be overwritten by NGINX_RETRIES environment variable.")
|
||||||
nginxRetryInterval = flag.Duration("nginx.retry-interval",
|
|
||||||
|
// Custom command-line flags
|
||||||
|
timeout = createPositiveDurationFlag("nginx.timeout",
|
||||||
|
defaultTimeout,
|
||||||
|
"A timeout for scraping metrics from NGINX or NGINX Plus. The default value can be overwritten by TIMEOUT environment variable.")
|
||||||
|
|
||||||
|
nginxRetryInterval = createPositiveDurationFlag("nginx.retry-interval",
|
||||||
defaultNginxRetryInterval,
|
defaultNginxRetryInterval,
|
||||||
"An interval between retries to connect to the NGINX stub_status page/NGINX Plus API on start. The default value can be overwritten by NGINX_RETRY_INTERVAL environment variable.")
|
"An interval between retries to connect to the NGINX stub_status page/NGINX Plus API on start. The default value can be overwritten by NGINX_RETRY_INTERVAL environment variable.")
|
||||||
)
|
)
|
||||||
@@ -144,7 +178,7 @@ func main() {
|
|||||||
registry.MustRegister(buildInfoMetric)
|
registry.MustRegister(buildInfoMetric)
|
||||||
|
|
||||||
httpClient := &http.Client{
|
httpClient := &http.Client{
|
||||||
Timeout: *timeout,
|
Timeout: timeout.Duration,
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: !*sslVerify},
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: !*sslVerify},
|
||||||
},
|
},
|
||||||
@@ -160,7 +194,7 @@ func main() {
|
|||||||
if *nginxPlus {
|
if *nginxPlus {
|
||||||
plusClient, err := createClientWithRetries(func() (interface{}, error) {
|
plusClient, err := createClientWithRetries(func() (interface{}, error) {
|
||||||
return plusclient.NewNginxClient(httpClient, *scrapeURI)
|
return plusclient.NewNginxClient(httpClient, *scrapeURI)
|
||||||
}, *nginxRetries, *nginxRetryInterval)
|
}, *nginxRetries, nginxRetryInterval.Duration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Could not create Nginx Plus Client: %v", err)
|
log.Fatalf("Could not create Nginx Plus Client: %v", err)
|
||||||
}
|
}
|
||||||
@@ -168,7 +202,7 @@ func main() {
|
|||||||
} else {
|
} else {
|
||||||
ossClient, err := createClientWithRetries(func() (interface{}, error) {
|
ossClient, err := createClientWithRetries(func() (interface{}, error) {
|
||||||
return client.NewNginxClient(httpClient, *scrapeURI)
|
return client.NewNginxClient(httpClient, *scrapeURI)
|
||||||
}, *nginxRetries, *nginxRetryInterval)
|
}, *nginxRetries, nginxRetryInterval.Duration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Could not create Nginx Client: %v", err)
|
log.Fatalf("Could not create Nginx Client: %v", err)
|
||||||
}
|
}
|
||||||
|
@@ -11,7 +11,7 @@ func TestCreateClientWithRetries(t *testing.T) {
|
|||||||
type args struct {
|
type args struct {
|
||||||
client interface{}
|
client interface{}
|
||||||
err error
|
err error
|
||||||
retries int
|
retries uint
|
||||||
retryInterval time.Duration
|
retryInterval time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,3 +81,43 @@ func TestCreateClientWithRetries(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParsePositiveDuration(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
testInput string
|
||||||
|
want positiveDuration
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"ParsePositiveDuration returns a positiveDuration",
|
||||||
|
"15ms",
|
||||||
|
positiveDuration{15 * time.Millisecond},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ParsePositiveDuration returns error for trying to parse negative value",
|
||||||
|
"-15ms",
|
||||||
|
positiveDuration{},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ParsePositiveDuration returns error for trying to parse empty string",
|
||||||
|
"",
|
||||||
|
positiveDuration{},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := parsePositiveDuration(tt.testInput)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("parsePositiveDuration() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("parsePositiveDuration() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user