github.com/grafana/pyroscope@v1.18.0/pkg/validation/relabeling_test.go (about)

     1  package validation
     2  
     3  import (
     4  	"bytes"
     5  	"flag"
     6  	"testing"
     7  
     8  	"github.com/prometheus/prometheus/model/labels"
     9  	"github.com/prometheus/prometheus/model/relabel"
    10  	"github.com/stretchr/testify/require"
    11  
    12  	phlaremodel "github.com/grafana/pyroscope/pkg/model"
    13  )
    14  
    15  type wrappedRuntimeConfig struct {
    16  	rc *RuntimeConfigValues
    17  }
    18  
    19  func (w *wrappedRuntimeConfig) TenantLimits(tenantID string) *Limits {
    20  	return w.rc.TenantLimits[tenantID]
    21  }
    22  
    23  func (w *wrappedRuntimeConfig) AllByTenantID() map[string]*Limits {
    24  	return w.rc.TenantLimits
    25  }
    26  
    27  func (w *wrappedRuntimeConfig) RuntimeConfig() *RuntimeConfigValues {
    28  	return w.rc
    29  }
    30  
    31  func newOverrides(rc *RuntimeConfigValues) (*Overrides, error) {
    32  	var defaultCfg Limits
    33  	fs := flag.NewFlagSet("test", flag.PanicOnError)
    34  	defaultCfg.RegisterFlags(fs)
    35  	return NewOverrides(defaultCfg, &wrappedRuntimeConfig{rc})
    36  }
    37  
    38  const tenantOverrideConfig = `
    39  overrides:
    40    nothing: {}
    41    disabled:
    42      ingestion_relabeling_default_rules_position: disabled
    43    custom-rule-end:
    44      ingestion_relabeling_rules:
    45        - action: drop
    46    custom-rule-start:
    47      ingestion_relabeling_default_rules_position: last
    48      ingestion_relabeling_rules:
    49        - action: drop
    50    custom-rule-only:
    51      ingestion_relabeling_default_rules_position: disabled
    52      ingestion_relabeling_rules:
    53        - action: drop
    54    
    55  `
    56  
    57  func Test_IngestionRelabelingRules(t *testing.T) {
    58  	rc, err := LoadRuntimeConfig(bytes.NewReader([]byte(tenantOverrideConfig)))
    59  	require.NoError(t, err)
    60  
    61  	o, err := newOverrides(rc)
    62  	require.NoError(t, err)
    63  
    64  	rules := o.IngestionRelabelingRules("xxxx")
    65  	require.Equal(t, len(defaultRelabelRules), len(rules))
    66  
    67  	rules = o.IngestionRelabelingRules("nothing")
    68  	require.Equal(t, len(defaultRelabelRules), len(rules))
    69  
    70  	rules = o.IngestionRelabelingRules("disabled")
    71  	require.Equal(t, 0, len(rules))
    72  
    73  	rules = o.IngestionRelabelingRules("custom-rule-end")
    74  	require.Equal(t, len(defaultRelabelRules)+1, len(rules))
    75  	require.Equal(t, relabel.Drop, rules[len(defaultRelabelRules)].Action)
    76  
    77  	rules = o.IngestionRelabelingRules("custom-rule-start")
    78  	require.Equal(t, len(defaultRelabelRules)+1, len(rules))
    79  	require.Equal(t, relabel.Drop, rules[0].Action)
    80  
    81  	rules = o.IngestionRelabelingRules("custom-rule-only")
    82  	require.Equal(t, 1, len(rules))
    83  	require.Equal(t, relabel.Drop, rules[0].Action)
    84  
    85  	_, err = LoadRuntimeConfig(bytes.NewReader([]byte(`
    86  overrides:
    87    wrong-mode:
    88      ingestion_relabeling_default_rules_position: end
    89    `)))
    90  	require.ErrorContains(t, err, "invalid ingestion_relabeling_default_rules_position: end")
    91  
    92  	_, err = LoadRuntimeConfig(bytes.NewReader([]byte(`
    93  overrides:
    94    wrong-rule-action:
    95      ingestion_relabeling_rules: [{action: refund}]
    96    `)))
    97  	require.ErrorContains(t, err, "unknown relabel action \"refund\"")
    98  
    99  	_, err = LoadRuntimeConfig(bytes.NewReader([]byte(`
   100  overrides:
   101    empty-rule:
   102      ingestion_relabeling_rules: [{}]
   103    `)))
   104  	require.ErrorContains(t, err, "relabel configuration for replace action requires 'target_label'")
   105  
   106  }
   107  
   108  func Test_SampleTypeRelabelRules_Set(t *testing.T) {
   109  	tests := []struct {
   110  		name        string
   111  		config      string
   112  		wantErr     bool
   113  		errContains string
   114  	}{
   115  		{
   116  			name:    "valid drop action",
   117  			config:  `[{"action": "drop", "source_labels": ["__type__"], "regex": "alloc_.*"}]`,
   118  			wantErr: false,
   119  		},
   120  		{
   121  			name:    "valid keep action",
   122  			config:  `[{"action": "keep", "source_labels": ["__type__"], "regex": "cpu|wall"}]`,
   123  			wantErr: false,
   124  		},
   125  		{
   126  			name:        "invalid replace action",
   127  			config:      `[{"action": "replace", "source_labels": ["__type__"], "target_label": "new_type", "replacement": "cpu"}]`,
   128  			wantErr:     true,
   129  			errContains: "sample type relabeling only supports 'drop' and 'keep' actions, got 'replace'",
   130  		},
   131  		{
   132  			name:        "invalid labeldrop action",
   133  			config:      `[{"action": "labeldrop", "regex": "temp_.*"}]`,
   134  			wantErr:     true,
   135  			errContains: "sample type relabeling only supports 'drop' and 'keep' actions, got 'labeldrop'",
   136  		},
   137  		{
   138  			name:        "multiple rules with one invalid",
   139  			config:      `[{"action": "keep", "source_labels": ["__type__"], "regex": "cpu"}, {"action": "replace", "source_labels": ["__type__"], "target_label": "new_type", "replacement": "cpu"}]`,
   140  			wantErr:     true,
   141  			errContains: "rule at pos 1: sample type relabeling only supports 'drop' and 'keep' actions, got 'replace'",
   142  		},
   143  	}
   144  
   145  	for _, tt := range tests {
   146  		t.Run(tt.name, func(t *testing.T) {
   147  			var rules SampleTypeRelabelRules
   148  			err := rules.Set(tt.config)
   149  			if tt.wantErr {
   150  				require.Error(t, err)
   151  				require.Contains(t, err.Error(), tt.errContains)
   152  			} else {
   153  				require.NoError(t, err)
   154  			}
   155  		})
   156  	}
   157  }
   158  
   159  func Test_defaultRelabelRules(t *testing.T) {
   160  	for _, tc := range []struct {
   161  		name     string
   162  		input    labels.Labels
   163  		expected labels.Labels
   164  		kept     bool
   165  	}{
   166  		{
   167  			name:     "let empty through",
   168  			input:    labels.Labels{},
   169  			expected: labels.Labels{},
   170  			kept:     true,
   171  		},
   172  		{
   173  			name: "godelta prof remove prefix",
   174  			input: labels.FromStrings(
   175  				phlaremodel.LabelNameProfileName, "godeltaprof_memory", // TODO: Verify is this is really the prefix used
   176  			),
   177  			expected: labels.FromStrings(
   178  				phlaremodel.LabelNameProfileName, "memory",
   179  				"__name_replaced__", "godeltaprof_memory",
   180  				"__delta__", "false",
   181  			),
   182  			kept: true,
   183  		},
   184  		{
   185  			name: "replace wall name with cpu type",
   186  			input: labels.FromStrings(
   187  				phlaremodel.LabelNameProfileName, "wall",
   188  				phlaremodel.LabelNameType, "cpu",
   189  			),
   190  			expected: labels.FromStrings(
   191  				phlaremodel.LabelNameProfileName, "process_cpu",
   192  				"__name_replaced__", "wall",
   193  				phlaremodel.LabelNameType, "cpu",
   194  			),
   195  			kept: true,
   196  		},
   197  	} {
   198  		result, kept := relabel.Process(tc.input, defaultRelabelRules...)
   199  		require.Equal(t, tc.expected, result)
   200  		require.Equal(t, tc.kept, kept)
   201  	}
   202  
   203  }