Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 56 additions & 2 deletions web/src/__tests__/attribute-filters.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
filtersFromQuery,
getContentPipelineStage,
getMatcherFromSet,
getK8sMatcherFromSet,
getMatchersFromFilters,
getNamespaceMatcher,
getSeverityFilterPipelineStage,
Expand Down Expand Up @@ -140,7 +140,7 @@ describe('Attribute filters', () => {
expected: { label: 'test', operator: '=~', value: '"value-1|value-2"' },
},
].forEach(({ set, label, expected }) => {
const selectors = getMatcherFromSet(label, set);
const selectors = getK8sMatcherFromSet(label, set);
expect(selectors).toEqual(expected);
});
});
Expand Down Expand Up @@ -363,6 +363,60 @@ describe('Attribute filters', () => {

test('query from filters', () => {
[
{
initialQuery: '{ kubernetes_pod_name=~"a-pod|b-pod" }',
// If a regex is provided as a value, the operator must be =~
filters: {
pod: new Set(['.*etcd.*']),
},
expectedQuery: '{ kubernetes_pod_name=~".*etcd.*" }',
},
{
initialQuery: '{ kubernetes_pod_name=~"a-pod|b-pod" }',
// Single dots are not considered regex as they are valid DNS names:
// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
filters: {
pod: new Set(['openshift.pod']),
},
expectedQuery: '{ kubernetes_pod_name="openshift.pod" }',
},
{
initialQuery: '{ kubernetes_pod_name=~"a-pod|b-pod" }',
// A regex including a dot must have a quantifier (* or + or ?)
// as regex match in LogQL is fully anchored
// https://grafana.com/docs/loki/latest/query/log_queries/#log-stream-selector
filters: {
pod: new Set(['openshift.*pod']),
},
expectedQuery: '{ kubernetes_pod_name=~"openshift.*pod" }',
},
{
initialQuery: '{ kubernetes_pod_name=~"a-pod|b-pod" }',
// A regex including a dot must have a quantifier (* or + or ?)
// as regex match in LogQL is fully anchored
// https://grafana.com/docs/loki/latest/query/log_queries/#log-stream-selector
filters: {
pod: new Set(['openshift.+pod']),
},
expectedQuery: '{ kubernetes_pod_name=~"openshift.+pod" }',
},
{
initialQuery: '{ kubernetes_pod_name=~"a-pod|b-pod" }',
// A regex including a dot must have a quantifier (* or + or ?)
// as regex match in LogQL is fully anchored
// https://grafana.com/docs/loki/latest/query/log_queries/#log-stream-selector
filters: {
pod: new Set(['openshift.?pod']),
},
expectedQuery: '{ kubernetes_pod_name=~"openshift.?pod" }',
},
{
initialQuery: '{ kubernetes_pod_name=~"a-pod|b-pod" }',
filters: {
pod: new Set(['ns-1']),
},
expectedQuery: '{ kubernetes_pod_name="ns-1" }',
},
{
initialQuery:
'{ kubernetes_pod_name=~"a-pod|b-pod", kubernetes_namespace_name=~"ns-1|ns-2", label="test", kubernetes_container_name="container-1" } |="some line content" | other="filter" | level="err|eror" or level="unknown" or level=""',
Expand Down
23 changes: 17 additions & 6 deletions web/src/attribute-filters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,12 @@ export const getNamespaceMatcher = (namespace?: string): LabelMatcher | undefine
};
};

const isK8sValueARegex = (value: string) => {
// Regex to test if a string contains a regex matching a k8s name value
const testRegex = /[{}()[\]^$*+?|/\\]|\.[*+?]/;
return testRegex.test(value);
};

export const queryWithNamespace = ({ query, namespace }: { query: string; namespace?: string }) => {
const logQLQuery = new LogQLQuery(query ?? '');

Expand All @@ -281,17 +287,22 @@ export const queryWithNamespace = ({ query, namespace }: { query: string; namesp
return logQLQuery.toString();
};

export const getMatcherFromSet = (label: string, values: Set<string>): LabelMatcher | undefined => {
export const getK8sMatcherFromSet = (
label: string,
values: Set<string>,
): LabelMatcher | undefined => {
const valuesArray = Array.from(values);
if (valuesArray.length === 0) {
return undefined;
}

if (valuesArray.length === 1) {
const value = valuesArray[0];

return {
label,
operator: '=',
value: `"${valuesArray[0]}"`,
operator: isK8sValueARegex(value) ? '=~' : '=',
value: `"${value}"`,
};
}

Expand All @@ -314,13 +325,13 @@ export const getMatchersFromFilters = (filters?: Filters): Array<LabelMatcher> =
if (value) {
switch (key) {
case 'namespace':
matchers.push(getMatcherFromSet('kubernetes_namespace_name', value));
matchers.push(getK8sMatcherFromSet('kubernetes_namespace_name', value));
break;
case 'pod':
matchers.push(getMatcherFromSet('kubernetes_pod_name', value));
matchers.push(getK8sMatcherFromSet('kubernetes_pod_name', value));
break;
case 'container':
matchers.push(getMatcherFromSet('kubernetes_container_name', value));
matchers.push(getK8sMatcherFromSet('kubernetes_container_name', value));
break;
}
}
Expand Down