|
|
|
@ -5,6 +5,7 @@ import ( |
|
|
|
|
"errors" |
|
|
|
|
"io" |
|
|
|
|
"net/http" |
|
|
|
|
"strconv" |
|
|
|
|
"testing" |
|
|
|
|
"time" |
|
|
|
|
|
|
|
|
@ -19,6 +20,8 @@ import ( |
|
|
|
|
"github.com/stretchr/testify/mock" |
|
|
|
|
"github.com/stretchr/testify/require" |
|
|
|
|
|
|
|
|
|
util_log "github.com/grafana/loki/v3/pkg/util/log" |
|
|
|
|
|
|
|
|
|
"github.com/grafana/loki/v3/pkg/compactor/deletion" |
|
|
|
|
"github.com/grafana/loki/v3/pkg/ingester/client" |
|
|
|
|
"github.com/grafana/loki/v3/pkg/logproto" |
|
|
|
@ -1148,6 +1151,13 @@ func setupIngesterQuerierMocks(conf Config, limits *validation.Overrides) (*quer |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
}, nil) |
|
|
|
|
ingesterClient.On("GetDetectedLabels", mock.Anything, mock.Anything, mock.Anything).Return(&logproto.DetectedLabelsResponse{ |
|
|
|
|
DetectedLabels: []*logproto.DetectedLabel{ |
|
|
|
|
{Label: "pod", Cardinality: 1}, |
|
|
|
|
{Label: "namespace", Cardinality: 3}, |
|
|
|
|
{Label: "customerId", Cardinality: 200}, |
|
|
|
|
}, |
|
|
|
|
}, nil) |
|
|
|
|
|
|
|
|
|
store := newStoreMock() |
|
|
|
|
store.On("SelectLogs", mock.Anything, mock.Anything).Return(mockStreamIterator(0, 1), nil) |
|
|
|
@ -1351,7 +1361,7 @@ func TestQuerier_SelectSamplesWithDeletes(t *testing.T) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func newQuerier(cfg Config, clientCfg client.Config, clientFactory ring_client.PoolFactory, ring ring.ReadRing, dg *mockDeleteGettter, store storage.Store, limits *validation.Overrides) (*SingleTenantQuerier, error) { |
|
|
|
|
iq, err := newIngesterQuerier(clientCfg, ring, cfg.ExtraQueryDelay, clientFactory, constants.Loki) |
|
|
|
|
iq, err := newIngesterQuerier(clientCfg, ring, cfg.ExtraQueryDelay, clientFactory, constants.Loki, util_log.Logger) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, err |
|
|
|
|
} |
|
|
|
@ -1373,44 +1383,306 @@ func TestQuerier_isLabelRelevant(t *testing.T) { |
|
|
|
|
for _, tc := range []struct { |
|
|
|
|
name string |
|
|
|
|
label string |
|
|
|
|
values *logproto.UniqueLabelValues |
|
|
|
|
values []string |
|
|
|
|
expected bool |
|
|
|
|
}{ |
|
|
|
|
{ |
|
|
|
|
label: "uuidv4 values are not relevant", |
|
|
|
|
values: &logproto.UniqueLabelValues{Values: []string{"751e8ee6-b377-4b2e-b7b5-5508fbe980ef", "6b7e2663-8ecb-42e1-8bdc-0c5de70185b3", "2e1e67ff-be4f-47b8-aee1-5d67ff1ddabf", "c95b2d62-74ed-4ed7-a8a1-eb72fc67946e"}}, |
|
|
|
|
values: []string{"751e8ee6-b377-4b2e-b7b5-5508fbe980ef", "6b7e2663-8ecb-42e1-8bdc-0c5de70185b3", "2e1e67ff-be4f-47b8-aee1-5d67ff1ddabf", "c95b2d62-74ed-4ed7-a8a1-eb72fc67946e"}, |
|
|
|
|
expected: false, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
label: "guid values are not relevant", |
|
|
|
|
values: &logproto.UniqueLabelValues{Values: []string{"57808f62-f117-4a22-84a0-bc3282c7f106", "5076e837-cd8d-4dd7-95ff-fecb087dccf6", "2e2a6554-1744-4399-b89a-88ae79c27096", "d3c31248-ec0c-4bc4-b11c-8fb1cfb42e62"}}, |
|
|
|
|
values: []string{"57808f62-f117-4a22-84a0-bc3282c7f106", "5076e837-cd8d-4dd7-95ff-fecb087dccf6", "2e2a6554-1744-4399-b89a-88ae79c27096", "d3c31248-ec0c-4bc4-b11c-8fb1cfb42e62"}, |
|
|
|
|
expected: false, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
label: "integer values are not relevant", |
|
|
|
|
values: &logproto.UniqueLabelValues{Values: []string{"1", "2", "3", "4"}}, |
|
|
|
|
values: []string{"1", "2", "3", "4"}, |
|
|
|
|
expected: false, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
label: "string values are relevant", |
|
|
|
|
values: &logproto.UniqueLabelValues{Values: []string{"ingester", "querier", "query-frontend", "index-gateway"}}, |
|
|
|
|
values: []string{"ingester", "querier", "query-frontend", "index-gateway"}, |
|
|
|
|
expected: true, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
label: "guid with braces are not relevant", |
|
|
|
|
values: &logproto.UniqueLabelValues{Values: []string{"{E9550CF7-58D9-48B9-8845-D9800C651AAC}", "{1617921B-1749-4FF0-A058-31AFB5D98149}", "{C119D92E-A4B9-48A3-A92C-6CA8AA8A6CCC}", "{228AAF1D-2DE7-4909-A4E9-246A7FA9D988}"}}, |
|
|
|
|
values: []string{"{E9550CF7-58D9-48B9-8845-D9800C651AAC}", "{1617921B-1749-4FF0-A058-31AFB5D98149}", "{C119D92E-A4B9-48A3-A92C-6CA8AA8A6CCC}", "{228AAF1D-2DE7-4909-A4E9-246A7FA9D988}"}, |
|
|
|
|
expected: false, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
label: "float values are not relevant", |
|
|
|
|
values: &logproto.UniqueLabelValues{Values: []string{"1.2", "2.5", "3.3", "4.1"}}, |
|
|
|
|
values: []string{"1.2", "2.5", "3.3", "4.1"}, |
|
|
|
|
expected: false, |
|
|
|
|
}, |
|
|
|
|
} { |
|
|
|
|
t.Run(tc.name, func(t *testing.T) { |
|
|
|
|
querier := &SingleTenantQuerier{cfg: mockQuerierConfig()} |
|
|
|
|
assert.Equal(t, tc.expected, querier.isLabelRelevant(tc.label, tc.values)) |
|
|
|
|
assert.Equal(t, tc.expected, querier.isLabelRelevant(tc.label, tc.values, map[string]struct{}{"host": {}, "cluster": {}, "namespace": {}, "instance": {}, "pod": {}})) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func TestQuerier_DetectedLabels(t *testing.T) { |
|
|
|
|
manyValues := []string{} |
|
|
|
|
now := time.Now() |
|
|
|
|
for i := 0; i < 60; i++ { |
|
|
|
|
manyValues = append(manyValues, "a"+strconv.Itoa(i)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
limits, err := validation.NewOverrides(defaultLimitsTestConfig(), nil) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
ctx := user.InjectOrgID(context.Background(), "test") |
|
|
|
|
|
|
|
|
|
conf := mockQuerierConfig() |
|
|
|
|
conf.IngesterQueryStoreMaxLookback = 0 |
|
|
|
|
|
|
|
|
|
request := logproto.DetectedLabelsRequest{ |
|
|
|
|
Start: &now, |
|
|
|
|
End: &now, |
|
|
|
|
Query: "", |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
t.Run("when both store and ingester responses are present, a combined response is returned", func(t *testing.T) { |
|
|
|
|
ingesterResponse := logproto.LabelToValuesResponse{Labels: map[string]*logproto.UniqueLabelValues{ |
|
|
|
|
"cluster": {Values: []string{"ingester"}}, |
|
|
|
|
"ingesterLabel": {Values: []string{"abc", "def", "ghi", "abc"}}, |
|
|
|
|
}} |
|
|
|
|
|
|
|
|
|
ingesterClient := newQuerierClientMock() |
|
|
|
|
storeClient := newStoreMock() |
|
|
|
|
|
|
|
|
|
ingesterClient.On("GetDetectedLabels", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). |
|
|
|
|
Return(&ingesterResponse, nil) |
|
|
|
|
storeClient.On("LabelNamesForMetricName", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). |
|
|
|
|
Return([]string{"storeLabel"}, nil). |
|
|
|
|
On("LabelValuesForMetricName", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, "storeLabel", mock.Anything). |
|
|
|
|
Return([]string{"val1", "val2"}, nil) |
|
|
|
|
|
|
|
|
|
querier, err := newQuerier( |
|
|
|
|
conf, |
|
|
|
|
mockIngesterClientConfig(), |
|
|
|
|
newIngesterClientMockFactory(ingesterClient), |
|
|
|
|
mockReadRingWithOneActiveIngester(), |
|
|
|
|
&mockDeleteGettter{}, |
|
|
|
|
storeClient, limits) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
resp, err := querier.DetectedLabels(ctx, &request) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
calls := ingesterClient.GetMockedCallsByMethod("GetDetectedLabels") |
|
|
|
|
assert.Equal(t, 1, len(calls)) |
|
|
|
|
|
|
|
|
|
detectedLabels := resp.DetectedLabels |
|
|
|
|
assert.Len(t, detectedLabels, 3) |
|
|
|
|
assert.Contains(t, detectedLabels, &logproto.DetectedLabel{Label: "storeLabel", Cardinality: 2}) |
|
|
|
|
assert.Contains(t, detectedLabels, &logproto.DetectedLabel{Label: "ingesterLabel", Cardinality: 3}) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
t.Run("when both store and ingester responses are present, duplicates are removed", func(t *testing.T) { |
|
|
|
|
ingesterResponse := logproto.LabelToValuesResponse{Labels: map[string]*logproto.UniqueLabelValues{ |
|
|
|
|
"cluster": {Values: []string{"ingester"}}, |
|
|
|
|
"ingesterLabel": {Values: []string{"abc", "def", "ghi", "abc"}}, |
|
|
|
|
"commonLabel": {Values: []string{"abc", "def", "ghi", "abc"}}, |
|
|
|
|
}} |
|
|
|
|
|
|
|
|
|
ingesterClient := newQuerierClientMock() |
|
|
|
|
storeClient := newStoreMock() |
|
|
|
|
|
|
|
|
|
ingesterClient.On("GetDetectedLabels", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). |
|
|
|
|
Return(&ingesterResponse, nil) |
|
|
|
|
storeClient.On("LabelNamesForMetricName", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). |
|
|
|
|
Return([]string{"storeLabel", "commonLabel"}, nil). |
|
|
|
|
On("LabelValuesForMetricName", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, "storeLabel", mock.Anything). |
|
|
|
|
Return([]string{"val1", "val2"}, nil). |
|
|
|
|
On("LabelValuesForMetricName", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, "commonLabel", mock.Anything). |
|
|
|
|
Return([]string{"def", "xyz", "lmo", "abc"}, nil) |
|
|
|
|
|
|
|
|
|
querier, err := newQuerier( |
|
|
|
|
conf, |
|
|
|
|
mockIngesterClientConfig(), |
|
|
|
|
newIngesterClientMockFactory(ingesterClient), |
|
|
|
|
mockReadRingWithOneActiveIngester(), |
|
|
|
|
&mockDeleteGettter{}, |
|
|
|
|
storeClient, limits) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
resp, err := querier.DetectedLabels(ctx, &request) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
calls := ingesterClient.GetMockedCallsByMethod("GetDetectedLabels") |
|
|
|
|
assert.Equal(t, 1, len(calls)) |
|
|
|
|
|
|
|
|
|
detectedLabels := resp.DetectedLabels |
|
|
|
|
assert.Len(t, detectedLabels, 4) |
|
|
|
|
assert.Contains(t, detectedLabels, &logproto.DetectedLabel{Label: "storeLabel", Cardinality: 2}) |
|
|
|
|
assert.Contains(t, detectedLabels, &logproto.DetectedLabel{Label: "ingesterLabel", Cardinality: 3}) |
|
|
|
|
assert.Contains(t, detectedLabels, &logproto.DetectedLabel{Label: "commonLabel", Cardinality: 5}) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
t.Run("returns a response when ingester data is empty", func(t *testing.T) { |
|
|
|
|
ingesterClient := newQuerierClientMock() |
|
|
|
|
storeClient := newStoreMock() |
|
|
|
|
|
|
|
|
|
ingesterClient.On("GetDetectedLabels", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). |
|
|
|
|
Return(&logproto.LabelToValuesResponse{}, nil) |
|
|
|
|
storeClient.On("LabelNamesForMetricName", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). |
|
|
|
|
Return([]string{"storeLabel1", "storeLabel2"}, nil). |
|
|
|
|
On("LabelValuesForMetricName", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, "storeLabel1", mock.Anything). |
|
|
|
|
Return([]string{"val1", "val2"}, nil). |
|
|
|
|
On("LabelValuesForMetricName", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, "storeLabel2", mock.Anything). |
|
|
|
|
Return([]string{"val1", "val2"}, nil) |
|
|
|
|
|
|
|
|
|
querier, err := newQuerier( |
|
|
|
|
conf, |
|
|
|
|
mockIngesterClientConfig(), |
|
|
|
|
newIngesterClientMockFactory(ingesterClient), |
|
|
|
|
mockReadRingWithOneActiveIngester(), |
|
|
|
|
&mockDeleteGettter{}, |
|
|
|
|
storeClient, limits) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
resp, err := querier.DetectedLabels(ctx, &request) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
detectedLabels := resp.DetectedLabels |
|
|
|
|
assert.Len(t, detectedLabels, 2) |
|
|
|
|
assert.Contains(t, detectedLabels, &logproto.DetectedLabel{Label: "storeLabel1", Cardinality: 2}) |
|
|
|
|
assert.Contains(t, detectedLabels, &logproto.DetectedLabel{Label: "storeLabel2", Cardinality: 2}) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
t.Run("returns a response when store data is empty", func(t *testing.T) { |
|
|
|
|
ingesterResponse := logproto.LabelToValuesResponse{Labels: map[string]*logproto.UniqueLabelValues{ |
|
|
|
|
"cluster": {Values: []string{"ingester"}}, |
|
|
|
|
"ingesterLabel": {Values: []string{"abc", "def", "ghi", "abc"}}, |
|
|
|
|
}} |
|
|
|
|
|
|
|
|
|
ingesterClient := newQuerierClientMock() |
|
|
|
|
storeClient := newStoreMock() |
|
|
|
|
|
|
|
|
|
ingesterClient.On("GetDetectedLabels", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). |
|
|
|
|
Return(&ingesterResponse, nil) |
|
|
|
|
storeClient.On("LabelNamesForMetricName", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). |
|
|
|
|
Return([]string{}, nil) |
|
|
|
|
|
|
|
|
|
querier, err := newQuerier( |
|
|
|
|
conf, |
|
|
|
|
mockIngesterClientConfig(), |
|
|
|
|
newIngesterClientMockFactory(ingesterClient), |
|
|
|
|
mockReadRingWithOneActiveIngester(), |
|
|
|
|
&mockDeleteGettter{}, |
|
|
|
|
storeClient, limits) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
resp, err := querier.DetectedLabels(ctx, &request) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
detectedLabels := resp.DetectedLabels |
|
|
|
|
assert.Len(t, detectedLabels, 2) |
|
|
|
|
assert.Contains(t, detectedLabels, &logproto.DetectedLabel{Label: "cluster", Cardinality: 1}) |
|
|
|
|
assert.Contains(t, detectedLabels, &logproto.DetectedLabel{Label: "ingesterLabel", Cardinality: 3}) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
t.Run("id types like uuids, guids and numbers are not relevant detected labels", func(t *testing.T) { |
|
|
|
|
ingesterResponse := logproto.LabelToValuesResponse{Labels: map[string]*logproto.UniqueLabelValues{ |
|
|
|
|
"all-ints": {Values: []string{"1", "2", "3", "4"}}, |
|
|
|
|
"all-floats": {Values: []string{"1.2", "2.3", "3.4", "4.5"}}, |
|
|
|
|
"all-uuids": {Values: []string{"751e8ee6-b377-4b2e-b7b5-5508fbe980ef", "6b7e2663-8ecb-42e1-8bdc-0c5de70185b3", "2e1e67ff-be4f-47b8-aee1-5d67ff1ddabf", "c95b2d62-74ed-4ed7-a8a1-eb72fc67946e"}}, |
|
|
|
|
}} |
|
|
|
|
|
|
|
|
|
ingesterClient := newQuerierClientMock() |
|
|
|
|
storeClient := newStoreMock() |
|
|
|
|
|
|
|
|
|
ingesterClient.On("GetDetectedLabels", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). |
|
|
|
|
Return(&ingesterResponse, nil) |
|
|
|
|
storeClient.On("LabelNamesForMetricName", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). |
|
|
|
|
Return([]string{}, nil) |
|
|
|
|
|
|
|
|
|
querier, err := newQuerier( |
|
|
|
|
conf, |
|
|
|
|
mockIngesterClientConfig(), |
|
|
|
|
newIngesterClientMockFactory(ingesterClient), |
|
|
|
|
mockReadRingWithOneActiveIngester(), |
|
|
|
|
&mockDeleteGettter{}, |
|
|
|
|
storeClient, limits) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
resp, err := querier.DetectedLabels(ctx, &request) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
detectedLabels := resp.DetectedLabels |
|
|
|
|
assert.Len(t, detectedLabels, 0) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
t.Run("labels with more than required cardinality are not relevant", func(t *testing.T) { |
|
|
|
|
ingesterResponse := logproto.LabelToValuesResponse{Labels: map[string]*logproto.UniqueLabelValues{ |
|
|
|
|
"less-than-m-values": {Values: []string{"val1"}}, |
|
|
|
|
"more-than-n-values": {Values: manyValues}, |
|
|
|
|
}} |
|
|
|
|
|
|
|
|
|
ingesterClient := newQuerierClientMock() |
|
|
|
|
storeClient := newStoreMock() |
|
|
|
|
|
|
|
|
|
ingesterClient.On("GetDetectedLabels", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). |
|
|
|
|
Return(&ingesterResponse, nil) |
|
|
|
|
storeClient.On("LabelNamesForMetricName", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). |
|
|
|
|
Return([]string{}, nil) |
|
|
|
|
|
|
|
|
|
querier, err := newQuerier( |
|
|
|
|
conf, |
|
|
|
|
mockIngesterClientConfig(), |
|
|
|
|
newIngesterClientMockFactory(ingesterClient), |
|
|
|
|
mockReadRingWithOneActiveIngester(), |
|
|
|
|
&mockDeleteGettter{}, |
|
|
|
|
storeClient, limits) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
resp, err := querier.DetectedLabels(ctx, &request) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
detectedLabels := resp.DetectedLabels |
|
|
|
|
assert.Len(t, detectedLabels, 0) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
t.Run("static labels are always returned no matter their cardinality or value types", func(t *testing.T) { |
|
|
|
|
ingesterResponse := logproto.LabelToValuesResponse{Labels: map[string]*logproto.UniqueLabelValues{ |
|
|
|
|
"cluster": {Values: []string{"val1"}}, |
|
|
|
|
"namespace": {Values: manyValues}, |
|
|
|
|
"pod": {Values: []string{"1", "2", "3", "4"}}, |
|
|
|
|
}} |
|
|
|
|
|
|
|
|
|
ingesterClient := newQuerierClientMock() |
|
|
|
|
storeClient := newStoreMock() |
|
|
|
|
|
|
|
|
|
ingesterClient.On("GetDetectedLabels", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). |
|
|
|
|
Return(&ingesterResponse, nil) |
|
|
|
|
storeClient.On("LabelNamesForMetricName", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). |
|
|
|
|
Return([]string{}, nil) |
|
|
|
|
request := logproto.DetectedLabelsRequest{ |
|
|
|
|
Start: &now, |
|
|
|
|
End: &now, |
|
|
|
|
Query: "", |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
querier, err := newQuerier( |
|
|
|
|
conf, |
|
|
|
|
mockIngesterClientConfig(), |
|
|
|
|
newIngesterClientMockFactory(ingesterClient), |
|
|
|
|
mockReadRingWithOneActiveIngester(), |
|
|
|
|
&mockDeleteGettter{}, |
|
|
|
|
storeClient, limits) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
resp, err := querier.DetectedLabels(ctx, &request) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
detectedLabels := resp.DetectedLabels |
|
|
|
|
assert.Len(t, detectedLabels, 3) |
|
|
|
|
assert.Contains(t, detectedLabels, &logproto.DetectedLabel{Label: "cluster", Cardinality: 1}) |
|
|
|
|
assert.Contains(t, detectedLabels, &logproto.DetectedLabel{Label: "pod", Cardinality: 4}) |
|
|
|
|
assert.Contains(t, detectedLabels, &logproto.DetectedLabel{Label: "namespace", Cardinality: 60}) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|