Skip to content

Commit e2a140e

Browse files
VladimirFilonovwildemat
authored andcommitted
index_not_found_exception for .workflows_execitions is expected error (elastic#241938)
closes: elastic/security-team#14562 Skip index_not_found_exception error in get executions methods, because it's expected (index is not created until first execution), so no need to flood logs and raise errors for user
1 parent 6971933 commit e2a140e

5 files changed

Lines changed: 204 additions & 1 deletion

File tree

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
import { errors } from '@elastic/elasticsearch';
11+
import type { ElasticsearchClient } from '@kbn/core/server';
12+
import { loggerMock } from '@kbn/logging-mocks';
13+
import { searchWorkflowExecutions } from './search_workflow_executions';
14+
15+
describe('searchWorkflowExecutions', () => {
16+
let mockEsClient: jest.Mocked<ElasticsearchClient>;
17+
let mockLogger: ReturnType<typeof loggerMock.create>;
18+
19+
beforeEach(() => {
20+
mockEsClient = {
21+
search: jest.fn(),
22+
} as any;
23+
24+
mockLogger = loggerMock.create();
25+
mockLogger.info = jest.fn();
26+
mockLogger.error = jest.fn();
27+
});
28+
29+
describe('error handling', () => {
30+
it('should not log error when index_not_found_exception occurs (expected behavior)', async () => {
31+
mockLogger.error.mockClear();
32+
const indexNotFoundError = new errors.ResponseError({
33+
statusCode: 404,
34+
body: {
35+
error: {
36+
type: 'index_not_found_exception',
37+
reason: 'no such index [.workflows-executions]',
38+
},
39+
},
40+
headers: {},
41+
meta: {} as any,
42+
warnings: [],
43+
});
44+
45+
mockEsClient.search.mockRejectedValue(indexNotFoundError);
46+
47+
const result = await searchWorkflowExecutions({
48+
esClient: mockEsClient,
49+
logger: mockLogger,
50+
workflowExecutionIndex: '.workflows-executions',
51+
query: { term: { workflowId: 'workflow-1' } },
52+
page: 1,
53+
perPage: 20,
54+
});
55+
56+
expect(result).toEqual({
57+
results: [],
58+
_pagination: {
59+
limit: 20,
60+
page: 1,
61+
total: 0,
62+
},
63+
});
64+
65+
expect(mockLogger.error).not.toHaveBeenCalled();
66+
});
67+
68+
it('should log error and throw when non-index_not_found_exception errors occur', async () => {
69+
mockLogger.error.mockClear();
70+
const otherError = new errors.ResponseError({
71+
statusCode: 500,
72+
body: {
73+
error: {
74+
type: 'internal_server_error',
75+
reason: 'Internal server error',
76+
},
77+
},
78+
headers: {},
79+
meta: {} as any,
80+
warnings: [],
81+
});
82+
83+
mockEsClient.search.mockRejectedValue(otherError);
84+
85+
await expect(
86+
searchWorkflowExecutions({
87+
esClient: mockEsClient,
88+
logger: mockLogger,
89+
workflowExecutionIndex: '.workflows-executions',
90+
query: { term: { workflowId: 'workflow-1' } },
91+
})
92+
).rejects.toThrow(otherError);
93+
94+
expect(mockLogger.error).toHaveBeenCalledWith(
95+
expect.stringContaining('Failed to search workflow executions')
96+
);
97+
});
98+
99+
it('should log error and throw for non-ResponseError exceptions', async () => {
100+
mockLogger.error.mockClear();
101+
const genericError = new Error('Network error');
102+
103+
mockEsClient.search.mockRejectedValue(genericError);
104+
105+
await expect(
106+
searchWorkflowExecutions({
107+
esClient: mockEsClient,
108+
logger: mockLogger,
109+
workflowExecutionIndex: '.workflows-executions',
110+
query: { term: { workflowId: 'workflow-1' } },
111+
})
112+
).rejects.toThrow('Network error');
113+
114+
expect(mockLogger.error).toHaveBeenCalledWith(
115+
expect.stringContaining('Failed to search workflow executions')
116+
);
117+
});
118+
});
119+
});

src/platform/plugins/shared/workflows_management/server/workflows_management/lib/search_workflow_executions.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import type {
1515
Sort,
1616
} from '@elastic/elasticsearch/lib/api/types';
1717
import type { ElasticsearchClient, Logger } from '@kbn/core/server';
18+
import { isResponseError } from '@kbn/es-errors';
1819
import type { EsWorkflowExecution, WorkflowExecutionListDto } from '@kbn/workflows';
1920

2021
interface SearchWorkflowExecutionsParams {
@@ -53,6 +54,18 @@ export const searchWorkflowExecutions = async ({
5354

5455
return transformToWorkflowExecutionListModel(response, page, perPage);
5556
} catch (error) {
57+
// Index not found is expected when no workflows have been executed yet
58+
if (isResponseError(error) && error.body?.error?.type === 'index_not_found_exception') {
59+
return {
60+
results: [],
61+
_pagination: {
62+
limit: perPage,
63+
page,
64+
total: 0,
65+
},
66+
};
67+
}
68+
5669
logger.error(`Failed to search workflow executions: ${error}`);
5770
throw error;
5871
}

src/platform/plugins/shared/workflows_management/server/workflows_management/workflows_management_service.test.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,72 @@ describe('WorkflowsService', () => {
618618
);
619619
});
620620

621+
it('should not log error when index_not_found_exception occurs (expected behavior)', async () => {
622+
mockLogger.error.mockClear();
623+
const mockSearchResponse = {
624+
hits: {
625+
hits: [mockWorkflowDocument],
626+
total: { value: 1 },
627+
},
628+
};
629+
630+
const indexNotFoundError = new errors.ResponseError({
631+
statusCode: 404,
632+
body: {
633+
error: {
634+
type: 'index_not_found_exception',
635+
reason: 'no such index [.workflows-executions]',
636+
},
637+
},
638+
headers: {},
639+
meta: {} as any,
640+
warnings: [],
641+
});
642+
643+
mockEsClient.search
644+
.mockResolvedValueOnce(mockSearchResponse as any)
645+
.mockRejectedValueOnce(indexNotFoundError);
646+
647+
const result = await service.getWorkflows({ limit: 10, page: 1 }, 'default');
648+
649+
expect(result.results[0].history).toEqual([]);
650+
expect(mockLogger.error).not.toHaveBeenCalled();
651+
});
652+
653+
it('should log error when non-index_not_found_exception errors occur', async () => {
654+
mockLogger.error.mockClear();
655+
const mockSearchResponse = {
656+
hits: {
657+
hits: [mockWorkflowDocument],
658+
total: { value: 1 },
659+
},
660+
};
661+
662+
const otherError = new errors.ResponseError({
663+
statusCode: 500,
664+
body: {
665+
error: {
666+
type: 'internal_server_error',
667+
reason: 'Internal server error',
668+
},
669+
},
670+
headers: {},
671+
meta: {} as any,
672+
warnings: [],
673+
});
674+
675+
mockEsClient.search
676+
.mockResolvedValueOnce(mockSearchResponse as any)
677+
.mockRejectedValueOnce(otherError);
678+
679+
const result = await service.getWorkflows({ limit: 10, page: 1 }, 'default');
680+
681+
expect(result.results[0].history).toEqual([]);
682+
expect(mockLogger.error).toHaveBeenCalledWith(
683+
expect.stringContaining('Failed to fetch recent executions for workflows')
684+
);
685+
});
686+
621687
it('should not fetch execution history for empty workflow list', async () => {
622688
const mockSearchResponse = {
623689
hits: {

src/platform/plugins/shared/workflows_management/server/workflows_management/workflows_management_service.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import type {
2323
Logger,
2424
SecurityServiceStart,
2525
} from '@kbn/core/server';
26+
import { isResponseError } from '@kbn/es-errors';
2627
import type { PublicMethodsOf } from '@kbn/utility-types';
2728
import type {
2829
ConnectorTypeInfo,
@@ -1006,7 +1007,10 @@ export class WorkflowsService {
10061007

10071008
return result;
10081009
} catch (error) {
1009-
this.logger.error(`Failed to fetch recent executions for workflows: ${error}`);
1010+
// Index not found is expected when no workflows have been executed yet
1011+
if (!isResponseError(error) || error.body?.error?.type !== 'index_not_found_exception') {
1012+
this.logger.error(`Failed to fetch recent executions for workflows: ${error}`);
1013+
}
10101014
return {};
10111015
}
10121016
}

src/platform/plugins/shared/workflows_management/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"@kbn/core-http-router-server-mocks",
6565
"@kbn/react-query",
6666
"@kbn/core-http-server",
67+
"@kbn/es-errors",
6768
"@kbn/core-http-server-mocks"
6869
],
6970
"exclude": ["target/**/*"]

0 commit comments

Comments
 (0)