2023-10-12 09:50:27 +00:00
|
|
|
package config
|
|
|
|
|
|
|
|
import (
|
|
|
|
"reflect"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"go.opentelemetry.io/otel/attribute"
|
|
|
|
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
2024-10-07 14:10:20 +00:00
|
|
|
"k8s.io/utils/ptr"
|
2023-10-12 09:50:27 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func Test_metricsConfig_load(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
configMap *corev1.ConfigMap
|
|
|
|
expectedValue *metricsConfig
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "Case 1: Test defaults",
|
|
|
|
configMap: &corev1.ConfigMap{
|
|
|
|
Data: map[string]string{},
|
|
|
|
},
|
|
|
|
expectedValue: &metricsConfig{
|
|
|
|
metricsRefreshInterval: 0,
|
|
|
|
namespaces: namespacesConfig{IncludeNamespaces: []string{}, ExcludeNamespaces: []string{}},
|
|
|
|
bucketBoundaries: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 15, 20, 25, 30},
|
|
|
|
metricsExposure: map[string]metricExposureConfig{},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "Case 2: All fields provided",
|
|
|
|
configMap: &corev1.ConfigMap{
|
|
|
|
Data: map[string]string{
|
|
|
|
"metricsRefreshInterval": "10s",
|
|
|
|
"namespaces": `{"include": ["namespace1"], "exclude": ["namespace2"]}`,
|
|
|
|
"bucketBoundaries": "0.005, 0.01, 0.025, 0.05",
|
|
|
|
"metricsExposure": `{"metric1": {"enabled": true, "disabledLabelDimensions": ["dim1"]}, "metric2": {"enabled": true, "disabledLabelDimensions": ["dim1","dim2"], "bucketBoundaries": [0.025, 0.05]}}`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expectedValue: &metricsConfig{
|
|
|
|
metricsRefreshInterval: 10 * time.Second,
|
|
|
|
namespaces: namespacesConfig{IncludeNamespaces: []string{"namespace1"}, ExcludeNamespaces: []string{"namespace2"}},
|
|
|
|
bucketBoundaries: []float64{0.005, 0.01, 0.025, 0.05},
|
|
|
|
metricsExposure: map[string]metricExposureConfig{
|
2024-10-07 14:10:20 +00:00
|
|
|
"metric1": {Enabled: ptr.To(true), DisabledLabelDimensions: []string{"dim1"}, BucketBoundaries: []float64{0.005, 0.01, 0.025, 0.05}},
|
|
|
|
"metric2": {Enabled: ptr.To(true), DisabledLabelDimensions: []string{"dim1", "dim2"}, BucketBoundaries: []float64{0.025, 0.05}},
|
2023-10-12 09:50:27 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "Case 3: Some of the fields provided",
|
|
|
|
configMap: &corev1.ConfigMap{
|
|
|
|
Data: map[string]string{
|
|
|
|
"namespaces": `{"include": ["namespace1"], "exclude": ["namespace2"]}`,
|
|
|
|
"metricsExposure": `{"metric1": {"enabled": true, "disabledLabelDimensions": ["dim1"]}, "metric2": {"enabled": true, "disabledLabelDimensions": ["dim1","dim2"], "bucketBoundaries": [0.025, 0.05]}}`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expectedValue: &metricsConfig{
|
|
|
|
metricsRefreshInterval: 0,
|
|
|
|
namespaces: namespacesConfig{IncludeNamespaces: []string{"namespace1"}, ExcludeNamespaces: []string{"namespace2"}},
|
|
|
|
bucketBoundaries: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 15, 20, 25, 30},
|
|
|
|
metricsExposure: map[string]metricExposureConfig{
|
2024-10-07 14:10:20 +00:00
|
|
|
"metric1": {Enabled: ptr.To(true), DisabledLabelDimensions: []string{"dim1"}, BucketBoundaries: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 15, 20, 25, 30}},
|
|
|
|
"metric2": {Enabled: ptr.To(true), DisabledLabelDimensions: []string{"dim1", "dim2"}, BucketBoundaries: []float64{0.025, 0.05}},
|
2023-10-12 09:50:27 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
cd := NewDefaultMetricsConfiguration()
|
|
|
|
cd.load(tt.configMap)
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(cd.metricsRefreshInterval, tt.expectedValue.metricsRefreshInterval) {
|
|
|
|
t.Errorf("Expected %+v, but got %+v", tt.expectedValue.metricsRefreshInterval, cd.metricsRefreshInterval)
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(cd.namespaces, tt.expectedValue.namespaces) {
|
|
|
|
t.Errorf("Expected %+v, but got %+v", tt.expectedValue.namespaces, cd.namespaces)
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(cd.bucketBoundaries, tt.expectedValue.bucketBoundaries) {
|
|
|
|
t.Errorf("Expected %+v, but got %+v", tt.expectedValue.bucketBoundaries, cd.bucketBoundaries)
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(cd.metricsExposure, tt.expectedValue.metricsExposure) {
|
|
|
|
t.Errorf("Expected %+v, but got %+v", tt.expectedValue.metricsExposure, cd.metricsRefreshInterval)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Test_metricsConfig_BuildMeterProviderViews(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
metricsExposure map[string]metricExposureConfig
|
|
|
|
expectedSize int
|
|
|
|
validateFunc func([]sdkmetric.View) bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "Case 1: defaults",
|
|
|
|
metricsExposure: map[string]metricExposureConfig{},
|
|
|
|
expectedSize: 0,
|
|
|
|
},
|
|
|
|
{
|
2024-04-22 07:33:04 +00:00
|
|
|
name: "Case 2: there is no matching entry on the exposure config",
|
2023-10-12 09:50:27 +00:00
|
|
|
metricsExposure: map[string]metricExposureConfig{
|
2024-10-07 14:10:20 +00:00
|
|
|
"metric1": {Enabled: ptr.To(true), DisabledLabelDimensions: []string{"dim1"}, BucketBoundaries: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 15, 20, 25, 30}},
|
2023-10-12 09:50:27 +00:00
|
|
|
},
|
|
|
|
expectedSize: 1,
|
2024-04-22 07:33:04 +00:00
|
|
|
validateFunc: func(views []sdkmetric.View) bool {
|
|
|
|
stream, _ := views[0](sdkmetric.Instrument{Name: "metric2"})
|
|
|
|
assert := stream.AttributeFilter == nil
|
|
|
|
assert = assert && stream.Aggregation == nil
|
|
|
|
return assert
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "Case 3: metrics enabled, no transformation configured",
|
|
|
|
metricsExposure: map[string]metricExposureConfig{
|
2024-10-07 14:10:20 +00:00
|
|
|
"metric1": {Enabled: ptr.To(true)},
|
2024-04-22 07:33:04 +00:00
|
|
|
},
|
|
|
|
expectedSize: 1,
|
2023-10-12 09:50:27 +00:00
|
|
|
validateFunc: func(views []sdkmetric.View) bool {
|
|
|
|
stream, _ := views[0](sdkmetric.Instrument{Name: "metric1"})
|
2024-04-22 07:33:04 +00:00
|
|
|
assert := stream.AttributeFilter == nil
|
|
|
|
assert = assert && stream.Aggregation == nil
|
|
|
|
return assert
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "Case 4: metrics enabled, histogram metric",
|
|
|
|
metricsExposure: map[string]metricExposureConfig{
|
2024-10-07 14:10:20 +00:00
|
|
|
"metric1": {Enabled: ptr.To(true), DisabledLabelDimensions: []string{"dim1"}, BucketBoundaries: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 15, 20, 25, 30}},
|
2024-04-22 07:33:04 +00:00
|
|
|
},
|
|
|
|
expectedSize: 1,
|
|
|
|
validateFunc: func(views []sdkmetric.View) bool {
|
|
|
|
stream, _ := views[0](sdkmetric.Instrument{Name: "metric1", Kind: sdkmetric.InstrumentKindHistogram})
|
2023-10-12 09:50:27 +00:00
|
|
|
assert := stream.AttributeFilter(attribute.String("policy_validation_mode", ""))
|
|
|
|
assert = assert && !stream.AttributeFilter(attribute.String("dim1", ""))
|
|
|
|
assert = assert && reflect.DeepEqual(stream.Aggregation, sdkmetric.AggregationExplicitBucketHistogram{
|
|
|
|
Boundaries: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 15, 20, 25, 30},
|
|
|
|
NoMinMax: false,
|
|
|
|
})
|
|
|
|
return assert
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2024-04-22 07:33:04 +00:00
|
|
|
name: "Case 5: metrics enabled, non histogram metric",
|
|
|
|
metricsExposure: map[string]metricExposureConfig{
|
2024-10-07 14:10:20 +00:00
|
|
|
"metric1": {Enabled: ptr.To(true), DisabledLabelDimensions: []string{"dim1"}, BucketBoundaries: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 15, 20, 25, 30}},
|
2024-04-22 07:33:04 +00:00
|
|
|
},
|
|
|
|
expectedSize: 1,
|
|
|
|
validateFunc: func(views []sdkmetric.View) bool {
|
|
|
|
stream, _ := views[0](sdkmetric.Instrument{Name: "metric1", Kind: sdkmetric.InstrumentKindCounter})
|
|
|
|
assert := stream.AttributeFilter(attribute.String("policy_validation_mode", ""))
|
|
|
|
assert = assert && !stream.AttributeFilter(attribute.String("dim1", ""))
|
|
|
|
assert = assert && stream.Aggregation == nil
|
|
|
|
return assert
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "Case 6: metrics disabled",
|
2023-10-12 09:50:27 +00:00
|
|
|
metricsExposure: map[string]metricExposureConfig{
|
2024-10-07 14:10:20 +00:00
|
|
|
"metric1": {Enabled: ptr.To(false)},
|
2023-10-12 09:50:27 +00:00
|
|
|
},
|
|
|
|
expectedSize: 1,
|
|
|
|
validateFunc: func(views []sdkmetric.View) bool {
|
|
|
|
stream, _ := views[0](sdkmetric.Instrument{Name: "metric1"})
|
|
|
|
return reflect.DeepEqual(stream.Aggregation, sdkmetric.AggregationDrop{})
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
mcd := NewDefaultMetricsConfiguration()
|
|
|
|
mcd.metricsExposure = tt.metricsExposure
|
|
|
|
got := mcd.BuildMeterProviderViews()
|
|
|
|
if len(got) != tt.expectedSize {
|
|
|
|
t.Errorf("Expected result size to be %v, but got %v", tt.expectedSize, len(got))
|
|
|
|
}
|
|
|
|
if tt.validateFunc != nil {
|
|
|
|
if !tt.validateFunc(got) {
|
|
|
|
t.Errorf("The validation function did not return true!")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Test_metricsConfig_GetBucketBoundaries(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
provided []float64
|
|
|
|
want []float64
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "Case 1: Test defaults",
|
|
|
|
provided: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 15, 20, 25, 30},
|
|
|
|
want: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 15, 20, 25, 30},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "Case 2: Custom",
|
|
|
|
provided: []float64{0.005, 0.01, 0.025, 0.05},
|
|
|
|
want: []float64{0.005, 0.01, 0.025, 0.05},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "Case 3: Empty",
|
|
|
|
provided: []float64{},
|
|
|
|
want: []float64{},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
mcd := NewDefaultMetricsConfiguration()
|
|
|
|
mcd.bucketBoundaries = tt.provided
|
|
|
|
if got := mcd.GetBucketBoundaries(); !reflect.DeepEqual(got, tt.want) {
|
|
|
|
t.Errorf("GetBucketBoundaries() = %v, want %v", got, tt.want)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|