Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add upgrade agents global action #6501

Merged
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
4 changes: 1 addition & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ All notable changes to the Wazuh app project will be documented in this file.
- Added AngularJS dependencies [#6145](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6145)
- Added a migration task to setup the configuration using a configuration file [#6337](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6337)
- Added the ability to manage the API hosts from the Server APIs [#6337](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6337) [#6519](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6519)
- Added edit groups action to Endpoints Summary [#6250](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6250)
- Added upgrade agent action to Endpoints Summary [#6476](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6476)
- Added global actions add agents to groups and remove agents from groups to Endpoints Summary [#6274](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6274)
- Added edit agent groups and upgrade agents actions to Endpoints Summary [#6250](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6250) [#6476](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6476) [#6274](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6274) [#6501](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6501)
- Added propagation of updates from the table to dashboard visualizations in Endpoints summary [#6460](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6460)

### Changed
Expand Down
1 change: 0 additions & 1 deletion docker/osd-dev/dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,6 @@ services:
- '${SRC}/main:/home/node/kbn/plugins/wazuh'
- '${SRC}/wazuh-core:/home/node/kbn/plugins/wazuh-core'
- '${SRC}/wazuh-check-updates:/home/node/kbn/plugins/wazuh-check-updates'
- '${SRC}/wazuh-endpoints:/home/node/kbn/plugins/wazuh-endpoints'
- wd_certs:/home/node/kbn/certs/
- ${WAZUH_DASHBOARD_CONF}:/home/node/kbn/config/opensearch_dashboards.yml
- ./config/${OSD_MAJOR}/osd/wazuh.yml:/home/node/kbn/data/wazuh/config/wazuh.yml
Expand Down
9 changes: 9 additions & 0 deletions plugins/main/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,14 +370,23 @@ export const API_NAME_TASK_STATUS = {
DONE: 'Done',
IN_PROGRESS: 'In progress',
FAILED: 'Failed',
TIMEOUT: 'Timeout',
} as const;

export const UI_TASK_STATUS = [
API_NAME_TASK_STATUS.DONE,
API_NAME_TASK_STATUS.IN_PROGRESS,
API_NAME_TASK_STATUS.FAILED,
API_NAME_TASK_STATUS.TIMEOUT,
];

export const UI_TASK_STATUS_COLORS = {
[API_NAME_TASK_STATUS.DONE]: 'success',
[API_NAME_TASK_STATUS.IN_PROGRESS]: 'warning',
[API_NAME_TASK_STATUS.FAILED]: 'danger',
[API_NAME_TASK_STATUS.TIMEOUT]: 'subdued',
};

// Documentation
export const DOCUMENTATION_WEB_BASE_URL = 'https://documentation.wazuh.com';

Expand Down
12 changes: 10 additions & 2 deletions plugins/main/public/components/common/tables/table-wz-api.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const getFilters = filters => {

export function TableWzAPI({
actionButtons,
postActionButtons,
addOnTitle,
extra,
setReload,
Expand All @@ -53,6 +54,11 @@ export function TableWzAPI({
| ReactNode
| ReactNode[]
| (({ filters }: { filters }) => ReactNode);
postActionButtons?:
| ReactNode
| ReactNode[]
| (({ filters }: { filters }) => ReactNode);

title?: string;
addOnTitle?: ReactNode;
description?: string;
Expand Down Expand Up @@ -147,7 +153,7 @@ export function TableWzAPI({
},
[]);

const renderActionButtons = filters => {
const renderActionButtons = (actionButtons, filters) => {
if (Array.isArray(actionButtons)) {
return actionButtons.map((button, key) => (
<EuiFlexItem key={key} grow={false}>
Expand Down Expand Up @@ -216,7 +222,7 @@ export function TableWzAPI({
<EuiFlexItem grow={false}>
<EuiFlexGroup wrap alignItems={'center'} responsive={false}>
{/* Render optional custom action button */}
{renderActionButtons(filters)}
{renderActionButtons(actionButtons, filters)}
{/* Render optional reload button */}
{rest.showReload && ReloadButton}
{/* Render optional export to CSV button */}
Expand All @@ -232,6 +238,8 @@ export function TableWzAPI({
}
/>
)}
{/* Render optional post custom action button */}
{renderActionButtons(postActionButtons, filters)}
{rest.showFieldSelector && (
<EuiFlexItem grow={false}>
<EuiToolTip content='Select visible fields' position='left'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ const OutdatedAgentsCard = ({
}
description={
<EuiTextColor color={contentType}>
<small>Agents</small>
<small>{outdatedAgents === 1 ? 'Agent' : 'Agents'}</small>
</EuiTextColor>
}
titleColor='danger'
titleColor='warning'
isLoading={isLoading}
titleSize='l'
textAlign='center'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,14 @@
width: 100%;
display: grid;
grid-template-columns: 1fr;
gap: 20px 10px;
gap: 16px;
min-height: 200px;

@media (min-width: 1024px) {
grid-template-columns: 1fr 1fr;
}

@media (min-width: 1440px) {
gap: 10px;
grid-template-columns:
minmax(375px, 1fr) minmax(375px, 1fr) minmax(375px, 1fr)
minmax(150px, 300px);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,68 +18,83 @@ export const useGetUpgradeTasks = (reload: any) => {
const [getErrorIsLoading, setErrorIsLoading] = useState(true);
const [getErrorTasksError, setGetErrorTasksError] = useState();

const [totalTimeoutUpgradeTasks, setTotalTimeoutUpgradeTasks] =
useState<number>(0);
const [getTimeoutIsLoading, setTimeoutIsLoading] = useState(true);
const [getTimeoutError, setGetTimeoutError] = useState();

const datetime = new Date();
datetime.setMinutes(datetime.getMinutes() - beforeMinutes);
const formattedDate = datetime.toISOString();
const timeFilter = `last_update_time>${formattedDate}`;

const getUpgradesInProgress = async () => {
const getUpgradeStatus = async (
status: string,
setIsLoading: (isLoading: boolean) => void,
setTotalTasks: (totalTasks: number) => void,
setError: (error) => void,
q?: string,
) => {
try {
setGetInProgressIsLoading(true);
setIsLoading(true);
const { total_affected_items } = await getTasks({
status: API_NAME_TASK_STATUS.IN_PROGRESS,
status,
command: 'upgrade',
limit: 1,
q,
});
setTotalInProgressTasks(total_affected_items);
setGetInProgressError(undefined);
setTotalTasks(total_affected_items);
setError(undefined);
} catch (error: any) {
setGetInProgressError(error);
setError(error);
} finally {
setGetInProgressIsLoading(false);
setIsLoading(false);
}
};

const getUpgradesInProgress = async () =>
await getUpgradeStatus(
API_NAME_TASK_STATUS.IN_PROGRESS,
setGetInProgressIsLoading,
setTotalInProgressTasks,
setGetInProgressError,
);

const getUpgradesSuccess = async () => {
try {
setSuccessIsLoading(true);
const { total_affected_items } = await getTasks({
status: API_NAME_TASK_STATUS.DONE,
command: 'upgrade',
limit: 1,
q: timeFilter,
});
setTotalSuccessTasks(total_affected_items);
setGetSuccessError(undefined);
} catch (error: any) {
setGetSuccessError(error);
} finally {
setSuccessIsLoading(false);
}
await getUpgradeStatus(
API_NAME_TASK_STATUS.DONE,
setSuccessIsLoading,
setTotalSuccessTasks,
setGetSuccessError,
timeFilter,
);
};

const getUpgradesError = async () => {
try {
setErrorIsLoading(true);
const { total_affected_items } = await getTasks({
status: API_NAME_TASK_STATUS.FAILED,
command: 'upgrade',
limit: 1,
q: timeFilter,
});
setTotalErrorUpgradeTasks(total_affected_items);
setGetErrorTasksError(undefined);
} catch (error: any) {
setGetErrorTasksError(error);
} finally {
setErrorIsLoading(false);
}
await getUpgradeStatus(
API_NAME_TASK_STATUS.FAILED,
setErrorIsLoading,
setTotalErrorUpgradeTasks,
setGetErrorTasksError,
timeFilter,
);
};

const getUpgradeTimeout = async () => {
await getUpgradeStatus(
API_NAME_TASK_STATUS.TIMEOUT,
setTimeoutIsLoading,
setTotalTimeoutUpgradeTasks,
setGetTimeoutError,
timeFilter,
);
};

const fetchData = async () => {
await getUpgradesInProgress();
await getUpgradesSuccess();
await getUpgradesError();
await getUpgradeTimeout();
};

useEffect(() => {
Expand All @@ -104,5 +119,8 @@ export const useGetUpgradeTasks = (reload: any) => {
getErrorIsLoading,
totalErrorUpgradeTasks,
getErrorTasksError,
getTimeoutIsLoading,
totalTimeoutUpgradeTasks,
getTimeoutError,
};
};
2 changes: 1 addition & 1 deletion plugins/main/public/components/endpoints-summary/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const MainEndpointsSummary = compose(
return (
<EuiEmptyPrompt
iconType='watchesApp'
title={<h2>No agents were added to this manager.</h2>}
title={<h2>No agents were added to the manager</h2>}
body={<p>Add agents to fleet to start monitoring</p>}
actions={
<WzButtonPermissions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import IApiResponse from '../../../react-services/interfaces/api-response.interface';
import { paginatedAgentsGroupService } from './paginated-agents-group';
import { paginatedAgentsRequestService } from './paginated-agents-request';

export const addAgentsToGroupService = async (parameters: {
agentIds: string[];
groupId: string;
pageSize?: number;
}): Promise<IApiResponse<string>> =>
await paginatedAgentsGroupService({ addOrRemove: 'add', ...parameters });
await paginatedAgentsRequestService({
method: 'PUT',
url: '/agents/group',
...parameters,
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export { removeAgentsFromGroupService } from './remove-agents-from-group';
export { addAgentToGroupService } from './add-agent-to-group';
export { addAgentsToGroupService } from './add-agents-to-group';
export { getGroupsService } from './get-groups';
export { upgradeAgentService } from './upgrade-agent';
export { upgradeAgentsService } from './upgrade-agents';
export { getOutdatedAgents } from './get-outdated-agents';
export { getTasks } from './get-tasks';
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { paginatedAgentsGroupService } from './paginated-agents-group';
import { paginatedAgentsRequestService } from './paginated-agents-request';
import { WzRequest } from '../../../react-services/wz-request';

jest.mock('../../../react-services/wz-request', () => ({
Expand All @@ -7,7 +7,7 @@ jest.mock('../../../react-services/wz-request', () => ({
},
}));

describe('paginatedAgentsGroupService', () => {
describe('paginatedAgentsRequestService', () => {
beforeEach(() => {
jest.clearAllMocks();
});
Expand Down Expand Up @@ -46,13 +46,14 @@ describe('paginatedAgentsGroupService', () => {
);

const params = {
addOrRemove: 'add' as any,
method: 'PUT' as any,
url: '/agents/group',
agentIds: ['agent1', 'agent2', 'agent3'],
groupId: 'group1',
pageSize: 2,
};

const result = await paginatedAgentsGroupService(params);
const result = await paginatedAgentsRequestService(params);

expect(WzRequest.apiReq).toHaveBeenCalledWith(
'PUT',
Expand Down Expand Up @@ -146,13 +147,14 @@ describe('paginatedAgentsGroupService', () => {
);

const params = {
addOrRemove: 'add' as any,
method: 'PUT' as any,
url: '/agents/group',
agentIds: ['agent1', 'agent2', 'agent3'],
groupId: 'group1',
pageSize: 2,
};

const result = await paginatedAgentsGroupService(params);
const result = await paginatedAgentsRequestService(params);

expect(WzRequest.apiReq).toHaveBeenCalledWith(
'PUT',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,19 @@ export type ErrorAgent = {
id: string[];
};

export const paginatedAgentsGroupService = async ({
addOrRemove,
export const paginatedAgentsRequestService = async ({
method,
url,
agentIds,
groupId,
pageSize = 1000,
}: {
addOrRemove: 'add' | 'remove';
method: 'PUT' | 'DELETE';
url: string;
agentIds: string[];
groupId: string;
groupId?: string;
pageSize?: number;
}): Promise<IApiResponse<string>> => {
}): Promise<IApiResponse<any>> => {
let offset = 0;
let requestAgentIds: string[] = [];
let allAffectedItems: string[] = [];
Expand All @@ -45,11 +47,11 @@ export const paginatedAgentsGroupService = async ({
message: responseMessage,
},
} = (await WzRequest.apiReq(
addOrRemove === 'add' ? 'PUT' : 'DELETE',
`/agents/group`,
method,
url,
{
params: {
group_id: groupId,
...(groupId ? { group_id: groupId } : {}),
agents_list: requestAgentIds.join(','),
wait_for_complete: true,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import IApiResponse from '../../../react-services/interfaces/api-response.interface';
import { paginatedAgentsGroupService } from './paginated-agents-group';
import { paginatedAgentsRequestService } from './paginated-agents-request';

export const removeAgentsFromGroupService = async (parameters: {
agentIds: string[];
groupId: string;
pageSize?: number;
}): Promise<IApiResponse<string>> =>
await paginatedAgentsGroupService({ addOrRemove: 'remove', ...parameters });
await paginatedAgentsRequestService({
method: 'DELETE',
url: '/agents/group',
...parameters,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import IApiResponse from '../../../react-services/interfaces/api-response.interface';
import { WzRequest } from '../../../react-services/wz-request';
import { ResponseUpgradeAgents } from '../types';

export const upgradeAgentService = async (agentId: string) =>
(await WzRequest.apiReq('PUT', '/agents/upgrade', {
params: {
agents_list: agentId,
wait_for_complete: true,
},
})) as IApiResponse<ResponseUpgradeAgents>;
Loading
Loading