Avoid nil point when config.Metrics is nil and expose all metrics if none are configured (#4101)
Co-authored-by: Nikola Jokic <jokicnikola07@gmail.com>
This commit is contained in:
parent
9d8c59aeb3
commit
c359d14e69
|
|
@ -69,8 +69,8 @@ func New(config config.Config) (*App, error) {
|
||||||
Repository: ghConfig.Repository,
|
Repository: ghConfig.Repository,
|
||||||
ServerAddr: config.MetricsAddr,
|
ServerAddr: config.MetricsAddr,
|
||||||
ServerEndpoint: config.MetricsEndpoint,
|
ServerEndpoint: config.MetricsEndpoint,
|
||||||
|
Metrics: config.Metrics,
|
||||||
Logger: app.logger.WithName("metrics exporter"),
|
Logger: app.logger.WithName("metrics exporter"),
|
||||||
Metrics: *config.Metrics,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -154,13 +154,148 @@ type ExporterConfig struct {
|
||||||
ServerAddr string
|
ServerAddr string
|
||||||
ServerEndpoint string
|
ServerEndpoint string
|
||||||
Logger logr.Logger
|
Logger logr.Logger
|
||||||
Metrics v1alpha1.MetricsConfig
|
Metrics *v1alpha1.MetricsConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultMetrics = v1alpha1.MetricsConfig{
|
||||||
|
Counters: map[string]*v1alpha1.CounterMetric{
|
||||||
|
MetricStartedJobsTotal: {
|
||||||
|
Labels: []string{
|
||||||
|
labelKeyEnterprise,
|
||||||
|
labelKeyOrganization,
|
||||||
|
labelKeyRepository,
|
||||||
|
labelKeyJobName,
|
||||||
|
labelKeyEventName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MetricCompletedJobsTotal: {
|
||||||
|
Labels: []string{
|
||||||
|
labelKeyEnterprise,
|
||||||
|
labelKeyOrganization,
|
||||||
|
labelKeyRepository,
|
||||||
|
labelKeyJobName,
|
||||||
|
labelKeyEventName,
|
||||||
|
labelKeyJobResult,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Gauges: map[string]*v1alpha1.GaugeMetric{
|
||||||
|
MetricAssignedJobs: {
|
||||||
|
Labels: []string{
|
||||||
|
labelKeyEnterprise,
|
||||||
|
labelKeyOrganization,
|
||||||
|
labelKeyRepository,
|
||||||
|
labelKeyRunnerScaleSetName,
|
||||||
|
labelKeyRunnerScaleSetNamespace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MetricRunningJobs: {
|
||||||
|
Labels: []string{
|
||||||
|
labelKeyEnterprise,
|
||||||
|
labelKeyOrganization,
|
||||||
|
labelKeyRepository,
|
||||||
|
labelKeyRunnerScaleSetName,
|
||||||
|
labelKeyRunnerScaleSetNamespace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MetricRegisteredRunners: {
|
||||||
|
Labels: []string{
|
||||||
|
labelKeyEnterprise,
|
||||||
|
labelKeyOrganization,
|
||||||
|
labelKeyRepository,
|
||||||
|
labelKeyRunnerScaleSetName,
|
||||||
|
labelKeyRunnerScaleSetNamespace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MetricBusyRunners: {
|
||||||
|
Labels: []string{
|
||||||
|
labelKeyEnterprise,
|
||||||
|
labelKeyOrganization,
|
||||||
|
labelKeyRepository,
|
||||||
|
labelKeyRunnerScaleSetName,
|
||||||
|
labelKeyRunnerScaleSetNamespace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MetricMinRunners: {
|
||||||
|
Labels: []string{
|
||||||
|
labelKeyEnterprise,
|
||||||
|
labelKeyOrganization,
|
||||||
|
labelKeyRepository,
|
||||||
|
labelKeyRunnerScaleSetName,
|
||||||
|
labelKeyRunnerScaleSetNamespace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MetricMaxRunners: {
|
||||||
|
Labels: []string{
|
||||||
|
labelKeyEnterprise,
|
||||||
|
labelKeyOrganization,
|
||||||
|
labelKeyRepository,
|
||||||
|
labelKeyRunnerScaleSetName,
|
||||||
|
labelKeyRunnerScaleSetNamespace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MetricDesiredRunners: {
|
||||||
|
Labels: []string{
|
||||||
|
labelKeyEnterprise,
|
||||||
|
labelKeyOrganization,
|
||||||
|
labelKeyRepository,
|
||||||
|
labelKeyRunnerScaleSetName,
|
||||||
|
labelKeyRunnerScaleSetNamespace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MetricIdleRunners: {
|
||||||
|
Labels: []string{
|
||||||
|
labelKeyEnterprise,
|
||||||
|
labelKeyOrganization,
|
||||||
|
labelKeyRepository,
|
||||||
|
labelKeyRunnerScaleSetName,
|
||||||
|
labelKeyRunnerScaleSetNamespace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Histograms: map[string]*v1alpha1.HistogramMetric{
|
||||||
|
MetricJobStartupDurationSeconds: {
|
||||||
|
Labels: []string{
|
||||||
|
labelKeyEnterprise,
|
||||||
|
labelKeyOrganization,
|
||||||
|
labelKeyRepository,
|
||||||
|
labelKeyJobName,
|
||||||
|
labelKeyEventName,
|
||||||
|
},
|
||||||
|
Buckets: defaultRuntimeBuckets,
|
||||||
|
},
|
||||||
|
MetricJobExecutionDurationSeconds: {
|
||||||
|
Labels: []string{
|
||||||
|
labelKeyEnterprise,
|
||||||
|
labelKeyOrganization,
|
||||||
|
labelKeyRepository,
|
||||||
|
labelKeyJobName,
|
||||||
|
labelKeyEventName,
|
||||||
|
labelKeyJobResult,
|
||||||
|
},
|
||||||
|
Buckets: defaultRuntimeBuckets,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ExporterConfig) defaults() {
|
||||||
|
if e.ServerAddr == "" {
|
||||||
|
e.ServerAddr = ":8080"
|
||||||
|
}
|
||||||
|
if e.ServerEndpoint == "" {
|
||||||
|
e.ServerEndpoint = "/metrics"
|
||||||
|
}
|
||||||
|
if e.Metrics == nil {
|
||||||
|
defaultMetrics := defaultMetrics
|
||||||
|
e.Metrics = &defaultMetrics
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewExporter(config ExporterConfig) ServerExporter {
|
func NewExporter(config ExporterConfig) ServerExporter {
|
||||||
|
config.defaults()
|
||||||
reg := prometheus.NewRegistry()
|
reg := prometheus.NewRegistry()
|
||||||
|
|
||||||
metrics := installMetrics(config.Metrics, reg, config.Logger)
|
metrics := installMetrics(*config.Metrics, reg, config.Logger)
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
mux.Handle(
|
mux.Handle(
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInstallMetrics(t *testing.T) {
|
func TestInstallMetrics(t *testing.T) {
|
||||||
|
|
@ -86,3 +87,179 @@ func TestInstallMetrics(t *testing.T) {
|
||||||
assert.Equal(t, duration.config.Labels, metricsConfig.Histograms[MetricJobStartupDurationSeconds].Labels)
|
assert.Equal(t, duration.config.Labels, metricsConfig.Histograms[MetricJobStartupDurationSeconds].Labels)
|
||||||
assert.Equal(t, duration.config.Buckets, defaultRuntimeBuckets)
|
assert.Equal(t, duration.config.Buckets, defaultRuntimeBuckets)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewExporter(t *testing.T) {
|
||||||
|
t.Run("with defaults metrics applied", func(t *testing.T) {
|
||||||
|
config := ExporterConfig{
|
||||||
|
ScaleSetName: "test-scale-set",
|
||||||
|
ScaleSetNamespace: "test-namespace",
|
||||||
|
Enterprise: "",
|
||||||
|
Organization: "org",
|
||||||
|
Repository: "repo",
|
||||||
|
ServerAddr: ":6060",
|
||||||
|
ServerEndpoint: "/metrics",
|
||||||
|
Logger: logr.Discard(),
|
||||||
|
Metrics: nil, // when metrics is nil, all default metrics should be registered
|
||||||
|
}
|
||||||
|
|
||||||
|
exporter, ok := NewExporter(config).(*exporter)
|
||||||
|
require.True(t, ok, "expected exporter to be of type *exporter")
|
||||||
|
require.NotNil(t, exporter)
|
||||||
|
|
||||||
|
reg := prometheus.NewRegistry()
|
||||||
|
wantMetrics := installMetrics(defaultMetrics, reg, config.Logger)
|
||||||
|
|
||||||
|
assert.Equal(t, len(wantMetrics.counters), len(exporter.counters))
|
||||||
|
for k, v := range wantMetrics.counters {
|
||||||
|
assert.Contains(t, exporter.counters, k)
|
||||||
|
assert.Equal(t, v.config, exporter.counters[k].config)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, len(wantMetrics.gauges), len(exporter.gauges))
|
||||||
|
for k, v := range wantMetrics.gauges {
|
||||||
|
assert.Contains(t, exporter.gauges, k)
|
||||||
|
assert.Equal(t, v.config, exporter.gauges[k].config)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, len(wantMetrics.histograms), len(exporter.histograms))
|
||||||
|
for k, v := range wantMetrics.histograms {
|
||||||
|
assert.Contains(t, exporter.histograms, k)
|
||||||
|
assert.Equal(t, v.config, exporter.histograms[k].config)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NotNil(t, exporter.srv)
|
||||||
|
assert.Equal(t, config.ServerAddr, exporter.srv.Addr)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("with default server URL", func(t *testing.T) {
|
||||||
|
config := ExporterConfig{
|
||||||
|
ScaleSetName: "test-scale-set",
|
||||||
|
ScaleSetNamespace: "test-namespace",
|
||||||
|
Enterprise: "",
|
||||||
|
Organization: "org",
|
||||||
|
Repository: "repo",
|
||||||
|
ServerAddr: "", // empty ServerAddr should default to ":8080"
|
||||||
|
ServerEndpoint: "",
|
||||||
|
Logger: logr.Discard(),
|
||||||
|
Metrics: nil, // when metrics is nil, all default metrics should be registered
|
||||||
|
}
|
||||||
|
|
||||||
|
exporter, ok := NewExporter(config).(*exporter)
|
||||||
|
require.True(t, ok, "expected exporter to be of type *exporter")
|
||||||
|
require.NotNil(t, exporter)
|
||||||
|
|
||||||
|
reg := prometheus.NewRegistry()
|
||||||
|
wantMetrics := installMetrics(defaultMetrics, reg, config.Logger)
|
||||||
|
|
||||||
|
assert.Equal(t, len(wantMetrics.counters), len(exporter.counters))
|
||||||
|
for k, v := range wantMetrics.counters {
|
||||||
|
assert.Contains(t, exporter.counters, k)
|
||||||
|
assert.Equal(t, v.config, exporter.counters[k].config)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, len(wantMetrics.gauges), len(exporter.gauges))
|
||||||
|
for k, v := range wantMetrics.gauges {
|
||||||
|
assert.Contains(t, exporter.gauges, k)
|
||||||
|
assert.Equal(t, v.config, exporter.gauges[k].config)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, len(wantMetrics.histograms), len(exporter.histograms))
|
||||||
|
for k, v := range wantMetrics.histograms {
|
||||||
|
assert.Contains(t, exporter.histograms, k)
|
||||||
|
assert.Equal(t, v.config, exporter.histograms[k].config)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NotNil(t, exporter.srv)
|
||||||
|
assert.Equal(t, exporter.srv.Addr, ":8080")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("with metrics configured", func(t *testing.T) {
|
||||||
|
metricsConfig := v1alpha1.MetricsConfig{
|
||||||
|
Counters: map[string]*v1alpha1.CounterMetric{
|
||||||
|
MetricStartedJobsTotal: {
|
||||||
|
Labels: []string{labelKeyRepository},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Gauges: map[string]*v1alpha1.GaugeMetric{
|
||||||
|
MetricAssignedJobs: {
|
||||||
|
Labels: []string{labelKeyRepository},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Histograms: map[string]*v1alpha1.HistogramMetric{
|
||||||
|
MetricJobExecutionDurationSeconds: {
|
||||||
|
Labels: []string{labelKeyRepository},
|
||||||
|
Buckets: []float64{0.1, 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
config := ExporterConfig{
|
||||||
|
ScaleSetName: "test-scale-set",
|
||||||
|
ScaleSetNamespace: "test-namespace",
|
||||||
|
Enterprise: "",
|
||||||
|
Organization: "org",
|
||||||
|
Repository: "repo",
|
||||||
|
ServerAddr: ":6060",
|
||||||
|
ServerEndpoint: "/metrics",
|
||||||
|
Logger: logr.Discard(),
|
||||||
|
Metrics: &metricsConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
exporter, ok := NewExporter(config).(*exporter)
|
||||||
|
require.True(t, ok, "expected exporter to be of type *exporter")
|
||||||
|
require.NotNil(t, exporter)
|
||||||
|
|
||||||
|
reg := prometheus.NewRegistry()
|
||||||
|
wantMetrics := installMetrics(metricsConfig, reg, config.Logger)
|
||||||
|
|
||||||
|
assert.Equal(t, len(wantMetrics.counters), len(exporter.counters))
|
||||||
|
for k, v := range wantMetrics.counters {
|
||||||
|
assert.Contains(t, exporter.counters, k)
|
||||||
|
assert.Equal(t, v.config, exporter.counters[k].config)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, len(wantMetrics.gauges), len(exporter.gauges))
|
||||||
|
for k, v := range wantMetrics.gauges {
|
||||||
|
assert.Contains(t, exporter.gauges, k)
|
||||||
|
assert.Equal(t, v.config, exporter.gauges[k].config)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, len(wantMetrics.histograms), len(exporter.histograms))
|
||||||
|
for k, v := range wantMetrics.histograms {
|
||||||
|
assert.Contains(t, exporter.histograms, k)
|
||||||
|
assert.Equal(t, v.config, exporter.histograms[k].config)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NotNil(t, exporter.srv)
|
||||||
|
assert.Equal(t, config.ServerAddr, exporter.srv.Addr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExporterConfigDefaults(t *testing.T) {
|
||||||
|
config := ExporterConfig{
|
||||||
|
ScaleSetName: "test-scale-set",
|
||||||
|
ScaleSetNamespace: "test-namespace",
|
||||||
|
Enterprise: "",
|
||||||
|
Organization: "org",
|
||||||
|
Repository: "repo",
|
||||||
|
ServerAddr: "",
|
||||||
|
ServerEndpoint: "",
|
||||||
|
Logger: logr.Discard(),
|
||||||
|
Metrics: nil, // when metrics is nil, all default metrics should be registered
|
||||||
|
}
|
||||||
|
|
||||||
|
config.defaults()
|
||||||
|
want := ExporterConfig{
|
||||||
|
ScaleSetName: "test-scale-set",
|
||||||
|
ScaleSetNamespace: "test-namespace",
|
||||||
|
Enterprise: "",
|
||||||
|
Organization: "org",
|
||||||
|
Repository: "repo",
|
||||||
|
ServerAddr: ":8080", // default server address
|
||||||
|
ServerEndpoint: "/metrics", // default server endpoint
|
||||||
|
Logger: logr.Discard(),
|
||||||
|
Metrics: &defaultMetrics, // when metrics is nil, all default metrics should be registered
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, want, config)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue