mirror of
https://github.com/hanzoai/traces.git
synced 2026-10-30 18:20:41 +00:00
* Add span_name_sanitization as user-configurable override This adds support for configuring span_name_sanitization through the user-configurable overrides API, allowing tenants to enable the DRAIN span name sanitizer without requiring operator configuration changes. Changes: - Add SpanNameSanitization field to LimitsMetricsGenerator - Add MetricsGeneratorSpanNameSanitization() to overrides manager - Add validation for span_name_sanitization in overrides API - Add comprehensive tests for the new configuration option - Add integration test for the overrides API endpoint Co-authored-by: Cursor <[email protected]> * Add PR link * Add documentation for span_name_sanitization override Co-authored-by: Cursor <[email protected]> --------- Co-authored-by: Cursor <[email protected]>
872 lines
26 KiB
Go
872 lines
26 KiB
Go
package app
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/grafana/dskit/ring"
|
|
"github.com/grafana/tempo/modules/distributor"
|
|
"github.com/grafana/tempo/modules/distributor/forwarder"
|
|
"github.com/grafana/tempo/modules/generator/processor"
|
|
"github.com/grafana/tempo/modules/generator/validation"
|
|
"github.com/grafana/tempo/modules/ingester"
|
|
"github.com/grafana/tempo/modules/overrides"
|
|
"github.com/grafana/tempo/modules/overrides/histograms"
|
|
"github.com/grafana/tempo/modules/overrides/userconfigurable/client"
|
|
"github.com/grafana/tempo/pkg/sharedconfig"
|
|
filterconfig "github.com/grafana/tempo/pkg/spanfilter/config"
|
|
"github.com/grafana/tempo/tempodb/backend"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func strPtr(s string) *string {
|
|
return &s
|
|
}
|
|
|
|
func float64Ptr(f float64) *float64 {
|
|
return &f
|
|
}
|
|
|
|
func boolMapPtr(m map[string]bool) *map[string]bool {
|
|
return &m
|
|
}
|
|
|
|
func strArrPtr(s []string) *[]string {
|
|
return &s
|
|
}
|
|
|
|
func dimensionMappingsPtr(d []sharedconfig.DimensionMappings) *[]sharedconfig.DimensionMappings {
|
|
return &d
|
|
}
|
|
|
|
func histogramMethodPtr(h histograms.HistogramMethod) *histograms.HistogramMethod {
|
|
return &h
|
|
}
|
|
|
|
func Test_runtimeOverridesValidator(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
cfg Config
|
|
overrides overrides.Overrides
|
|
expErr string
|
|
expWarnings []error
|
|
}{
|
|
{
|
|
name: "ingestion.tenant_shard_size smaller than RF",
|
|
cfg: Config{
|
|
Ingester: ingester.Config{
|
|
LifecyclerConfig: ring.LifecyclerConfig{
|
|
RingConfig: ring.Config{
|
|
ReplicationFactor: 3,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
overrides: overrides.Overrides{Ingestion: overrides.IngestionOverrides{TenantShardSize: 2}},
|
|
expErr: "ingester.tenant.shard_size is lower than replication factor (2 < 3)",
|
|
},
|
|
{
|
|
name: "ingestion.tenant_shard_size equal to RF",
|
|
cfg: Config{
|
|
Ingester: ingester.Config{
|
|
LifecyclerConfig: ring.LifecyclerConfig{
|
|
RingConfig: ring.Config{
|
|
ReplicationFactor: 3,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
overrides: overrides.Overrides{Ingestion: overrides.IngestionOverrides{TenantShardSize: 3}},
|
|
},
|
|
{
|
|
name: "metrics_generator.generate_native_histograms invalid",
|
|
cfg: Config{},
|
|
overrides: overrides.Overrides{MetricsGenerator: overrides.MetricsGeneratorOverrides{
|
|
GenerateNativeHistograms: "invalid",
|
|
}},
|
|
expErr: "metrics_generator.generate_native_histograms \"invalid\" is not a valid value, valid values: classic, native, both",
|
|
},
|
|
{
|
|
name: "metrics_generator.generate_native_histograms classic",
|
|
cfg: Config{},
|
|
overrides: overrides.Overrides{MetricsGenerator: overrides.MetricsGeneratorOverrides{
|
|
GenerateNativeHistograms: "classic",
|
|
}},
|
|
},
|
|
{
|
|
name: "metrics_generator.generate_native_histograms native",
|
|
cfg: Config{},
|
|
overrides: overrides.Overrides{MetricsGenerator: overrides.MetricsGeneratorOverrides{
|
|
GenerateNativeHistograms: "native",
|
|
}},
|
|
},
|
|
{
|
|
name: "metrics_generator.generate_native_histograms both",
|
|
cfg: Config{},
|
|
overrides: overrides.Overrides{MetricsGenerator: overrides.MetricsGeneratorOverrides{
|
|
GenerateNativeHistograms: "both",
|
|
}},
|
|
},
|
|
{
|
|
name: "service graphs histogram buckets must be increasing",
|
|
cfg: Config{},
|
|
overrides: overrides.Overrides{
|
|
MetricsGenerator: overrides.MetricsGeneratorOverrides{
|
|
Processor: overrides.ProcessorOverrides{
|
|
ServiceGraphs: overrides.ServiceGraphsOverrides{
|
|
HistogramBuckets: []float64{2, 1},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expErr: "metrics_generator.processor.service_graphs.histogram_buckets must be strictly increasing: bucket[1]=1 is <= bucket[0]=2",
|
|
},
|
|
{
|
|
name: "span metrics histogram buckets must be increasing",
|
|
cfg: Config{},
|
|
overrides: overrides.Overrides{
|
|
MetricsGenerator: overrides.MetricsGeneratorOverrides{
|
|
Processor: overrides.ProcessorOverrides{
|
|
SpanMetrics: overrides.SpanMetricsOverrides{
|
|
HistogramBuckets: []float64{0.5, 0.5},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expErr: "metrics_generator.processor.span_metrics.histogram_buckets must be strictly increasing: bucket[1]=0.5 is <= bucket[0]=0.5",
|
|
},
|
|
{
|
|
name: "service graphs histogram buckets valid",
|
|
cfg: Config{},
|
|
overrides: overrides.Overrides{
|
|
MetricsGenerator: overrides.MetricsGeneratorOverrides{
|
|
Processor: overrides.ProcessorOverrides{
|
|
ServiceGraphs: overrides.ServiceGraphsOverrides{
|
|
HistogramBuckets: []float64{0.01, 0.1, 1},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "span metrics histogram buckets valid",
|
|
cfg: Config{},
|
|
overrides: overrides.Overrides{
|
|
MetricsGenerator: overrides.MetricsGeneratorOverrides{
|
|
Processor: overrides.ProcessorOverrides{
|
|
SpanMetrics: overrides.SpanMetricsOverrides{
|
|
HistogramBuckets: []float64{0.005, 0.01, 0.02},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "native histogram bucket factor invalid",
|
|
cfg: Config{},
|
|
overrides: overrides.Overrides{
|
|
MetricsGenerator: overrides.MetricsGeneratorOverrides{
|
|
NativeHistogramBucketFactor: 1,
|
|
},
|
|
},
|
|
expErr: "metrics_generator.native_histogram_bucket_factor must be greater than 1",
|
|
},
|
|
{
|
|
name: "native histogram bucket factor valid",
|
|
cfg: Config{},
|
|
overrides: overrides.Overrides{
|
|
MetricsGenerator: overrides.MetricsGeneratorOverrides{
|
|
NativeHistogramBucketFactor: 1.5,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "valid cost attribution dimensions",
|
|
cfg: Config{},
|
|
overrides: overrides.Overrides{
|
|
CostAttribution: overrides.CostAttributionOverrides{
|
|
Dimensions: map[string]string{"span.name": "op_name"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "invalid cost attribution dimensions",
|
|
cfg: Config{},
|
|
overrides: overrides.Overrides{
|
|
CostAttribution: overrides.CostAttributionOverrides{
|
|
Dimensions: map[string]string{"span.name": "__name__"},
|
|
},
|
|
},
|
|
expErr: "cost_attribution.dimensions config has invalid label name: '__name__'",
|
|
},
|
|
{
|
|
name: "invalid dedicated columns",
|
|
cfg: Config{},
|
|
overrides: overrides.Overrides{
|
|
Storage: overrides.StorageOverrides{
|
|
DedicatedColumns: backend.DedicatedColumns{
|
|
{Name: "dedicated.resource.1", Type: "string", Scope: "foo"},
|
|
},
|
|
},
|
|
},
|
|
expErr: "invalid dedicated attribute columns: unsupported dedicated column scope 'foo'",
|
|
},
|
|
{
|
|
name: "too many dedicated columns",
|
|
cfg: Config{},
|
|
overrides: overrides.Overrides{
|
|
Storage: overrides.StorageOverrides{
|
|
DedicatedColumns: backend.DedicatedColumns{
|
|
{Name: "dedicated.resource.1", Type: "string", Scope: "resource"},
|
|
{Name: "dedicated.resource.2", Type: "string", Scope: "resource"},
|
|
{Name: "dedicated.resource.3", Type: "string", Scope: "resource"},
|
|
{Name: "dedicated.resource.4", Type: "string", Scope: "resource"},
|
|
{Name: "dedicated.resource.5", Type: "string", Scope: "resource"},
|
|
{Name: "dedicated.resource.6", Type: "string", Scope: "resource"},
|
|
{Name: "dedicated.resource.7", Type: "string", Scope: "resource"},
|
|
{Name: "dedicated.resource.8", Type: "string", Scope: "resource"},
|
|
{Name: "dedicated.resource.9", Type: "string", Scope: "resource"},
|
|
{Name: "dedicated.resource.10", Type: "string", Scope: "resource"},
|
|
{Name: "dedicated.resource.11", Type: "string", Scope: "resource"},
|
|
{Name: "dedicated.resource.12", Type: "string", Scope: "resource"},
|
|
{Name: "dedicated.resource.13", Type: "string", Scope: "resource"},
|
|
{Name: "dedicated.resource.14", Type: "string", Scope: "resource"},
|
|
{Name: "dedicated.resource.15", Type: "string", Scope: "resource"},
|
|
{Name: "dedicated.resource.16", Type: "string", Scope: "resource"},
|
|
{Name: "dedicated.resource.17", Type: "string", Scope: "resource"},
|
|
{Name: "dedicated.resource.18", Type: "string", Scope: "resource"},
|
|
{Name: "dedicated.resource.19", Type: "string", Scope: "resource"},
|
|
{Name: "dedicated.resource.20", Type: "string", Scope: "resource"},
|
|
{Name: "dedicated.resource.21", Type: "string", Scope: "resource"},
|
|
},
|
|
},
|
|
},
|
|
expWarnings: []error{
|
|
backend.WarnTooManyColumns{Type: "string", Scope: "resource", Count: 21, MaxCount: 20},
|
|
},
|
|
},
|
|
{
|
|
name: "metrics_generator.span_name_sanitization empty (disabled)",
|
|
cfg: Config{},
|
|
overrides: overrides.Overrides{MetricsGenerator: overrides.MetricsGeneratorOverrides{
|
|
SpanNameSanitization: "",
|
|
}},
|
|
},
|
|
{
|
|
name: "metrics_generator.span_name_sanitization dry_run",
|
|
cfg: Config{},
|
|
overrides: overrides.Overrides{MetricsGenerator: overrides.MetricsGeneratorOverrides{
|
|
SpanNameSanitization: "dry_run",
|
|
}},
|
|
},
|
|
{
|
|
name: "metrics_generator.span_name_sanitization enabled",
|
|
cfg: Config{},
|
|
overrides: overrides.Overrides{MetricsGenerator: overrides.MetricsGeneratorOverrides{
|
|
SpanNameSanitization: "enabled",
|
|
}},
|
|
},
|
|
{
|
|
name: "metrics_generator.span_name_sanitization invalid",
|
|
cfg: Config{},
|
|
overrides: overrides.Overrides{MetricsGenerator: overrides.MetricsGeneratorOverrides{
|
|
SpanNameSanitization: "invalid",
|
|
}},
|
|
expErr: "span_name_sanitization \"invalid\" is not valid, valid values: [ dry_run enabled]",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
validator := newRuntimeConfigValidator(&tc.cfg)
|
|
|
|
warnings, err := validator.Validate(&tc.overrides)
|
|
if tc.expErr != "" {
|
|
assert.EqualError(t, err, tc.expErr)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
assert.Equal(t, tc.expWarnings, warnings)
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_overridesValidator(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
cfg Config
|
|
limits client.Limits
|
|
expErr string
|
|
}{
|
|
{
|
|
name: "forwarders - valid",
|
|
cfg: Config{
|
|
Distributor: distributor.Config{
|
|
Forwarders: []forwarder.Config{
|
|
{Name: "forwarder-1"},
|
|
{Name: "forwarder-2"},
|
|
{Name: "forwarder-3"},
|
|
},
|
|
},
|
|
},
|
|
limits: client.Limits{
|
|
Forwarders: &[]string{"forwarder-1", "forwarder-3"},
|
|
},
|
|
},
|
|
{
|
|
name: "forwarders - invalid",
|
|
cfg: Config{
|
|
Distributor: distributor.Config{
|
|
Forwarders: []forwarder.Config{
|
|
{Name: "forwarder-1"},
|
|
{Name: "forwarder-2"},
|
|
},
|
|
},
|
|
},
|
|
limits: client.Limits{
|
|
Forwarders: &[]string{"forwarder-1", "some-forwarder"},
|
|
},
|
|
expErr: "forwarder \"some-forwarder\" is not a known forwarder, contact your system administrator",
|
|
},
|
|
{
|
|
name: "metrics_generator.processor",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
Processors: map[string]struct{}{
|
|
"service-graphs": {},
|
|
"span-span": {},
|
|
},
|
|
},
|
|
},
|
|
expErr: fmt.Sprintf("metrics_generator.processor \"span-span\" is not a known processor, valid values: %v", validation.SupportedProcessors),
|
|
},
|
|
{
|
|
name: "filter policies",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
Forwarders: &[]string{},
|
|
MetricsGenerator: client.LimitsMetricsGenerator{Processor: client.LimitsMetricsGeneratorProcessor{SpanMetrics: client.LimitsMetricsGeneratorProcessorSpanMetrics{FilterPolicies: &[]filterconfig.FilterPolicy{{
|
|
Include: &filterconfig.PolicyMatch{
|
|
MatchType: filterconfig.Strict,
|
|
Attributes: []filterconfig.MatchPolicyAttribute{
|
|
{
|
|
Key: "span.kind",
|
|
Value: "SPAN_KIND_SERVER",
|
|
},
|
|
},
|
|
},
|
|
}}}}},
|
|
},
|
|
},
|
|
{
|
|
name: "filter policies - invalid",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
Forwarders: &[]string{},
|
|
MetricsGenerator: client.LimitsMetricsGenerator{Processor: client.LimitsMetricsGeneratorProcessor{SpanMetrics: client.LimitsMetricsGeneratorProcessorSpanMetrics{FilterPolicies: &[]filterconfig.FilterPolicy{
|
|
{
|
|
Include: &filterconfig.PolicyMatch{
|
|
MatchType: "invalid",
|
|
},
|
|
},
|
|
}}}},
|
|
},
|
|
expErr: "invalid include policy: invalid match type: invalid",
|
|
},
|
|
{
|
|
name: "metrics_generator.collection_interval valid",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
CollectionInterval: &client.Duration{Duration: 60 * time.Second},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "metrics_generator.collection_interval minimum",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
CollectionInterval: &client.Duration{Duration: 1 * time.Second},
|
|
},
|
|
},
|
|
expErr: "metrics_generator.collection_interval \"1s\" is outside acceptable range of 15s to 5m",
|
|
},
|
|
{
|
|
name: "metrics_generator.collection_interval maximum",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
CollectionInterval: &client.Duration{Duration: 10 * time.Minute},
|
|
},
|
|
},
|
|
expErr: "metrics_generator.collection_interval \"10m0s\" is outside acceptable range of 15s to 5m",
|
|
},
|
|
{
|
|
name: "metrics_generator.trace_id_label_name empty is allowed",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
TraceIDLabelName: strPtr(""),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "metrics_generator.trace_id_label_name invalid",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
TraceIDLabelName: strPtr("trace-id"),
|
|
},
|
|
},
|
|
expErr: "trace_id_label_name \"trace-id\" is not a valid Prometheus label name",
|
|
},
|
|
{
|
|
name: "metrics_generator.ingestion_time_range_slack valid",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
IngestionSlack: &client.Duration{Duration: 2 * time.Minute},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "metrics_generator.ingestion_time_range_slack invalid",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
IngestionSlack: &client.Duration{Duration: 15 * time.Hour},
|
|
},
|
|
},
|
|
expErr: "metrics_generator.ingestion_time_range_slack \"15h0m0s\" is outside acceptable range of 0s to 12h",
|
|
},
|
|
{
|
|
name: "service graphs histogram buckets must be increasing",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
Processor: client.LimitsMetricsGeneratorProcessor{
|
|
ServiceGraphs: client.LimitsMetricsGeneratorProcessorServiceGraphs{
|
|
HistogramBuckets: &[]float64{5, 4},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expErr: "metrics_generator.processor.service_graphs.histogram_buckets must be strictly increasing: bucket[1]=4 is <= bucket[0]=5",
|
|
},
|
|
{
|
|
name: "span metrics histogram buckets must be increasing",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
Processor: client.LimitsMetricsGeneratorProcessor{
|
|
SpanMetrics: client.LimitsMetricsGeneratorProcessorSpanMetrics{
|
|
HistogramBuckets: &[]float64{1, 1},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expErr: "metrics_generator.processor.span_metrics.histogram_buckets must be strictly increasing: bucket[1]=1 is <= bucket[0]=1",
|
|
},
|
|
{
|
|
name: "service graphs histogram buckets valid",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
Processor: client.LimitsMetricsGeneratorProcessor{
|
|
ServiceGraphs: client.LimitsMetricsGeneratorProcessorServiceGraphs{
|
|
HistogramBuckets: &[]float64{0.01, 0.1, 1},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "span metrics histogram buckets valid",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
Processor: client.LimitsMetricsGeneratorProcessor{
|
|
SpanMetrics: client.LimitsMetricsGeneratorProcessorSpanMetrics{
|
|
HistogramBuckets: &[]float64{0.005, 0.01, 0.02},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "native histogram bucket factor invalid",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
NativeHistogramBucketFactor: float64Ptr(1),
|
|
},
|
|
},
|
|
expErr: "metrics_generator.native_histogram_bucket_factor must be greater than 1",
|
|
},
|
|
{
|
|
name: "native histogram bucket factor valid",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
NativeHistogramBucketFactor: float64Ptr(1.5),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "valid cost attribution dimensions",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
CostAttribution: client.CostAttribution{
|
|
Dimensions: &map[string]string{"span.name": "op_name"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "invalid cost attribution dimensions",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
CostAttribution: client.CostAttribution{
|
|
Dimensions: &map[string]string{"span.name": "__name__"},
|
|
},
|
|
},
|
|
expErr: "cost_attribution.dimensions config has invalid label name: '__name__'",
|
|
},
|
|
{
|
|
name: "metrics_generator.span_metrics.intrinsic_dimensions valid",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
Processor: client.LimitsMetricsGeneratorProcessor{
|
|
SpanMetrics: client.LimitsMetricsGeneratorProcessorSpanMetrics{
|
|
IntrinsicDimensions: boolMapPtr(map[string]bool{
|
|
processor.DimService: true,
|
|
processor.DimSpanKind: false,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "metrics_generator.span_metrics.intrinsic_dimensions invalid",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
Processor: client.LimitsMetricsGeneratorProcessor{
|
|
SpanMetrics: client.LimitsMetricsGeneratorProcessorSpanMetrics{
|
|
IntrinsicDimensions: boolMapPtr(map[string]bool{
|
|
"not_supported": true,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expErr: fmt.Sprintf("intrinsic dimension \"%s\" is not supported, valid values: %v", "not_supported", validation.SupportedIntrinsicDimensions),
|
|
},
|
|
{
|
|
name: "metrics_generator.generate_native_histograms invalid",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
GenerateNativeHistograms: histogramMethodPtr("invalid"),
|
|
},
|
|
},
|
|
expErr: "metrics_generator.generate_native_histograms \"invalid\" is not a valid value, valid values: classic, native, both",
|
|
},
|
|
{
|
|
name: "metrics_generator.generate_native_histograms valid",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
GenerateNativeHistograms: histogramMethodPtr(histograms.HistogramMethodBoth),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "host_info.host_identifiers with values",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
Processor: client.LimitsMetricsGeneratorProcessor{
|
|
HostInfo: client.LimitsMetricGeneratorProcessorHostInfo{
|
|
HostIdentifiers: strArrPtr([]string{"host.id", "k8s.node.name"}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "host_info.metric_name valid",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
Processor: client.LimitsMetricsGeneratorProcessor{
|
|
HostInfo: client.LimitsMetricGeneratorProcessorHostInfo{
|
|
MetricName: strPtr("custom_host_info"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "host_info.metric_name invalid empty string",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
Processor: client.LimitsMetricsGeneratorProcessor{
|
|
HostInfo: client.LimitsMetricGeneratorProcessorHostInfo{
|
|
MetricName: strPtr(""),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expErr: "metric_name is invalid",
|
|
},
|
|
{
|
|
name: "dimension_mappings empty name",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
Processor: client.LimitsMetricsGeneratorProcessor{
|
|
SpanMetrics: client.LimitsMetricsGeneratorProcessorSpanMetrics{
|
|
DimensionMappings: dimensionMappingsPtr([]sharedconfig.DimensionMappings{
|
|
{
|
|
Name: "",
|
|
SourceLabel: []string{"foo"},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expErr: "dimension_mappings: name cannot be empty",
|
|
},
|
|
{
|
|
name: "dimension_mappings empty source_labels",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
Processor: client.LimitsMetricsGeneratorProcessor{
|
|
SpanMetrics: client.LimitsMetricsGeneratorProcessorSpanMetrics{
|
|
DimensionMappings: dimensionMappingsPtr([]sharedconfig.DimensionMappings{
|
|
{
|
|
Name: "my_label",
|
|
SourceLabel: []string{},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expErr: "dimension_mappings: source_labels cannot be empty for mapping with name \"my_label\"",
|
|
},
|
|
{
|
|
name: "dimension_mappings valid",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
Processor: client.LimitsMetricsGeneratorProcessor{
|
|
SpanMetrics: client.LimitsMetricsGeneratorProcessorSpanMetrics{
|
|
DimensionMappings: dimensionMappingsPtr([]sharedconfig.DimensionMappings{
|
|
{
|
|
Name: "my_label",
|
|
SourceLabel: []string{"foo", "bar"},
|
|
Join: "/",
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "service_graphs.dimensions valid",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
Processor: client.LimitsMetricsGeneratorProcessor{
|
|
ServiceGraphs: client.LimitsMetricsGeneratorProcessorServiceGraphs{
|
|
Dimensions: strArrPtr([]string{"http.method", "db.system"}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "service_graphs.dimensions no duplicate validation",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
Processor: client.LimitsMetricsGeneratorProcessor{
|
|
ServiceGraphs: client.LimitsMetricsGeneratorProcessorServiceGraphs{
|
|
Dimensions: strArrPtr([]string{"my_label", "my.label"}), // both sanitize to "my_label"
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expErr: "",
|
|
},
|
|
{
|
|
name: "span_metrics.dimensions valid",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
Processor: client.LimitsMetricsGeneratorProcessor{
|
|
SpanMetrics: client.LimitsMetricsGeneratorProcessorSpanMetrics{
|
|
Dimensions: strArrPtr([]string{"http.method", "db.system"}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "span_metrics combined dimensions, intrinsic_dimensions, and dimension_mappings valid",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
Processor: client.LimitsMetricsGeneratorProcessor{
|
|
SpanMetrics: client.LimitsMetricsGeneratorProcessorSpanMetrics{
|
|
Dimensions: strArrPtr([]string{"http.method"}),
|
|
IntrinsicDimensions: boolMapPtr(map[string]bool{
|
|
processor.DimService: true,
|
|
}),
|
|
DimensionMappings: dimensionMappingsPtr([]sharedconfig.DimensionMappings{
|
|
{
|
|
Name: "combined",
|
|
SourceLabel: []string{"foo", "bar"},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "span_metrics dimension named 'service' with intrinsic service enabled is valid (prefixed with __)",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
Processor: client.LimitsMetricsGeneratorProcessor{
|
|
SpanMetrics: client.LimitsMetricsGeneratorProcessorSpanMetrics{
|
|
Dimensions: strArrPtr([]string{"service"}), // becomes __service to avoid collision
|
|
IntrinsicDimensions: boolMapPtr(map[string]bool{
|
|
processor.DimService: true,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "span_metrics no duplicate validation",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
Processor: client.LimitsMetricsGeneratorProcessor{
|
|
SpanMetrics: client.LimitsMetricsGeneratorProcessorSpanMetrics{
|
|
Dimensions: strArrPtr([]string{"my_label", "my.label"}), // both sanitize to "my_label"
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expErr: "",
|
|
},
|
|
{
|
|
name: "span_metrics dimension_mapping collision with dimension",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
Processor: client.LimitsMetricsGeneratorProcessor{
|
|
SpanMetrics: client.LimitsMetricsGeneratorProcessorSpanMetrics{
|
|
Dimensions: strArrPtr([]string{"my_label"}),
|
|
DimensionMappings: dimensionMappingsPtr([]sharedconfig.DimensionMappings{
|
|
{
|
|
Name: "my_label", // collides with dimension
|
|
SourceLabel: []string{"foo"},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expErr: `dimension_mapping "my_label" produces label "my_label" which collides with dimension "my_label"`,
|
|
},
|
|
{
|
|
name: "span_metrics dimension_mapping collision with another dimension_mapping",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
Processor: client.LimitsMetricsGeneratorProcessor{
|
|
SpanMetrics: client.LimitsMetricsGeneratorProcessorSpanMetrics{
|
|
DimensionMappings: dimensionMappingsPtr([]sharedconfig.DimensionMappings{
|
|
{
|
|
Name: "combined",
|
|
SourceLabel: []string{"foo"},
|
|
},
|
|
{
|
|
Name: "combined", // collides with first mapping
|
|
SourceLabel: []string{"bar"},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expErr: `dimension_mapping "combined" produces label "combined" which collides with dimension_mapping "combined"`,
|
|
},
|
|
{
|
|
name: "metrics_generator.span_name_sanitization empty (disabled)",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
SpanNameSanitization: strPtr(""),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "metrics_generator.span_name_sanitization dry_run",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
SpanNameSanitization: strPtr("dry_run"),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "metrics_generator.span_name_sanitization enabled",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
SpanNameSanitization: strPtr("enabled"),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "metrics_generator.span_name_sanitization invalid",
|
|
cfg: Config{},
|
|
limits: client.Limits{
|
|
MetricsGenerator: client.LimitsMetricsGenerator{
|
|
SpanNameSanitization: strPtr("invalid"),
|
|
},
|
|
},
|
|
expErr: "span_name_sanitization \"invalid\" is not valid, valid values: [ dry_run enabled]",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
validator := newOverridesValidator(&tc.cfg)
|
|
|
|
err := validator.Validate(&tc.limits)
|
|
if tc.expErr != "" {
|
|
assert.EqualError(t, err, tc.expErr)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|