Skip to content

Commit ccbf5f4

Browse files
authored
chore: cherry-pick workspace/orchestrator loki patch(1.0.2) to main (#2474)
* Orchestrator: bug fix for logging for workflows feature (#2431) * fix: RHDHBUGS-2735 - Logging For Workflows (#2411) * fix: Fixes RHDHBUGS-2735. Properly adds the auth token to the loki query request * chore: update the changeset to be a patch release (#2430) * chore: update the changelog with the patch value * squash: yarn dedeupe * squash: update changelogs * squash: use global dispatcher for setting an agent instead of per request
1 parent 06ff6df commit ccbf5f4

File tree

10 files changed

+173
-17
lines changed

10 files changed

+173
-17
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@red-hat-developer-hub/backstage-plugin-orchestrator-backend-module-loki': patch
3+
---
4+
5+
Fixes https://issues.redhat.com/browse/RHDHBUGS-2735. Adds the ability to pass an auth token to the lokistack query route
6+
7+
Also adds the ability to add custom Loki log pipeline filters

workspaces/orchestrator/plugins/orchestrator-backend-module-loki/CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,21 @@
2828
- @red-hat-developer-hub/backstage-plugin-orchestrator-common@3.5.0
2929
- @red-hat-developer-hub/backstage-plugin-orchestrator-node@1.1.0
3030

31+
## 1.0.3
32+
33+
### Patch Changes
34+
35+
- Updated dependencies [02a0552]
36+
- @red-hat-developer-hub/backstage-plugin-orchestrator-node@1.0.2
37+
38+
## 1.0.2
39+
40+
### Patch Changes
41+
42+
- cb26604: Fixes https://issues.redhat.com/browse/RHDHBUGS-2735. Adds the ability to pass an auth token to the lokistack query route
43+
44+
Also adds the ability to add custom Loki log pipeline filters
45+
3146
## 1.0.1
3247

3348
### Patch Changes

workspaces/orchestrator/plugins/orchestrator-backend-module-loki/README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,24 @@ orchestrator:
1313
workflowLogProvider:
1414
loki:
1515
baseUrl: http://localhost:3100
16+
token: secrettoken
17+
# rejectUnauthorized: false
18+
# logPipelineFilters:
19+
# - '| filter1'
20+
# - '|= filter2'
1621
# logStreamSelectors:
1722
# - label: 'app'
1823
# value: '=~".+"'
1924
```
2025

21-
The `baseUrl` is required.
26+
The `baseUrl` and `token` are required.
27+
28+
If you're baseURL is using a self-signed certificate, add the `rejectUnauthorized: false` parameter. Note: this should only be used in development.
2229

2330
Multiple Log Stream Selectors can be specified in the `logStreamSelectors` section. See the loki docs to learn more about log stream selectors and their values: https://grafana.com/docs/loki/latest/query/log_queries/#log-stream-selector
2431

32+
Multiple Log Pipeline Filters can be specified in the `logPipelineFilters` section. See the loki docs to learn more about the log pipeline filters and their values and usage: https://grafana.com/docs/loki/latest/query/log_queries/#log-pipeline
33+
2534
## Installation
2635

2736
To install this backend module:

workspaces/orchestrator/plugins/orchestrator-backend-module-loki/config.d.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,22 @@ export interface Config {
2323
loki?: {
2424
/**
2525
* Base URL of the Loki service.
26+
* /loki/api/v1/query_range will be appended to the baseUrl
2627
*/
2728
baseUrl: string;
29+
/**
30+
* Auth Token for accessing the loki query url
31+
*/
32+
token: string;
33+
/**
34+
* Set to false if the baseUrl has a self-signed certificate
35+
* defaults to true
36+
*/
37+
rejectUnauthorized?: boolean;
38+
// Add custom log pipeline filters
39+
// default is the workflow instanceId
40+
// new values will be appened
41+
logPipelineFilters?: Array<string>;
2842
logStreamSelectors?: Array<{
2943
// label is the selector, something like 'app' or 'service_name', etc...
3044
label: string;

workspaces/orchestrator/plugins/orchestrator-backend-module-loki/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@
4040
"@backstage/backend-plugin-api": "^1.6.2",
4141
"@red-hat-developer-hub/backstage-plugin-orchestrator-common": "workspace:^",
4242
"@red-hat-developer-hub/backstage-plugin-orchestrator-node": "workspace:^",
43-
"luxon": "^3.7.2"
43+
"luxon": "^3.7.2",
44+
"undici": "^7.22.0"
4445
},
4546
"devDependencies": {
4647
"@backstage/backend-test-utils": "^1.10.4",

workspaces/orchestrator/plugins/orchestrator-backend-module-loki/src/workflowLogsProviders/LokiProvider.test.ts

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ describe('LokiProvider', () => {
2727
workflowLogProvider: {
2828
loki: {
2929
baseUrl: 'http://localhost:3100',
30+
token: 'notsecret',
3031
},
3132
},
3233
},
@@ -45,13 +46,21 @@ describe('LokiProvider', () => {
4546
// Test the selectors passed in
4647
// Should be an empty array when nothing is passed in
4748
expect(provider.getSelectors()).toEqual([]);
49+
50+
// Test the token passed in
51+
expect(provider.getToken()).toEqual('notsecret');
52+
53+
// Test the default rejectUnauthorized value
54+
expect(provider.getRejectUnauthorized()).toEqual(true);
4855
});
4956
it('should create a provider with custom selectors', () => {
5057
const lokiAppConfig = {
5158
orchestrator: {
5259
workflowLogProvider: {
5360
loki: {
5461
baseUrl: 'http://localhost:3100',
62+
token: 'notsecret',
63+
rejectUnauthorized: false,
5564
logStreamSelectors: [
5665
{
5766
label: 'custom-selector',
@@ -70,6 +79,9 @@ describe('LokiProvider', () => {
7079
const lokiConfig = new ConfigReader(lokiAppConfig);
7180
const provider = LokiProvider.fromConfig(lokiConfig);
7281

82+
// Test the rejectUnauthorized value when set to false
83+
expect(provider.getRejectUnauthorized()).toEqual(false);
84+
7385
// Test the selectors passed in
7486
expect(provider.getSelectors()[0]).toEqual(
7587
lokiAppConfig.orchestrator.workflowLogProvider.loki
@@ -98,6 +110,7 @@ describe('LokiProvider', () => {
98110
workflowLogProvider: {
99111
loki: {
100112
baseUrl: 'http://localhost:3100',
113+
token: 'notsecret',
101114
},
102115
},
103116
},
@@ -114,12 +127,11 @@ describe('LokiProvider', () => {
114127
};
115128

116129
const urlToFetch =
117-
'http://localhost:3100/loki/api/v1/query_range?query=%7Bservice_name%3D%7E%22.%2B%22%7D+%7C%3D%2212345%22&start=2025-12-05T16%3A30%3A13.621Z&end=2026-01-03T16%3A35%3A13.621Z';
130+
'http://localhost:3100/loki/api/v1/query_range?query=%7Bopenshift_log_type%3D%22application%22%7D+%7C%3D%2212345%22&start=2025-12-05T16%3A30%3A13.621Z&end=2026-01-03T16%3A35%3A13.621Z';
118131
const workflowLogs =
119132
await provider.fetchWorkflowLogsByInstance(workflowInstance);
120133

121134
const parsedURLToFetch = new URL(urlToFetch);
122-
expect(fetch).toHaveBeenCalledWith(urlToFetch);
123135
expect(parsedURLToFetch.origin).toEqual(provider.getBaseURL());
124136
expect(parsedURLToFetch.pathname).toEqual('/loki/api/v1/query_range');
125137
expect(parsedURLToFetch.searchParams.get('end')).toEqual(
@@ -129,7 +141,7 @@ describe('LokiProvider', () => {
129141
'2025-12-05T16:30:13.621Z',
130142
); // should be 5 minutes before
131143
expect(parsedURLToFetch.searchParams.get('query')).toEqual(
132-
`{service_name=~".+"} |="${workflowInstance.id}"`,
144+
`{openshift_log_type="application"} |="${workflowInstance.id}"`,
133145
);
134146
expect(workflowLogs).toHaveProperty('instanceId', workflowInstance.id);
135147
expect(workflowLogs).toHaveProperty('logs');
@@ -154,6 +166,7 @@ describe('LokiProvider', () => {
154166
workflowLogProvider: {
155167
loki: {
156168
baseUrl: 'http://localhost:3100',
169+
token: 'notsecret',
157170
},
158171
},
159172
},
@@ -174,7 +187,7 @@ describe('LokiProvider', () => {
174187

175188
await provider.fetchWorkflowLogsByInstance(workflowInstance);
176189
const parsedURLToFetch = new URL(urlToFetch);
177-
expect(fetch).toHaveBeenCalledWith(urlToFetch);
190+
178191
expect(parsedURLToFetch.searchParams.get('end')).toEqual(
179192
'2025-12-05T17:40:13.621Z',
180193
); // Should be 5 minutes after
@@ -193,6 +206,7 @@ describe('LokiProvider', () => {
193206
workflowLogProvider: {
194207
loki: {
195208
baseUrl: 'http://localhost:3100',
209+
token: 'notsecret',
196210
logStreamSelectors: [
197211
{
198212
label: 'custom-selector',
@@ -224,7 +238,7 @@ describe('LokiProvider', () => {
224238
await provider.fetchWorkflowLogsByInstance(workflowInstance);
225239

226240
const parsedURLToFetch = new URL(urlToFetch);
227-
expect(fetch).toHaveBeenCalledWith(urlToFetch);
241+
228242
expect(parsedURLToFetch.origin).toEqual(provider.getBaseURL());
229243
expect(parsedURLToFetch.pathname).toEqual('/loki/api/v1/query_range');
230244
expect(parsedURLToFetch.searchParams.get('query')).toEqual(
@@ -245,6 +259,7 @@ describe('LokiProvider', () => {
245259
workflowLogProvider: {
246260
loki: {
247261
baseUrl: 'http://localhost:3100',
262+
token: 'notsecret',
248263
logStreamSelectors: [
249264
{
250265
value: '=~".+"',
@@ -273,12 +288,55 @@ describe('LokiProvider', () => {
273288

274289
await provider.fetchWorkflowLogsByInstance(workflowInstance);
275290
const parsedURLToFetch = new URL(urlToFetch);
276-
expect(fetch).toHaveBeenCalledWith(urlToFetch);
291+
277292
expect(parsedURLToFetch.origin).toEqual(provider.getBaseURL());
278293
expect(parsedURLToFetch.pathname).toEqual('/loki/api/v1/query_range');
279294
expect(parsedURLToFetch.searchParams.get('query')).toEqual(
280295
`{service_name=~".+",custom-selector1=~".+"} |="${workflowInstance.id}"`,
281296
);
282297
});
298+
299+
it('should have a custom pipeline filter, no label and no value, use defaults', async () => {
300+
const mockResponse: Partial<Response> = {
301+
ok: true,
302+
status: 200,
303+
json: jest.fn().mockResolvedValue(mockWorkflowLog),
304+
};
305+
global.fetch = jest.fn().mockResolvedValue(mockResponse as any);
306+
307+
const lokiAppConfig = {
308+
orchestrator: {
309+
workflowLogProvider: {
310+
loki: {
311+
baseUrl: 'http://localhost:3100',
312+
token: 'notsecret',
313+
logPipelineFilters: ['| filter1', '| json'],
314+
},
315+
},
316+
},
317+
};
318+
319+
const lokiConfig = new ConfigReader(lokiAppConfig);
320+
const provider = LokiProvider.fromConfig(lokiConfig);
321+
const workflowInstance: ProcessInstanceDTO = {
322+
id: '12345',
323+
processId: '54321',
324+
start: '2025-12-05T16:35:13.621Z',
325+
end: '',
326+
nodes: [],
327+
};
328+
329+
const urlToFetch =
330+
'http://localhost:3100/loki/api/v1/query_range?query=%7Bopenshift_log_type%3D%22application%22%7D+%7C%3D%2212345%22+%7C+filter1+%7C+json&start=2025-12-05T16%3A30%3A13.621Z&end=2026-01-03T16%3A35%3A13.621Z';
331+
332+
await provider.fetchWorkflowLogsByInstance(workflowInstance);
333+
const parsedURLToFetch = new URL(urlToFetch);
334+
335+
expect(parsedURLToFetch.origin).toEqual(provider.getBaseURL());
336+
expect(parsedURLToFetch.pathname).toEqual('/loki/api/v1/query_range');
337+
expect(parsedURLToFetch.searchParams.get('query')).toEqual(
338+
`{openshift_log_type="application"} |="${workflowInstance.id}" | filter1 | json`,
339+
);
340+
});
283341
});
284342
});

workspaces/orchestrator/plugins/orchestrator-backend-module-loki/src/workflowLogsProviders/LokiProvider.ts

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,29 @@ import {
2222
} from '@red-hat-developer-hub/backstage-plugin-orchestrator-common';
2323
import { WorkflowLogProvider } from '@red-hat-developer-hub/backstage-plugin-orchestrator-node';
2424

25+
import { Agent, setGlobalDispatcher } from 'undici';
26+
2527
export class LokiProvider implements WorkflowLogProvider {
2628
private readonly baseURL: string;
29+
private readonly token: string;
2730
private readonly selectors: any;
31+
private readonly rejectUnauthorized: boolean;
32+
private readonly logPipelineFilters: any;
2833
private constructor(config: Config) {
2934
this.baseURL = config.getString('baseUrl');
35+
this.token = config.getString('token');
36+
// Only should be false if specified, undefined here should be true
37+
this.rejectUnauthorized =
38+
config.getOptionalBoolean('rejectUnauthorized') === false ? false : true;
3039
this.selectors = config.getOptional('logStreamSelectors') || [];
40+
this.logPipelineFilters = config.getOptional('logPipelineFilters') || [];
41+
42+
const agent = new Agent({
43+
connect: {
44+
rejectUnauthorized: this.rejectUnauthorized,
45+
},
46+
});
47+
setGlobalDispatcher(agent);
3148
}
3249
getBaseURL(): string {
3350
return this.baseURL;
@@ -41,6 +58,14 @@ export class LokiProvider implements WorkflowLogProvider {
4158
return this.selectors;
4259
}
4360

61+
getToken(): string {
62+
return this.token;
63+
}
64+
65+
getRejectUnauthorized(): boolean {
66+
return this.rejectUnauthorized;
67+
}
68+
4469
async fetchWorkflowLogsByInstance(
4570
instance: ProcessInstanceDTO,
4671
): Promise<WorkflowLogsResponse> {
@@ -63,13 +88,14 @@ export class LokiProvider implements WorkflowLogProvider {
6388
const lokiApiEndpoint = '/loki/api/v1/query_range';
6489
// Query is created with a log stream selector and then a log pipeline for more filtering
6590
// format looks like this: {stream-selector=expression} | log pipeline/log filter expression
66-
// The log stream selector part of the query here is getting all service names
91+
// The log stream selector part of the query here is defaulting to openshift_log_type=application
92+
// This is the value used for Openshift Logging
6793
// This might need to be configurable, based on https://grafana.com/docs/loki/latest/query/log_queries/#log-stream-selector
6894
// Log pipeline part looks for the workflow instance id in those logs
6995
// Create the streamSelector
7096
let streamSelector: string = '';
7197
if (this.selectors.length < 1) {
72-
streamSelector = 'service_name=~".+"';
98+
streamSelector = 'openshift_log_type="application"';
7399
} else {
74100
this.selectors.forEach(
75101
(
@@ -78,11 +104,18 @@ export class LokiProvider implements WorkflowLogProvider {
78104
arr: string | any[],
79105
) => {
80106
// something about that last comma
81-
streamSelector += `${entry.label || 'service_name'}${entry.value || '=~".+"'}${index !== arr.length - 1 ? ',' : ''}`;
107+
streamSelector += `${entry.label || 'openshift_log_type'}${entry.value || '=~".+"'}${index !== arr.length - 1 ? ',' : ''}`;
82108
},
83109
);
84110
}
85-
const logPipelineFilter = `|="${instance.id}"`;
111+
let logPipelineFilter: string = `|="${instance.id}"`;
112+
113+
if (this.logPipelineFilters.length > 0) {
114+
this.logPipelineFilters.forEach((element: any) => {
115+
logPipelineFilter += ` ${element}`;
116+
});
117+
}
118+
86119
const params = new URLSearchParams({
87120
query: `{${streamSelector}} ${logPipelineFilter}`,
88121
start: startTime as string,
@@ -93,7 +126,12 @@ export class LokiProvider implements WorkflowLogProvider {
93126

94127
let allResults;
95128
try {
96-
const response = await fetch(urlToFetch);
129+
const response = await fetch(urlToFetch, {
130+
headers: {
131+
Authorization: `Bearer ${this.token}`,
132+
'Content-Type': 'application/json',
133+
},
134+
});
97135
if (!response.ok) {
98136
throw new Error(await response.text());
99137
}

workspaces/orchestrator/plugins/orchestrator-backend/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@
3131
- @red-hat-developer-hub/backstage-plugin-orchestrator-common@3.5.0
3232
- @red-hat-developer-hub/backstage-plugin-orchestrator-node@1.1.0
3333

34+
## 8.6.2
35+
36+
### Patch Changes
37+
38+
- Updated dependencies [02a0552]
39+
- @red-hat-developer-hub/backstage-plugin-orchestrator-node@1.0.2
40+
3441
## 8.6.1
3542

3643
### Patch Changes

workspaces/orchestrator/plugins/orchestrator-node/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@
2525
- Updated dependencies [3648a62]
2626
- @red-hat-developer-hub/backstage-plugin-orchestrator-common@3.5.0
2727

28+
## 1.0.2
29+
30+
### Patch Changes
31+
32+
- 02a0552: Updating the backend-plugin-api to 1.5.0 to be inline with the other packages. For example, the loki-backend module.
33+
2834
## 1.0.1
2935

3036
### Patch Changes

0 commit comments

Comments
 (0)