From ed740633937850716f4fcb6f3488c8d0d63dec2b Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Tue, 2 Apr 2024 14:39:53 +0200 Subject: [PATCH 1/4] added CPU metrics to request diagnostics --- .../fleet/common/types/rest_spec/agent.ts | 11 ++ .../components/agent_diagnostics/index.tsx | 134 ++++++++---------- .../agent_request_diagnostics_modal/index.tsx | 18 ++- .../fleet/public/hooks/use_request/agents.ts | 8 +- .../agent/request_diagnostics_handler.test.ts | 5 +- .../agent/request_diagnostics_handler.ts | 9 +- .../services/agents/request_diagnostics.ts | 17 ++- .../request_diagnostics_action_runner.ts | 6 + .../fleet/server/types/rest_spec/agent.ts | 10 ++ 9 files changed, 134 insertions(+), 84 deletions(-) diff --git a/x-pack/plugins/fleet/common/types/rest_spec/agent.ts b/x-pack/plugins/fleet/common/types/rest_spec/agent.ts index 07553dbc21acf..3e5244bf67353 100644 --- a/x-pack/plugins/fleet/common/types/rest_spec/agent.ts +++ b/x-pack/plugins/fleet/common/types/rest_spec/agent.ts @@ -152,6 +152,16 @@ export interface PostBulkAgentReassignRequest { }; } +export enum RequestDiagnosticsAdditionalMetrics { + 'CPU' = 'CPU', +} + +export interface PostRequestDiagnosticsRequest { + body: { + additional_metrics: RequestDiagnosticsAdditionalMetrics[]; + }; +} + export type PostRequestDiagnosticsResponse = BulkAgentAction; export type PostBulkRequestDiagnosticsResponse = BulkAgentAction; @@ -159,6 +169,7 @@ export interface PostRequestBulkDiagnosticsRequest { body: { agents: string[] | string; batchSize?: number; + additional_metrics: RequestDiagnosticsAdditionalMetrics[]; }; } diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_diagnostics/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_diagnostics/index.tsx index c16066b0e771b..e1079fea1fd4a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_diagnostics/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_diagnostics/index.tsx @@ -6,6 +6,7 @@ */ import type { EuiTableFieldDataColumnType } from '@elastic/eui'; +import { EuiPortal } from '@elastic/eui'; import { EuiToolTip } from '@elastic/eui'; import { EuiBasicTable, @@ -30,14 +31,9 @@ import { MINIMUM_DIAGNOSTICS_AGENT_VERSION, } from '../../../../../../../../common/services'; -import { - sendGetAgentUploads, - sendPostRequestDiagnostics, - useAuthz, - useLink, - useStartServices, -} from '../../../../../hooks'; +import { sendGetAgentUploads, useAuthz, useLink, useStartServices } from '../../../../../hooks'; import type { AgentDiagnostics, Agent } from '../../../../../../../../common/types/models'; +import { AgentRequestDiagnosticsModal } from '../../../components/agent_request_diagnostics_modal'; const FlexStartEuiFlexItem = styled(EuiFlexItem)` align-self: flex-start; @@ -60,6 +56,7 @@ export const AgentDiagnosticsTab: React.FunctionComponent const [diagnosticsEntries, setDiagnosticEntries] = useState([]); const [prevDiagnosticsEntries, setPrevDiagnosticEntries] = useState([]); const [loadInterval, setLoadInterval] = useState(10000); + const [isRequestDiagnosticsModalOpen, setIsRequestDiagnosticsModalOpen] = useState(false); const loadData = useCallback(async () => { try { @@ -194,93 +191,80 @@ export const AgentDiagnosticsTab: React.FunctionComponent }, ]; - async function onSubmit() { - try { - setIsSubmitting(true); - const { error } = await sendPostRequestDiagnostics(agent.id); - if (error) { - throw error; - } - setIsSubmitting(false); - const successMessage = i18n.translate( - 'xpack.fleet.requestDiagnostics.successSingleNotificationTitle', - { - defaultMessage: 'Request diagnostics submitted', - } - ); - notifications.toasts.addSuccess(successMessage); - loadData(); - } catch (error) { - setIsSubmitting(false); - notifications.toasts.addError(error, { - title: i18n.translate('xpack.fleet.requestDiagnostics.fatalErrorNotificationTitle', { - defaultMessage: - 'Error requesting diagnostics {count, plural, one {agent} other {agents}}', - values: { count: 1 }, - }), - }); - } - } - const requestDiagnosticsButton = ( { + setIsRequestDiagnosticsModalOpen(true); + }} disabled={ isSubmitting || !isAgentRequestDiagnosticsSupported(agent) || !authz.fleet.readAgents } > ); return ( - - - - } - > - + {isRequestDiagnosticsModalOpen && ( + + { + setIsRequestDiagnosticsModalOpen(false); + }} /> - - - - {isAgentRequestDiagnosticsSupported(agent) ? ( - requestDiagnosticsButton - ) : ( - + )} + + + } > - {requestDiagnosticsButton} - - )} - - - {isLoading ? ( - - ) : ( - items={diagnosticsEntries} columns={columns} /> - )} - - + + + + + {isAgentRequestDiagnosticsSupported(agent) ? ( + requestDiagnosticsButton + ) : ( + + } + > + {requestDiagnosticsButton} + + )} + + + {isLoading ? ( + + ) : ( + items={diagnosticsEntries} columns={columns} /> + )} + + + ); }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_request_diagnostics_modal/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_request_diagnostics_modal/index.tsx index 0d3ccbd804e52..75b335076195c 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_request_diagnostics_modal/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_request_diagnostics_modal/index.tsx @@ -8,9 +8,10 @@ import React, { useState } from 'react'; import { useHistory } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; -import { EuiConfirmModal } from '@elastic/eui'; +import { EuiCheckbox, EuiConfirmModal } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { RequestDiagnosticsAdditionalMetrics } from '../../../../../../../common/types'; import type { Agent } from '../../../../types'; import { sendPostRequestDiagnostics, @@ -35,15 +36,20 @@ export const AgentRequestDiagnosticsModal: React.FunctionComponent = ({ const isSingleAgent = Array.isArray(agents) && agents.length === 1; const { getPath } = useLink(); const history = useHistory(); + const [cpuMetricsEnabled, setCPUMetricsEnabled] = useState(false); async function onSubmit() { try { setIsSubmitting(true); + const additionalMetrics = cpuMetricsEnabled ? [RequestDiagnosticsAdditionalMetrics.CPU] : []; const { error } = isSingleAgent - ? await sendPostRequestDiagnostics((agents[0] as Agent).id) + ? await sendPostRequestDiagnostics((agents[0] as Agent).id, { + additional_metrics: additionalMetrics, + }) : await sendPostBulkRequestDiagnostics({ agents: typeof agents === 'string' ? agents : agents.map((agent) => agent.id), + additional_metrics: additionalMetrics, }); if (error) { throw error; @@ -122,6 +128,14 @@ export const AgentRequestDiagnosticsModal: React.FunctionComponent = ({ defaultMessage="Diagnostics files are stored in Elasticsearch, and as such can incur storage costs." />

+

+ setCPUMetricsEnabled(!cpuMetricsEnabled)} + /> +

); }; diff --git a/x-pack/plugins/fleet/public/hooks/use_request/agents.ts b/x-pack/plugins/fleet/public/hooks/use_request/agents.ts index 70576d65ef575..e83243b9efa4d 100644 --- a/x-pack/plugins/fleet/public/hooks/use_request/agents.ts +++ b/x-pack/plugins/fleet/public/hooks/use_request/agents.ts @@ -14,6 +14,7 @@ import type { PostBulkRequestDiagnosticsResponse, PostBulkUpdateAgentTagsRequest, PostRequestBulkDiagnosticsRequest, + PostRequestDiagnosticsRequest, PostRequestDiagnosticsResponse, UpdateAgentRequest, } from '../../../common/types'; @@ -206,10 +207,15 @@ export function sendPostAgentUpgrade( }); } -export function sendPostRequestDiagnostics(agentId: string, options?: RequestOptions) { +export function sendPostRequestDiagnostics( + agentId: string, + body: PostRequestDiagnosticsRequest['body'], + options?: RequestOptions +) { return sendRequest({ path: agentRouteService.getRequestDiagnosticsPath(agentId), method: 'post', + body, version: API_VERSIONS.public.v1, ...options, }); diff --git a/x-pack/plugins/fleet/server/routes/agent/request_diagnostics_handler.test.ts b/x-pack/plugins/fleet/server/routes/agent/request_diagnostics_handler.test.ts index 4442d7694d4bb..77efd46704366 100644 --- a/x-pack/plugins/fleet/server/routes/agent/request_diagnostics_handler.test.ts +++ b/x-pack/plugins/fleet/server/routes/agent/request_diagnostics_handler.test.ts @@ -50,7 +50,10 @@ describe('request diagnostics handler', () => { }, }, } as unknown as RequestHandlerContext; - mockRequest = httpServerMock.createKibanaRequest({ params: { agentId: 'agent1' } }); + mockRequest = httpServerMock.createKibanaRequest({ + params: { agentId: 'agent1' }, + body: { additional_metrics: ['CPU'] }, + }); }); it('should return ok if agent supports request diagnostics', async () => { diff --git a/x-pack/plugins/fleet/server/routes/agent/request_diagnostics_handler.ts b/x-pack/plugins/fleet/server/routes/agent/request_diagnostics_handler.ts index d4598101580ef..f82bc8fda927c 100644 --- a/x-pack/plugins/fleet/server/routes/agent/request_diagnostics_handler.ts +++ b/x-pack/plugins/fleet/server/routes/agent/request_diagnostics_handler.ts @@ -21,7 +21,7 @@ import { getAgentById } from '../../services/agents'; export const requestDiagnosticsHandler: RequestHandler< TypeOf, undefined, - undefined + TypeOf > = async (context, request, response) => { const coreContext = await context.core; const esClient = coreContext.elasticsearch.client.asInternalUser; @@ -38,7 +38,11 @@ export const requestDiagnosticsHandler: RequestHandler< }); } - const result = await AgentService.requestDiagnostics(esClient, request.params.agentId); + const result = await AgentService.requestDiagnostics( + esClient, + request.params.agentId, + request.body.additional_metrics + ); return response.ok({ body: { actionId: result.actionId } }); } catch (error) { @@ -61,6 +65,7 @@ export const bulkRequestDiagnosticsHandler: RequestHandler< const result = await AgentService.bulkRequestDiagnostics(esClient, soClient, { ...agentOptions, batchSize: request.body.batchSize, + additionalMetrics: request.body.additional_metrics, }); return response.ok({ body: { actionId: result.actionId } }); diff --git a/x-pack/plugins/fleet/server/services/agents/request_diagnostics.ts b/x-pack/plugins/fleet/server/services/agents/request_diagnostics.ts index 51b56d0e0007e..e2ed7e7ae8f1f 100644 --- a/x-pack/plugins/fleet/server/services/agents/request_diagnostics.ts +++ b/x-pack/plugins/fleet/server/services/agents/request_diagnostics.ts @@ -7,6 +7,8 @@ import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; +import type { RequestDiagnosticsAdditionalMetrics } from '../../../common/types'; + import { SO_SEARCH_LIMIT } from '../../constants'; import type { GetAgentsOptions } from '.'; @@ -22,13 +24,17 @@ const REQUEST_DIAGNOSTICS_TIMEOUT_MS = 3 * 60 * 1000; // 3 hours; export async function requestDiagnostics( esClient: ElasticsearchClient, - agentId: string + agentId: string, + additionalMetrics?: RequestDiagnosticsAdditionalMetrics[] ): Promise<{ actionId: string }> { const response = await createAgentAction(esClient, { agents: [agentId], created_at: new Date().toISOString(), type: 'REQUEST_DIAGNOSTICS', expiration: new Date(Date.now() + REQUEST_DIAGNOSTICS_TIMEOUT_MS).toISOString(), + data: { + additional_metrics: additionalMetrics, + }, }); return { actionId: response.id }; } @@ -38,11 +44,14 @@ export async function bulkRequestDiagnostics( soClient: SavedObjectsClientContract, options: GetAgentsOptions & { batchSize?: number; + additionalMetrics?: RequestDiagnosticsAdditionalMetrics[]; } ): Promise<{ actionId: string }> { if ('agentIds' in options) { const givenAgents = await getAgents(esClient, soClient, options); - return await requestDiagnosticsBatch(esClient, givenAgents, {}); + return await requestDiagnosticsBatch(esClient, givenAgents, { + additionalMetrics: options.additionalMetrics, + }); } const batchSize = options.batchSize ?? SO_SEARCH_LIMIT; @@ -54,7 +63,9 @@ export async function bulkRequestDiagnostics( }); if (res.total <= batchSize) { const givenAgents = await getAgents(esClient, soClient, options); - return await requestDiagnosticsBatch(esClient, givenAgents, {}); + return await requestDiagnosticsBatch(esClient, givenAgents, { + additionalMetrics: options.additionalMetrics, + }); } else { return await new RequestDiagnosticsActionRunner( esClient, diff --git a/x-pack/plugins/fleet/server/services/agents/request_diagnostics_action_runner.ts b/x-pack/plugins/fleet/server/services/agents/request_diagnostics_action_runner.ts index 88cb2d5770688..a2d2d753eaa71 100644 --- a/x-pack/plugins/fleet/server/services/agents/request_diagnostics_action_runner.ts +++ b/x-pack/plugins/fleet/server/services/agents/request_diagnostics_action_runner.ts @@ -8,6 +8,8 @@ import { v4 as uuidv4 } from 'uuid'; import type { ElasticsearchClient } from '@kbn/core/server'; +import type { RequestDiagnosticsAdditionalMetrics } from '../../../common/types'; + import { isAgentRequestDiagnosticsSupported } from '../../../common/services'; import type { Agent } from '../../types'; @@ -38,6 +40,7 @@ export async function requestDiagnosticsBatch( options: { actionId?: string; total?: number; + additionalMetrics?: RequestDiagnosticsAdditionalMetrics[]; } ): Promise<{ actionId: string }> { const errors: Record = {}; @@ -62,6 +65,9 @@ export async function requestDiagnosticsBatch( created_at: now, type: 'REQUEST_DIAGNOSTICS', total, + data: { + additional_metrics: options.additionalMetrics, + }, }); await createErrorActionResults( diff --git a/x-pack/plugins/fleet/server/types/rest_spec/agent.ts b/x-pack/plugins/fleet/server/types/rest_spec/agent.ts index 456463561f0b2..7a92798aca29d 100644 --- a/x-pack/plugins/fleet/server/types/rest_spec/agent.ts +++ b/x-pack/plugins/fleet/server/types/rest_spec/agent.ts @@ -9,6 +9,8 @@ import { schema } from '@kbn/config-schema'; import moment from 'moment'; import semverIsValid from 'semver/functions/valid'; +import { RequestDiagnosticsAdditionalMetrics } from '../../../common/types'; + import { SO_SEARCH_LIMIT, AGENTS_PREFIX, AGENT_MAPPINGS } from '../../constants'; import { NewAgentActionSchema } from '../models'; @@ -163,12 +165,20 @@ export const PostRequestDiagnosticsActionRequestSchema = { params: schema.object({ agentId: schema.string(), }), + body: schema.object({ + additional_metrics: schema.maybe( + schema.arrayOf(schema.oneOf([schema.literal(RequestDiagnosticsAdditionalMetrics.CPU)])) + ), + }), }; export const PostBulkRequestDiagnosticsActionRequestSchema = { body: schema.object({ agents: schema.oneOf([schema.arrayOf(schema.string()), schema.string()]), batchSize: schema.maybe(schema.number()), + additional_metrics: schema.maybe( + schema.arrayOf(schema.oneOf([schema.literal(RequestDiagnosticsAdditionalMetrics.CPU)])) + ), }), }; From e5d1e544954c40d6698dcbe296231bf415dd9c3d Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Tue, 2 Apr 2024 15:52:30 +0200 Subject: [PATCH 2/4] updated openapi spec --- .../plugins/fleet/common/openapi/bundled.json | 43 ++++++++++++++++++- .../plugins/fleet/common/openapi/bundled.yaml | 25 ++++++++++- .../fleet/common/openapi/entrypoint.yaml | 3 +- .../agents@bulk_request_diagnostics.yaml | 7 +++ ...agents@{agent_id}@request_diagnostics.yaml | 13 ++++++ ...lates@{pkg_name}@{pkg_version}@inputs.yaml | 2 + 6 files changed, 87 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/fleet/common/openapi/bundled.json b/x-pack/plugins/fleet/common/openapi/bundled.json index f7c192db0fe5e..a526294d963fa 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.json +++ b/x-pack/plugins/fleet/common/openapi/bundled.json @@ -15,8 +15,7 @@ }, "servers": [ { - "url": "http://localhost:5601/api/fleet", - "description": "local" + "url": "http://KIBANA_HOST:5601/api/fleet" } ], "paths": { @@ -1530,6 +1529,9 @@ "Elastic Package Manager (EPM)" ], "responses": { + "200": { + "description": "OK" + }, "400": { "$ref": "#/components/responses/error" } @@ -3076,6 +3078,30 @@ "tags": [ "Agents" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "additional_metrics": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string", + "enum": [ + "CPU" + ] + } + ] + } + } + } + } + } + } + }, "responses": { "200": { "description": "OK", @@ -3159,6 +3185,19 @@ "description": "list of agent IDs" } ] + }, + "additional_metrics": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string", + "enum": [ + "CPU" + ] + } + ] + } } }, "required": [ diff --git a/x-pack/plugins/fleet/common/openapi/bundled.yaml b/x-pack/plugins/fleet/common/openapi/bundled.yaml index 406a2550d770d..f76e50c095aed 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.yaml +++ b/x-pack/plugins/fleet/common/openapi/bundled.yaml @@ -10,8 +10,7 @@ info: name: Elastic License 2.0 url: https://www.elastic.co/licensing/elastic-license servers: - - url: http://localhost:5601/api/fleet - description: local + - url: http://KIBANA_HOST:5601/api/fleet paths: /health_check: post: @@ -966,6 +965,8 @@ paths: tags: - Elastic Package Manager (EPM) responses: + '200': + description: OK '400': $ref: '#/components/responses/error' operationId: get-inputs-template @@ -1928,6 +1929,19 @@ paths: summary: Request agent diagnostics tags: - Agents + requestBody: + content: + application/json: + schema: + type: object + properties: + additional_metrics: + type: array + items: + oneOf: + - type: string + enum: + - CPU responses: '200': description: OK @@ -1979,6 +1993,13 @@ paths: items: type: string description: list of agent IDs + additional_metrics: + type: array + items: + oneOf: + - type: string + enum: + - CPU required: - agents example: diff --git a/x-pack/plugins/fleet/common/openapi/entrypoint.yaml b/x-pack/plugins/fleet/common/openapi/entrypoint.yaml index beb18d507079e..a4b3f2cbaaa18 100644 --- a/x-pack/plugins/fleet/common/openapi/entrypoint.yaml +++ b/x-pack/plugins/fleet/common/openapi/entrypoint.yaml @@ -10,8 +10,7 @@ info: name: Elastic License 2.0 url: https://www.elastic.co/licensing/elastic-license servers: - - url: 'http://localhost:5601/api/fleet' - description: local + - url: 'http://KIBANA_HOST:5601/api/fleet' paths: # Fleet internals /health_check: diff --git a/x-pack/plugins/fleet/common/openapi/paths/agents@bulk_request_diagnostics.yaml b/x-pack/plugins/fleet/common/openapi/paths/agents@bulk_request_diagnostics.yaml index 3f0733ed8f258..2ef2d1c1ff5d5 100644 --- a/x-pack/plugins/fleet/common/openapi/paths/agents@bulk_request_diagnostics.yaml +++ b/x-pack/plugins/fleet/common/openapi/paths/agents@bulk_request_diagnostics.yaml @@ -33,6 +33,13 @@ post: items: type: string description: list of agent IDs + additional_metrics: + type: array + items: + oneOf: + - type: string + enum: + - "CPU" required: - agents example: diff --git a/x-pack/plugins/fleet/common/openapi/paths/agents@{agent_id}@request_diagnostics.yaml b/x-pack/plugins/fleet/common/openapi/paths/agents@{agent_id}@request_diagnostics.yaml index 37aed12d5f4f1..eba9a695d1cbc 100644 --- a/x-pack/plugins/fleet/common/openapi/paths/agents@{agent_id}@request_diagnostics.yaml +++ b/x-pack/plugins/fleet/common/openapi/paths/agents@{agent_id}@request_diagnostics.yaml @@ -8,6 +8,19 @@ post: summary: Request agent diagnostics tags: - Agents + requestBody: + content: + application/json: + schema: + type: object + properties: + additional_metrics: + type: array + items: + oneOf: + - type: string + enum: + - "CPU" responses: '200': description: OK diff --git a/x-pack/plugins/fleet/common/openapi/paths/epm@templates@{pkg_name}@{pkg_version}@inputs.yaml b/x-pack/plugins/fleet/common/openapi/paths/epm@templates@{pkg_name}@{pkg_version}@inputs.yaml index 5f7b3f28c8c7d..3a5c89f7c34e3 100644 --- a/x-pack/plugins/fleet/common/openapi/paths/epm@templates@{pkg_name}@{pkg_version}@inputs.yaml +++ b/x-pack/plugins/fleet/common/openapi/paths/epm@templates@{pkg_name}@{pkg_version}@inputs.yaml @@ -5,6 +5,8 @@ get: responses: '400': $ref: ../components/responses/error.yaml + '200': + description: OK operationId: get-inputs-template security: - basicAuth: [] From 27aab02e9ca9ec38d139cf9ec5d7db1974ff2b71 Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Tue, 2 Apr 2024 16:38:44 +0200 Subject: [PATCH 3/4] fix tests --- .../components/agent_diagnostics/index.tsx | 5 +-- .../agent/request_diagnostics_handler.test.ts | 9 ++++- .../agent/request_diagnostics_handler.ts | 2 +- .../fleet/server/types/rest_spec/agent.ts | 12 +++--- .../apis/agents/request_diagnostics.ts | 39 +++++++++++++++++++ 5 files changed, 56 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_diagnostics/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_diagnostics/index.tsx index e1079fea1fd4a..1eaea92be9639 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_diagnostics/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_diagnostics/index.tsx @@ -51,7 +51,6 @@ export const AgentDiagnosticsTab: React.FunctionComponent const authz = useAuthz(); const { notifications } = useStartServices(); const { getAbsolutePath } = useLink(); - const [isSubmitting, setIsSubmitting] = useState(false); const [isLoading, setIsLoading] = useState(true); const [diagnosticsEntries, setDiagnosticEntries] = useState([]); const [prevDiagnosticsEntries, setPrevDiagnosticEntries] = useState([]); @@ -198,9 +197,7 @@ export const AgentDiagnosticsTab: React.FunctionComponent onClick={() => { setIsRequestDiagnosticsModalOpen(true); }} - disabled={ - isSubmitting || !isAgentRequestDiagnosticsSupported(agent) || !authz.fleet.readAgents - } + disabled={!isAgentRequestDiagnosticsSupported(agent) || !authz.fleet.readAgents} > { let mockSavedObjectsClient: jest.Mocked; let mockElasticsearchClient: jest.Mocked; let mockContext: RequestHandlerContext; - let mockRequest: KibanaRequest<{ agentId: string }, undefined, undefined, any>; + let mockRequest: KibanaRequest< + { agentId: string }, + undefined, + { additional_metrics: RequestDiagnosticsAdditionalMetrics[] }, + any + >; beforeEach(() => { mockSavedObjectsClient = savedObjectsClientMock.create(); diff --git a/x-pack/plugins/fleet/server/routes/agent/request_diagnostics_handler.ts b/x-pack/plugins/fleet/server/routes/agent/request_diagnostics_handler.ts index f82bc8fda927c..a87b21ab89352 100644 --- a/x-pack/plugins/fleet/server/routes/agent/request_diagnostics_handler.ts +++ b/x-pack/plugins/fleet/server/routes/agent/request_diagnostics_handler.ts @@ -41,7 +41,7 @@ export const requestDiagnosticsHandler: RequestHandler< const result = await AgentService.requestDiagnostics( esClient, request.params.agentId, - request.body.additional_metrics + request.body?.additional_metrics ); return response.ok({ body: { actionId: result.actionId } }); diff --git a/x-pack/plugins/fleet/server/types/rest_spec/agent.ts b/x-pack/plugins/fleet/server/types/rest_spec/agent.ts index 7a92798aca29d..ad3e96d10d555 100644 --- a/x-pack/plugins/fleet/server/types/rest_spec/agent.ts +++ b/x-pack/plugins/fleet/server/types/rest_spec/agent.ts @@ -165,11 +165,13 @@ export const PostRequestDiagnosticsActionRequestSchema = { params: schema.object({ agentId: schema.string(), }), - body: schema.object({ - additional_metrics: schema.maybe( - schema.arrayOf(schema.oneOf([schema.literal(RequestDiagnosticsAdditionalMetrics.CPU)])) - ), - }), + body: schema.nullable( + schema.object({ + additional_metrics: schema.maybe( + schema.arrayOf(schema.oneOf([schema.literal(RequestDiagnosticsAdditionalMetrics.CPU)])) + ), + }) + ), }; export const PostBulkRequestDiagnosticsActionRequestSchema = { diff --git a/x-pack/test/fleet_api_integration/apis/agents/request_diagnostics.ts b/x-pack/test/fleet_api_integration/apis/agents/request_diagnostics.ts index 8bf70174780a7..70b1b2c20b1d1 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/request_diagnostics.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/request_diagnostics.ts @@ -17,6 +17,7 @@ export default function (providerContext: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); const supertestWithoutAuth = getService('supertestWithoutAuth'); + const es = getService('es'); describe('fleet_request_diagnostics', () => { skipIfNoDockerRegistry(providerContext); @@ -118,5 +119,43 @@ export default function (providerContext: FtrProviderContext) { throw e; }); }); + + it('should create action with additional_metrics when api contains CPU option', async () => { + await supertest + .post(`/api/fleet/agents/agent1/request_diagnostics`) + .set('kbn-xsrf', 'xxx') + .send({ + additional_metrics: ['CPU'], + }) + .expect(200); + const actionsRes = await es.search({ + index: '.fleet-actions', + body: { + sort: [{ '@timestamp': { order: 'desc' } }], + }, + }); + const action: any = actionsRes.hits.hits[0]._source; + expect(action.data.additional_metrics).contain('CPU'); + }); + + it('/agents/bulk_request_diagnostics should add CPU option to action doc', async () => { + await supertestWithoutAuth + .post(`/api/fleet/agents/bulk_request_diagnostics`) + .set('kbn-xsrf', 'xxx') + .auth(testUsers.fleet_agents_read_only.username, testUsers.fleet_agents_read_only.password) + .send({ + agents: ['agent2', 'agent3'], + additional_metrics: ['CPU'], + }); + + const actionsRes = await es.search({ + index: '.fleet-actions', + body: { + sort: [{ '@timestamp': { order: 'desc' } }], + }, + }); + const action: any = actionsRes.hits.hits[0]._source; + expect(action.data.additional_metrics).contain('CPU'); + }); }); } From bdfd7948b50a726e44060cc0fcea258b65aac1f4 Mon Sep 17 00:00:00 2001 From: Julia Bardi Date: Wed, 3 Apr 2024 09:37:12 +0200 Subject: [PATCH 4/4] added unit test --- .../index.test.tsx | 104 ++++++++++++++++++ .../agent_request_diagnostics_modal/index.tsx | 1 + 2 files changed, 105 insertions(+) create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_request_diagnostics_modal/index.test.tsx diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_request_diagnostics_modal/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_request_diagnostics_modal/index.test.tsx new file mode 100644 index 0000000000000..b86e3cc29a67a --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_request_diagnostics_modal/index.test.tsx @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { act, fireEvent } from '@testing-library/react'; + +import { createFleetTestRendererMock } from '../../../../../../mock'; + +import { sendPostRequestDiagnostics, sendPostBulkRequestDiagnostics } from '../../../../hooks'; + +import { AgentRequestDiagnosticsModal } from '.'; + +jest.mock('../../../../hooks', () => { + return { + ...jest.requireActual('../../../../hooks'), + sendPostRequestDiagnostics: jest.fn().mockResolvedValue({}), + sendPostBulkRequestDiagnostics: jest.fn().mockResolvedValue({}), + }; +}); + +const mockSendPostRequestDiagnostics = sendPostRequestDiagnostics as jest.Mock; +const mockSendPostBulkRequestDiagnostics = sendPostBulkRequestDiagnostics as jest.Mock; + +describe('AgentRequestDiagnosticsModal', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + function render(props: any = {}) { + const renderer = createFleetTestRendererMock(); + + const utils = renderer.render( + + ); + + return { utils }; + } + + it('should include CPU option when checkbox is checked', async () => { + const { utils } = render(); + + act(() => { + fireEvent.click(utils.getByTestId('cpuMetricsCheckbox')); + }); + act(() => { + fireEvent.click(utils.getByTestId('confirmModalConfirmButton')); + }); + + expect(mockSendPostRequestDiagnostics).toHaveBeenCalledWith('agent1', { + additional_metrics: ['CPU'], + }); + }); + + it('should not include CPU option when checkbox is not checked', async () => { + const { utils } = render(); + + act(() => { + fireEvent.click(utils.getByTestId('confirmModalConfirmButton')); + }); + + expect(mockSendPostRequestDiagnostics).toHaveBeenCalledWith('agent1', { + additional_metrics: [], + }); + }); + + it('should include CPU option when checkbox is checked when bulk action', async () => { + const { utils } = render({ agents: [{ id: 'agent1' }, { id: 'agent2' }], agentCount: 2 }); + + act(() => { + fireEvent.click(utils.getByTestId('cpuMetricsCheckbox')); + }); + act(() => { + fireEvent.click(utils.getByTestId('confirmModalConfirmButton')); + }); + + expect(mockSendPostBulkRequestDiagnostics).toHaveBeenCalledWith({ + additional_metrics: ['CPU'], + agents: ['agent1', 'agent2'], + }); + }); + + it('should not include CPU option when checkbox is checked when bulk action', async () => { + const { utils } = render({ agents: [{ id: 'agent1' }, { id: 'agent2' }], agentCount: 2 }); + + act(() => { + fireEvent.click(utils.getByTestId('confirmModalConfirmButton')); + }); + + expect(mockSendPostBulkRequestDiagnostics).toHaveBeenCalledWith({ + additional_metrics: [], + agents: ['agent1', 'agent2'], + }); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_request_diagnostics_modal/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_request_diagnostics_modal/index.tsx index 75b335076195c..82bf1f1b31874 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_request_diagnostics_modal/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_request_diagnostics_modal/index.tsx @@ -131,6 +131,7 @@ export const AgentRequestDiagnosticsModal: React.FunctionComponent = ({

setCPUMetricsEnabled(!cpuMetricsEnabled)}