1
0
mirror of https://github.com/nginxinc/nginx-prometheus-exporter.git synced 2025-07-30 10:03:04 +03:00

Don't try to connect to nginx on client creation (#504)

The client now is created without making a call to nginx.
When Collect() is called we try to get the metrics from nginx and if
it's not available it will set 'up' to 0 and not fail anymore.
This commit is contained in:
Luca Comellini
2023-10-09 18:09:38 +02:00
committed by GitHub
parent 06af414484
commit 2ab961100c
3 changed files with 8 additions and 117 deletions

View File

@ -37,14 +37,13 @@ type StubConnections struct {
} }
// NewNginxClient creates an NginxClient. // NewNginxClient creates an NginxClient.
func NewNginxClient(httpClient *http.Client, apiEndpoint string) (*NginxClient, error) { func NewNginxClient(httpClient *http.Client, apiEndpoint string) *NginxClient {
client := &NginxClient{ client := &NginxClient{
apiEndpoint: apiEndpoint, apiEndpoint: apiEndpoint,
httpClient: httpClient, httpClient: httpClient,
} }
_, err := client.GetStubStats() return client
return client, err
} }
// GetStubStats fetches the stub_status metrics. // GetStubStats fetches the stub_status metrics.

View File

@ -19,7 +19,6 @@ import (
"github.com/nginxinc/nginx-prometheus-exporter/collector" "github.com/nginxinc/nginx-prometheus-exporter/collector"
"github.com/alecthomas/kingpin/v2" "github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level" "github.com/go-kit/log/level"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
@ -61,23 +60,6 @@ func createPositiveDurationFlag(s kingpin.Settings) (target *time.Duration) {
return return
} }
func createClientWithRetries(getClient func() (interface{}, error), retries uint, retryInterval time.Duration, logger log.Logger) (interface{}, error) {
var err error
var nginxClient interface{}
for i := 0; i <= int(retries); i++ {
nginxClient, err = getClient()
if err == nil {
return nginxClient, nil
}
if i < int(retries) {
level.Error(logger).Log("msg", fmt.Sprintf("Could not create Nginx Client. Retrying in %v...", retryInterval))
time.Sleep(retryInterval)
}
}
return nil, err
}
func parseUnixSocketAddress(address string) (string, string, error) { func parseUnixSocketAddress(address string) (string, string, error) {
addressParts := strings.Split(address, ":") addressParts := strings.Split(address, ":")
addressPartsLength := len(addressParts) addressPartsLength := len(addressParts)
@ -106,11 +88,9 @@ var (
sslCaCert = kingpin.Flag("nginx.ssl-ca-cert", "Path to the PEM encoded CA certificate file used to validate the servers SSL certificate.").Default("").Envar("SSL_CA_CERT").String() sslCaCert = kingpin.Flag("nginx.ssl-ca-cert", "Path to the PEM encoded CA certificate file used to validate the servers SSL certificate.").Default("").Envar("SSL_CA_CERT").String()
sslClientCert = kingpin.Flag("nginx.ssl-client-cert", "Path to the PEM encoded client certificate file to use when connecting to the server.").Default("").Envar("SSL_CLIENT_CERT").String() sslClientCert = kingpin.Flag("nginx.ssl-client-cert", "Path to the PEM encoded client certificate file to use when connecting to the server.").Default("").Envar("SSL_CLIENT_CERT").String()
sslClientKey = kingpin.Flag("nginx.ssl-client-key", "Path to the PEM encoded client certificate key file to use when connecting to the server.").Default("").Envar("SSL_CLIENT_KEY").String() sslClientKey = kingpin.Flag("nginx.ssl-client-key", "Path to the PEM encoded client certificate key file to use when connecting to the server.").Default("").Envar("SSL_CLIENT_KEY").String()
nginxRetries = kingpin.Flag("nginx.retries", "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.").Default("0").Envar("NGINX_RETRIES").Uint()
// Custom command-line flags // Custom command-line flags
timeout = createPositiveDurationFlag(kingpin.Flag("nginx.timeout", "A timeout for scraping metrics from NGINX or NGINX Plus.").Default("5s").Envar("TIMEOUT")) timeout = createPositiveDurationFlag(kingpin.Flag("nginx.timeout", "A timeout for scraping metrics from NGINX or NGINX Plus.").Default("5s").Envar("TIMEOUT"))
nginxRetryInterval = createPositiveDurationFlag(kingpin.Flag("nginx.retry-interval", "An interval between retries to connect to the NGINX stub_status page/NGINX Plus API on start.").Default("5s").Envar("NGINX_RETRY_INTERVAL"))
) )
const exporterName = "nginx_exporter" const exporterName = "nginx_exporter"
@ -123,7 +103,7 @@ func main() {
// convert deprecated flags to new format // convert deprecated flags to new format
for i, arg := range os.Args { for i, arg := range os.Args {
if strings.HasPrefix(arg, "-") && len(arg) > 1 { if strings.HasPrefix(arg, "-") && !strings.HasPrefix(arg, "--") && len(arg) > 2 {
newArg := fmt.Sprintf("-%s", arg) newArg := fmt.Sprintf("-%s", arg)
level.Warn(logger).Log("msg", "the flag format is deprecated and will be removed in a future release, please use the new format", "old", arg, "new", newArg) level.Warn(logger).Log("msg", "the flag format is deprecated and will be removed in a future release, please use the new format", "old", arg, "new", newArg)
os.Args[i] = newArg os.Args[i] = newArg
@ -195,24 +175,16 @@ func main() {
} }
if *nginxPlus { if *nginxPlus {
plusClient, err := createClientWithRetries(func() (interface{}, error) { plusClient, err := plusclient.NewNginxClient(*scrapeURI, plusclient.WithHTTPClient(httpClient))
return plusclient.NewNginxClient(*scrapeURI, plusclient.WithHTTPClient(httpClient))
}, *nginxRetries, *nginxRetryInterval, logger)
if err != nil { if err != nil {
level.Error(logger).Log("msg", "Could not create Nginx Plus Client", "error", err.Error()) level.Error(logger).Log("msg", "Could not create Nginx Plus Client", "error", err.Error())
os.Exit(1) os.Exit(1)
} }
variableLabelNames := collector.NewVariableLabelNames(nil, nil, nil, nil, nil, nil) variableLabelNames := collector.NewVariableLabelNames(nil, nil, nil, nil, nil, nil)
prometheus.MustRegister(collector.NewNginxPlusCollector(plusClient.(*plusclient.NginxClient), "nginxplus", variableLabelNames, constLabels, logger)) prometheus.MustRegister(collector.NewNginxPlusCollector(plusClient, "nginxplus", variableLabelNames, constLabels, logger))
} else { } else {
ossClient, err := createClientWithRetries(func() (interface{}, error) { ossClient := client.NewNginxClient(httpClient, *scrapeURI)
return client.NewNginxClient(httpClient, *scrapeURI) prometheus.MustRegister(collector.NewNginxCollector(ossClient, "nginx", constLabels, logger))
}, *nginxRetries, *nginxRetryInterval, logger)
if err != nil {
level.Error(logger).Log("msg", "Could not create Nginx Client", "error", err.Error())
os.Exit(1)
}
prometheus.MustRegister(collector.NewNginxCollector(ossClient.(*client.NginxClient), "nginx", constLabels, logger))
} }
http.Handle(*metricsPath, promhttp.Handler()) http.Handle(*metricsPath, promhttp.Handler())

View File

@ -1,91 +1,11 @@
package main package main
import ( import (
"errors"
"reflect" "reflect"
"testing" "testing"
"time" "time"
"github.com/go-kit/log"
) )
func TestCreateClientWithRetries(t *testing.T) {
t.Parallel()
type args struct {
client interface{}
err error
retries uint
retryInterval time.Duration
}
tests := []struct {
name string
args args
expectedRetries int
want interface{}
wantErr bool
}{
{
name: "getClient returns a valid client",
args: args{
client: "client",
err: nil,
},
expectedRetries: 0,
want: "client",
wantErr: false,
},
{
name: "getClient returns an error after no retries",
args: args{
client: nil,
err: errors.New("error"),
},
expectedRetries: 0,
want: nil,
wantErr: true,
},
{
name: "getClient returns an error after retries",
args: args{
client: nil,
err: errors.New("error"),
retries: 3,
retryInterval: time.Millisecond * 1,
},
expectedRetries: 3,
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
invocations := 0
getClient := func() (interface{}, error) {
invocations++
return tt.args.client, tt.args.err
}
got, err := createClientWithRetries(getClient, tt.args.retries, tt.args.retryInterval, log.NewNopLogger())
actualRetries := invocations - 1
if actualRetries != tt.expectedRetries {
t.Errorf("createClientWithRetries() got %v retries, expected %v", actualRetries, tt.expectedRetries)
return
} else if (err != nil) != tt.wantErr {
t.Errorf("createClientWithRetries() error = %v, wantErr %v", err, tt.wantErr)
return
} else if err != nil && tt.wantErr {
return
} else if !reflect.DeepEqual(got, tt.want) {
t.Errorf("createClientWithRetries() = %v, want %v", got, tt.want)
}
})
}
}
func TestParsePositiveDuration(t *testing.T) { func TestParsePositiveDuration(t *testing.T) {
t.Parallel() t.Parallel()