Loki: Add scope filters to queries on the frontend (#104638)
Actionlint / Lint GitHub Actions files (push) Waiting to run Details
Backend Code Checks / Validate Backend Configs (push) Waiting to run Details
Backend Unit Tests / Detect whether code changed (push) Waiting to run Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (1/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (2/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (3/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (4/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (5/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (6/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (7/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (8/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (1/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (2/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (3/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (4/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (5/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (6/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (7/8) (push) Blocked by required conditions Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (8/8) (push) Blocked by required conditions Details
Backend Unit Tests / All backend unit tests complete (push) Blocked by required conditions Details
CodeQL checks / Analyze (actions) (push) Waiting to run Details
CodeQL checks / Analyze (go) (push) Waiting to run Details
CodeQL checks / Analyze (javascript) (push) Waiting to run Details
Lint Frontend / Detect whether code changed (push) Waiting to run Details
Lint Frontend / Lint (push) Blocked by required conditions Details
Lint Frontend / Typecheck (push) Blocked by required conditions Details
Lint Frontend / Betterer (push) Blocked by required conditions Details
golangci-lint / lint-go (push) Waiting to run Details
Verify i18n / verify-i18n (push) Waiting to run Details
End-to-end tests / Detect whether code changed (push) Waiting to run Details
End-to-end tests / Build & Package Grafana (push) Blocked by required conditions Details
End-to-end tests / Build E2E test runner (push) Blocked by required conditions Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/dashboards-suite, dashboards-suite (old arch)) (push) Blocked by required conditions Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/panels-suite, panels-suite (old arch)) (push) Blocked by required conditions Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/smoke-tests-suite, smoke-tests-suite (old arch)) (push) Blocked by required conditions Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/various-suite, various-suite (old arch)) (push) Blocked by required conditions Details
End-to-end tests / ${{ matrix.suite }} (e2e/dashboards-suite, dashboards-suite) (push) Blocked by required conditions Details
End-to-end tests / ${{ matrix.suite }} (e2e/panels-suite, panels-suite) (push) Blocked by required conditions Details
End-to-end tests / ${{ matrix.suite }} (e2e/smoke-tests-suite, smoke-tests-suite) (push) Blocked by required conditions Details
End-to-end tests / ${{ matrix.suite }} (e2e/various-suite, various-suite) (push) Blocked by required conditions Details
End-to-end tests / A11y test (push) Blocked by required conditions Details
End-to-end tests / All E2E tests complete (push) Blocked by required conditions Details
Frontend tests / Detect whether code changed (push) Waiting to run Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (1) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (2) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (3) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (4) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (5) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (6) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (7) (push) Blocked by required conditions Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (8) (push) Blocked by required conditions Details
Frontend tests / All frontend unit tests complete (push) Blocked by required conditions Details
Integration Tests / Sqlite (${{ matrix.shard }}) (1/8) (push) Waiting to run Details
Integration Tests / Sqlite (${{ matrix.shard }}) (2/8) (push) Waiting to run Details
Integration Tests / Sqlite (${{ matrix.shard }}) (3/8) (push) Waiting to run Details
Integration Tests / Sqlite (${{ matrix.shard }}) (4/8) (push) Waiting to run Details
Integration Tests / Sqlite (${{ matrix.shard }}) (5/8) (push) Waiting to run Details
Integration Tests / Sqlite (${{ matrix.shard }}) (6/8) (push) Waiting to run Details
Integration Tests / Sqlite (${{ matrix.shard }}) (7/8) (push) Waiting to run Details
Integration Tests / Sqlite (${{ matrix.shard }}) (8/8) (push) Waiting to run Details
Integration Tests / MySQL (${{ matrix.shard }}) (1/8) (push) Waiting to run Details
Integration Tests / MySQL (${{ matrix.shard }}) (2/8) (push) Waiting to run Details
Integration Tests / MySQL (${{ matrix.shard }}) (3/8) (push) Waiting to run Details
Integration Tests / MySQL (${{ matrix.shard }}) (4/8) (push) Waiting to run Details
Integration Tests / MySQL (${{ matrix.shard }}) (5/8) (push) Waiting to run Details
Integration Tests / MySQL (${{ matrix.shard }}) (6/8) (push) Waiting to run Details
Integration Tests / MySQL (${{ matrix.shard }}) (7/8) (push) Waiting to run Details
Integration Tests / MySQL (${{ matrix.shard }}) (8/8) (push) Waiting to run Details
Integration Tests / Postgres (${{ matrix.shard }}) (1/8) (push) Waiting to run Details
Integration Tests / Postgres (${{ matrix.shard }}) (2/8) (push) Waiting to run Details
Integration Tests / Postgres (${{ matrix.shard }}) (3/8) (push) Waiting to run Details
Integration Tests / Postgres (${{ matrix.shard }}) (4/8) (push) Waiting to run Details
Integration Tests / Postgres (${{ matrix.shard }}) (5/8) (push) Waiting to run Details
Integration Tests / Postgres (${{ matrix.shard }}) (6/8) (push) Waiting to run Details
Integration Tests / Postgres (${{ matrix.shard }}) (7/8) (push) Waiting to run Details
Integration Tests / Postgres (${{ matrix.shard }}) (8/8) (push) Waiting to run Details
Integration Tests / All backend integration tests complete (push) Blocked by required conditions Details
Reject GitHub secrets / reject-gh-secrets (push) Waiting to run Details
Build Release Packages / setup (push) Waiting to run Details
Build Release Packages / Dispatch grafana-enterprise build (push) Blocked by required conditions Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:darwin/amd64, darwin-amd64) (push) Blocked by required conditions Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:darwin/arm64, darwin-arm64) (push) Blocked by required conditions Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/amd64,deb:grafana:linux/amd64,rpm:grafana:linux/amd64,docker:grafana:linux/amd64,docker:grafana:linux/amd64:ubuntu,npm:grafana,storybook, linux-amd64) (push) Blocked by required conditions Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/arm/v6,deb:grafana:linux/arm/v6, linux-armv6) (push) Blocked by required conditions Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/arm/v7,deb:grafana:linux/arm/v7,docker:grafana:linux/arm/v7,docker:grafana:linux/arm/v7:ubuntu, linux-armv7) (push) Blocked by required conditions Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/arm64,deb:grafana:linux/arm64,rpm:grafana:linux/arm64,docker:grafana:linux/arm64,docker:grafana:linux/arm64:ubuntu, linux-arm64) (push) Blocked by required conditions Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/s390x,deb:grafana:linux/s390x,rpm:grafana:linux/s390x,docker:grafana:linux/s390x,docker:grafana:linux/s390x:ubuntu, linux-s390x) (push) Blocked by required conditions Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:windows/amd64,zip:grafana:windows/amd64,msi:grafana:windows/amd64, windows-amd64) (push) Blocked by required conditions Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:windows/arm64,zip:grafana:windows/arm64, windows-arm64) (push) Blocked by required conditions Details
Build Release Packages / Upload artifacts (push) Blocked by required conditions Details
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Waiting to run Details
Shellcheck / Shellcheck scripts (push) Waiting to run Details
Swagger generated code / Verify committed API specs match (push) Waiting to run Details
Dispatch sync to mirror / dispatch-job (push) Waiting to run Details
Documentation / Build & Verify Docs (push) Has been cancelled Details
publish-technical-documentation-next / sync (push) Has been cancelled Details
Verify Storybook / Verify Storybook (push) Has been cancelled Details
trigger-dashboard-search-e2e / trigger-search-e2e (push) Has been cancelled Details
Trivy Scan / trivy-scan (push) Has been cancelled Details

* Add scopes to queries in DataSourceWithBackend

* Remove Prometheus-specific solution

* Readd prometheus support

* move scopes reordering ti loki ds

* Add tests and logQLScope feature flag

* Move featureToggles to before/after each

* Remove irrelevant file change
This commit is contained in:
Tobias Skarhed 2025-07-04 18:35:52 +02:00 committed by GitHub
parent 5edbdb7c4f
commit ab676ce035
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 165 additions and 1 deletions

View File

@ -181,6 +181,7 @@ class DataSourceWithBackend<
if (datasource.uid?.length) {
dsUIDs.add(datasource.uid);
}
return {
...(shouldApplyTemplateVariables ? this.applyTemplateVariables(q, request.scopedVars, request.filters) : q),
datasource,

View File

@ -1809,6 +1809,162 @@ describe('LokiDatasource', () => {
});
});
describe('scopes application', () => {
let ds: LokiDatasource;
let origBackendSrv: BackendSrv;
beforeEach(() => {
origBackendSrv = getBackendSrv();
ds = createLokiDatasource(templateSrvStub);
// Enable the required feature toggles
config.featureToggles.scopeFilters = true;
config.featureToggles.logQLScope = true;
});
afterEach(() => {
setBackendSrv(origBackendSrv);
// Reset feature toggles to false
config.featureToggles.scopeFilters = false;
config.featureToggles.logQLScope = false;
});
it('should apply scopes to queries when feature toggles are enabled', async () => {
const mockScopes = [
{
metadata: { name: 'test-scope' },
spec: {
title: 'Test Scope',
type: 'test',
description: 'Test scope description',
category: 'test-category',
filters: [
{ key: 'environment', value: 'production', operator: 'equals' as const },
{ key: 'service', value: 'api', operator: 'equals' as const },
],
},
},
];
const query: DataQueryRequest<LokiQuery> = {
...baseRequestOptions,
targets: [{ expr: '{job="grafana"}', refId: 'A' }],
scopes: mockScopes,
};
const fetchMock = jest.fn().mockReturnValue(of({ data: testLogsResponse }));
setBackendSrv({ ...origBackendSrv, fetch: fetchMock });
await ds.query(query).pipe(take(1)).toPromise();
expect(fetchMock).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.objectContaining({
queries: expect.arrayContaining([
expect.objectContaining({
scopes: [
{ key: 'environment', value: 'production', operator: 'equals' },
{ key: 'service', value: 'api', operator: 'equals' },
],
}),
]),
}),
})
);
});
it('should not apply scopes when feature toggles are disabled', async () => {
// Disable the required feature toggles
config.featureToggles.scopeFilters = false;
config.featureToggles.logQLScope = false;
const mockScopes = [
{
metadata: { name: 'test-scope' },
spec: {
title: 'Test Scope',
type: 'test',
description: 'Test scope description',
category: 'test-category',
filters: [{ key: 'environment', value: 'production', operator: 'equals' as const }],
},
},
];
const query: DataQueryRequest<LokiQuery> = {
...baseRequestOptions,
targets: [{ expr: '{job="grafana"}', refId: 'A' }],
scopes: mockScopes,
};
const fetchMock = jest.fn().mockReturnValue(of({ data: testLogsResponse }));
setBackendSrv({ ...origBackendSrv, fetch: fetchMock });
await ds.query(query).pipe(take(1)).toPromise();
expect(fetchMock).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.objectContaining({
queries: expect.arrayContaining([
expect.objectContaining({
scopes: undefined,
}),
]),
}),
})
);
});
it('should handle empty scopes array', async () => {
const query: DataQueryRequest<LokiQuery> = {
...baseRequestOptions,
targets: [{ expr: '{job="grafana"}', refId: 'A' }],
scopes: [],
};
const fetchMock = jest.fn().mockReturnValue(of({ data: testLogsResponse }));
setBackendSrv({ ...origBackendSrv, fetch: fetchMock });
await ds.query(query).pipe(take(1)).toPromise();
expect(fetchMock).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.objectContaining({
queries: expect.arrayContaining([
expect.objectContaining({
scopes: [],
}),
]),
}),
})
);
});
it('should handle undefined scopes', async () => {
const query: DataQueryRequest<LokiQuery> = {
...baseRequestOptions,
targets: [{ expr: '{job="grafana"}', refId: 'A' }],
scopes: undefined,
};
const fetchMock = jest.fn().mockReturnValue(of({ data: testLogsResponse }));
setBackendSrv({ ...origBackendSrv, fetch: fetchMock });
await ds.query(query).pipe(take(1)).toPromise();
expect(fetchMock).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.objectContaining({
queries: expect.arrayContaining([
expect.objectContaining({
scopes: undefined,
}),
]),
}),
})
);
});
});
describe('getQueryStats', () => {
let ds: LokiDatasource;
let query: LokiQuery;

View File

@ -299,7 +299,14 @@ export class LokiDatasource
query(request: DataQueryRequest<LokiQuery>): Observable<DataQueryResponse> {
const queries = request.targets
.map(getNormalizedLokiQuery) // used to "fix" the deprecated `.queryType` prop
.map((q) => ({ ...q, maxLines: q.maxLines ?? this.maxLines }));
.map((q) => ({
...q,
maxLines: q.maxLines ?? this.maxLines,
scopes:
config.featureToggles.scopeFilters && config.featureToggles.logQLScope
? request.scopes?.flatMap((scope) => scope.spec.filters)
: undefined,
}));
const fixedRequest: DataQueryRequest<LokiQuery> = {
...request,