mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[8.13] [Fleet] Actions menu for Fleet agents has correct agent count and bulk actions are correctly processed for all selected agents (#177035) (#179022)
# Backport This will backport the following commits from `main` to `8.13`: - [[Fleet] Actions menu for Fleet agents has correct agent count and bulk actions are correctly processed for all selected agents (#177035)](https://github.com/elastic/kibana/pull/177035) <!--- Backport version: 9.4.3 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Jill Guyonnet","email":"jill.guyonnet@elastic.co"},"sourceCommit":{"committedDate":"2024-03-12T08:03:36Z","message":"[Fleet] Actions menu for Fleet agents has correct agent count and bulk actions are correctly processed for all selected agents (#177035)\n\n## Summary\r\n\r\nCloses https://github.com/elastic/kibana/issues/167269\r\nCloses https://github.com/elastic/kibana/issues/171914\r\nCloses https://github.com/elastic/kibana/issues/167241\r\n\r\nChanges:\r\n- Agent count in `Actions` menu includes all selectable agents across\r\nall pages, including agents with inactive status.\r\n- `Actions` menu items are enabled if at least one agent is selected, no\r\nmatter its status.\r\n- Fix bug where managed agents could be accidentally selected in query\r\nmode when changing filtering.\r\n- Changing agent status or agent policy filtering while in bulk\r\nselection mode sets selection mode back to manual. This is to avoid a\r\nbad state where bulk selection mode is still enabled and more\r\n(unselected) agents are listed.\r\n- Fix the bulk selection query when some agents are excluded (managed\r\nagent policies).\r\n- Agent upgrades in bulk selection mode includes all selected agents,\r\nincluding agents with inactive status.\r\n- Agent policy reassign in bulk selection mode includes all selected\r\nagents, including agents with inactive status.\r\n\r\n### Steps for testing\r\n\r\nCf. screen recording below.\r\n\r\n#### Setup\r\n\r\n1. Enroll a Fleet Server with a managed agent policy (e.g. by making\r\nsure the preconfigured agent policy for Fleet Server has `is_managed:\r\ntrue`).\r\n2. Create agent policy \"Agent policy 1\". In the agent policy settings,\r\nset the inactivity timeout to a low value, e.g. 10 seconds.\r\n3. Enroll 7 agents on agent policy \"Agent policy 1\" (e.g. with Horde).\r\nOnce they are enrolled, kill the agents: they will become inactive in\r\nFleet.\r\n4. Create agent policy \"Agent policy 2\". Enroll 7 agents on it.\r\n\r\n#### UI\r\n\r\n1. In the Agents table, change the filtering to include inactive status.\r\nYou should see 15 agents: 7 Healthy, 7 Inactive, 1 (Healthy) Fleet\r\nServer. The Fleet Server should not be manually selectable (managed\r\nagent policy).\r\n2. Select one inactive agent. In the Actions menu, the agent count\r\nshould be 1 and actions should be available. NB: the action to schedule\r\nan upgrade requires Platinum license, so it may be disabled.\r\n3. Manually select all agents: above the table, it should say `Showing\r\n15 agents | 14 agents selected`. In the Actions menu, the agent count\r\nshould be 14 and actions should be available.\r\n4. Change the number of rows per page to 5; select all agents on the\r\nfirst page and then click `Select everything on all pages` (bulk\r\nselection): above the table, it should say `Showing 15 agents | All\r\nagents selected`. In the Actions menu, the agent count should be 14 and\r\nactions should be available.\r\n5. Go to page 2, where 2 Healthy and 3 Inactive agents should be listed.\r\nBulk select all agents again. Change the filtering to exclude inactive\r\nstatus: there should be 3 remaining agents (2 Healthy and Fleet Server)\r\nand Fleet Server should not be selected. Above the table, it should say\r\n`Showing 8 agents | 2 agents selected`. In the Actions menu, the agent\r\ncount should be 2 and actions should be available.\r\n6. Change the filtering to include inactive status again: you should see\r\n2 selected Healthy agents and 3 unselected Inactive agents. Above the\r\ntable, it should say `Showing 15 agents | 2 agents selected`. In the\r\nActions menu, the agent count should be 2 and actions should be\r\navailable.\r\n\r\n#### Bulk agent actions\r\n\r\n1. Bulk select all 14 agents (7 Healthy, 7 Inactive) and, in the Actions\r\nmenu, click \"Upgrade 14 agents\". The upgrade should be kicked off for\r\nall agents. In the Agents Activity flyout, you should be able to follow\r\nthe upgrades for the 14 agents.\r\n2. Create a new agent policy \"Agent policy 3\". Bulk select all 14 agents\r\n(7 Healthy, 7 Inactive). In the Actions menu, click \"Assign to new\r\npolicy\" and select \"Agent policy 3\". All 14 agents should be reassigned\r\nto the new policy (NB: Inactive agents will get Offline status).\r\n3. Bulk select all 14 agents (7 Healthy, 7 Inactive) and, in the Actions\r\nmenu, click \"Unenroll 14 agents\". All agents should be unrenrolled.\r\n\r\n### Screen recording\r\n\r\nThe following recording shows the main UI fixes:\r\n- Bulk selection with inactive agents gets correct agent count\r\n- Changing the filtering in bulk selection mode changes to manual mode\r\n- Managed policy agent cannot be selected\r\n\r\n\r\ne52b225c
-2951-4729-8903-551fcc793068\r\n\r\n### Checklist\r\n\r\n- [ ]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas added for features that require explanation or tutorials\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>","sha":"7e8ee65c63ad9810da51ca75d6dbd96e560bc7cf","branchLabelMapping":{"^v8.14.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","Team:Fleet","backport:prev-minor","v8.14.0"],"title":"[Fleet] Actions menu for Fleet agents has correct agent count and bulk actions are correctly processed for all selected agents","number":177035,"url":"https://github.com/elastic/kibana/pull/177035","mergeCommit":{"message":"[Fleet] Actions menu for Fleet agents has correct agent count and bulk actions are correctly processed for all selected agents (#177035)\n\n## Summary\r\n\r\nCloses https://github.com/elastic/kibana/issues/167269\r\nCloses https://github.com/elastic/kibana/issues/171914\r\nCloses https://github.com/elastic/kibana/issues/167241\r\n\r\nChanges:\r\n- Agent count in `Actions` menu includes all selectable agents across\r\nall pages, including agents with inactive status.\r\n- `Actions` menu items are enabled if at least one agent is selected, no\r\nmatter its status.\r\n- Fix bug where managed agents could be accidentally selected in query\r\nmode when changing filtering.\r\n- Changing agent status or agent policy filtering while in bulk\r\nselection mode sets selection mode back to manual. This is to avoid a\r\nbad state where bulk selection mode is still enabled and more\r\n(unselected) agents are listed.\r\n- Fix the bulk selection query when some agents are excluded (managed\r\nagent policies).\r\n- Agent upgrades in bulk selection mode includes all selected agents,\r\nincluding agents with inactive status.\r\n- Agent policy reassign in bulk selection mode includes all selected\r\nagents, including agents with inactive status.\r\n\r\n### Steps for testing\r\n\r\nCf. screen recording below.\r\n\r\n#### Setup\r\n\r\n1. Enroll a Fleet Server with a managed agent policy (e.g. by making\r\nsure the preconfigured agent policy for Fleet Server has `is_managed:\r\ntrue`).\r\n2. Create agent policy \"Agent policy 1\". In the agent policy settings,\r\nset the inactivity timeout to a low value, e.g. 10 seconds.\r\n3. Enroll 7 agents on agent policy \"Agent policy 1\" (e.g. with Horde).\r\nOnce they are enrolled, kill the agents: they will become inactive in\r\nFleet.\r\n4. Create agent policy \"Agent policy 2\". Enroll 7 agents on it.\r\n\r\n#### UI\r\n\r\n1. In the Agents table, change the filtering to include inactive status.\r\nYou should see 15 agents: 7 Healthy, 7 Inactive, 1 (Healthy) Fleet\r\nServer. The Fleet Server should not be manually selectable (managed\r\nagent policy).\r\n2. Select one inactive agent. In the Actions menu, the agent count\r\nshould be 1 and actions should be available. NB: the action to schedule\r\nan upgrade requires Platinum license, so it may be disabled.\r\n3. Manually select all agents: above the table, it should say `Showing\r\n15 agents | 14 agents selected`. In the Actions menu, the agent count\r\nshould be 14 and actions should be available.\r\n4. Change the number of rows per page to 5; select all agents on the\r\nfirst page and then click `Select everything on all pages` (bulk\r\nselection): above the table, it should say `Showing 15 agents | All\r\nagents selected`. In the Actions menu, the agent count should be 14 and\r\nactions should be available.\r\n5. Go to page 2, where 2 Healthy and 3 Inactive agents should be listed.\r\nBulk select all agents again. Change the filtering to exclude inactive\r\nstatus: there should be 3 remaining agents (2 Healthy and Fleet Server)\r\nand Fleet Server should not be selected. Above the table, it should say\r\n`Showing 8 agents | 2 agents selected`. In the Actions menu, the agent\r\ncount should be 2 and actions should be available.\r\n6. Change the filtering to include inactive status again: you should see\r\n2 selected Healthy agents and 3 unselected Inactive agents. Above the\r\ntable, it should say `Showing 15 agents | 2 agents selected`. In the\r\nActions menu, the agent count should be 2 and actions should be\r\navailable.\r\n\r\n#### Bulk agent actions\r\n\r\n1. Bulk select all 14 agents (7 Healthy, 7 Inactive) and, in the Actions\r\nmenu, click \"Upgrade 14 agents\". The upgrade should be kicked off for\r\nall agents. In the Agents Activity flyout, you should be able to follow\r\nthe upgrades for the 14 agents.\r\n2. Create a new agent policy \"Agent policy 3\". Bulk select all 14 agents\r\n(7 Healthy, 7 Inactive). In the Actions menu, click \"Assign to new\r\npolicy\" and select \"Agent policy 3\". All 14 agents should be reassigned\r\nto the new policy (NB: Inactive agents will get Offline status).\r\n3. Bulk select all 14 agents (7 Healthy, 7 Inactive) and, in the Actions\r\nmenu, click \"Unenroll 14 agents\". All agents should be unrenrolled.\r\n\r\n### Screen recording\r\n\r\nThe following recording shows the main UI fixes:\r\n- Bulk selection with inactive agents gets correct agent count\r\n- Changing the filtering in bulk selection mode changes to manual mode\r\n- Managed policy agent cannot be selected\r\n\r\n\r\ne52b225c
-2951-4729-8903-551fcc793068\r\n\r\n### Checklist\r\n\r\n- [ ]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas added for features that require explanation or tutorials\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>","sha":"7e8ee65c63ad9810da51ca75d6dbd96e560bc7cf"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v8.14.0","branchLabelMappingKey":"^v8.14.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/177035","number":177035,"mergeCommit":{"message":"[Fleet] Actions menu for Fleet agents has correct agent count and bulk actions are correctly processed for all selected agents (#177035)\n\n## Summary\r\n\r\nCloses https://github.com/elastic/kibana/issues/167269\r\nCloses https://github.com/elastic/kibana/issues/171914\r\nCloses https://github.com/elastic/kibana/issues/167241\r\n\r\nChanges:\r\n- Agent count in `Actions` menu includes all selectable agents across\r\nall pages, including agents with inactive status.\r\n- `Actions` menu items are enabled if at least one agent is selected, no\r\nmatter its status.\r\n- Fix bug where managed agents could be accidentally selected in query\r\nmode when changing filtering.\r\n- Changing agent status or agent policy filtering while in bulk\r\nselection mode sets selection mode back to manual. This is to avoid a\r\nbad state where bulk selection mode is still enabled and more\r\n(unselected) agents are listed.\r\n- Fix the bulk selection query when some agents are excluded (managed\r\nagent policies).\r\n- Agent upgrades in bulk selection mode includes all selected agents,\r\nincluding agents with inactive status.\r\n- Agent policy reassign in bulk selection mode includes all selected\r\nagents, including agents with inactive status.\r\n\r\n### Steps for testing\r\n\r\nCf. screen recording below.\r\n\r\n#### Setup\r\n\r\n1. Enroll a Fleet Server with a managed agent policy (e.g. by making\r\nsure the preconfigured agent policy for Fleet Server has `is_managed:\r\ntrue`).\r\n2. Create agent policy \"Agent policy 1\". In the agent policy settings,\r\nset the inactivity timeout to a low value, e.g. 10 seconds.\r\n3. Enroll 7 agents on agent policy \"Agent policy 1\" (e.g. with Horde).\r\nOnce they are enrolled, kill the agents: they will become inactive in\r\nFleet.\r\n4. Create agent policy \"Agent policy 2\". Enroll 7 agents on it.\r\n\r\n#### UI\r\n\r\n1. In the Agents table, change the filtering to include inactive status.\r\nYou should see 15 agents: 7 Healthy, 7 Inactive, 1 (Healthy) Fleet\r\nServer. The Fleet Server should not be manually selectable (managed\r\nagent policy).\r\n2. Select one inactive agent. In the Actions menu, the agent count\r\nshould be 1 and actions should be available. NB: the action to schedule\r\nan upgrade requires Platinum license, so it may be disabled.\r\n3. Manually select all agents: above the table, it should say `Showing\r\n15 agents | 14 agents selected`. In the Actions menu, the agent count\r\nshould be 14 and actions should be available.\r\n4. Change the number of rows per page to 5; select all agents on the\r\nfirst page and then click `Select everything on all pages` (bulk\r\nselection): above the table, it should say `Showing 15 agents | All\r\nagents selected`. In the Actions menu, the agent count should be 14 and\r\nactions should be available.\r\n5. Go to page 2, where 2 Healthy and 3 Inactive agents should be listed.\r\nBulk select all agents again. Change the filtering to exclude inactive\r\nstatus: there should be 3 remaining agents (2 Healthy and Fleet Server)\r\nand Fleet Server should not be selected. Above the table, it should say\r\n`Showing 8 agents | 2 agents selected`. In the Actions menu, the agent\r\ncount should be 2 and actions should be available.\r\n6. Change the filtering to include inactive status again: you should see\r\n2 selected Healthy agents and 3 unselected Inactive agents. Above the\r\ntable, it should say `Showing 15 agents | 2 agents selected`. In the\r\nActions menu, the agent count should be 2 and actions should be\r\navailable.\r\n\r\n#### Bulk agent actions\r\n\r\n1. Bulk select all 14 agents (7 Healthy, 7 Inactive) and, in the Actions\r\nmenu, click \"Upgrade 14 agents\". The upgrade should be kicked off for\r\nall agents. In the Agents Activity flyout, you should be able to follow\r\nthe upgrades for the 14 agents.\r\n2. Create a new agent policy \"Agent policy 3\". Bulk select all 14 agents\r\n(7 Healthy, 7 Inactive). In the Actions menu, click \"Assign to new\r\npolicy\" and select \"Agent policy 3\". All 14 agents should be reassigned\r\nto the new policy (NB: Inactive agents will get Offline status).\r\n3. Bulk select all 14 agents (7 Healthy, 7 Inactive) and, in the Actions\r\nmenu, click \"Unenroll 14 agents\". All agents should be unrenrolled.\r\n\r\n### Screen recording\r\n\r\nThe following recording shows the main UI fixes:\r\n- Bulk selection with inactive agents gets correct agent count\r\n- Changing the filtering in bulk selection mode changes to manual mode\r\n- Managed policy agent cannot be selected\r\n\r\n\r\ne52b225c
-2951-4729-8903-551fcc793068\r\n\r\n### Checklist\r\n\r\n- [ ]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas added for features that require explanation or tutorials\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>","sha":"7e8ee65c63ad9810da51ca75d6dbd96e560bc7cf"}}]}] BACKPORT--> Co-authored-by: Jill Guyonnet <jill.guyonnet@elastic.co>
This commit is contained in:
parent
24e7c9ab38
commit
01e897bb0d
16 changed files with 133 additions and 216 deletions
|
@ -114,6 +114,7 @@ export interface PostBulkAgentUpgradeRequest {
|
|||
rollout_duration_seconds?: number;
|
||||
start_time?: string;
|
||||
force?: boolean;
|
||||
includeInactive?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -147,6 +148,7 @@ export interface PostBulkAgentReassignRequest {
|
|||
policy_id: string;
|
||||
agents: string[] | string;
|
||||
batchSize?: number;
|
||||
includeInactive?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -185,6 +187,7 @@ export interface PostBulkUpdateAgentTagsRequest {
|
|||
agents: string[] | string;
|
||||
tagsToAdd?: string[];
|
||||
tagsToRemove?: string[];
|
||||
includeInactive?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -12,29 +12,29 @@ import { fireEvent, act } from '@testing-library/react';
|
|||
import type { Agent } from '../../../../types';
|
||||
|
||||
import { createFleetTestRendererMock } from '../../../../../../mock';
|
||||
import type { LicenseService } from '../../../../services';
|
||||
import { ExperimentalFeaturesService } from '../../../../services';
|
||||
import { AgentReassignAgentPolicyModal } from '../../components/agent_reassign_policy_modal';
|
||||
|
||||
import { useLicense } from '../../../../../../hooks/use_license';
|
||||
|
||||
import { AgentBulkActions } from './bulk_actions';
|
||||
|
||||
jest.mock('../../../../../../services/experimental_features');
|
||||
const mockedExperimentalFeaturesService = jest.mocked(ExperimentalFeaturesService);
|
||||
|
||||
jest.mock('../../../../hooks', () => ({
|
||||
...jest.requireActual('../../../../hooks'),
|
||||
}));
|
||||
jest.mock('../../../../../../hooks/use_license');
|
||||
const mockedUseLicence = useLicense as jest.MockedFunction<typeof useLicense>;
|
||||
|
||||
jest.mock('../../components/agent_reassign_policy_modal');
|
||||
|
||||
const defaultProps = {
|
||||
shownAgents: 10,
|
||||
inactiveShownAgents: 0,
|
||||
nAgentsInTable: 10,
|
||||
totalManagedAgentIds: [],
|
||||
inactiveManagedAgentIds: [],
|
||||
selectionMode: 'manual',
|
||||
currentQuery: '',
|
||||
selectedAgents: [],
|
||||
visibleAgents: [],
|
||||
agentsOnCurrentPage: [],
|
||||
refreshAgents: () => undefined,
|
||||
allTags: [],
|
||||
agentPolicies: [],
|
||||
|
@ -43,50 +43,28 @@ const defaultProps = {
|
|||
describe('AgentBulkActions', () => {
|
||||
beforeAll(() => {
|
||||
mockedExperimentalFeaturesService.get.mockReturnValue({
|
||||
diagnosticFileUploadEnabled: false,
|
||||
diagnosticFileUploadEnabled: true,
|
||||
} as any);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mockedUseLicence.mockReturnValue({
|
||||
hasAtLeast: () => false,
|
||||
} as unknown as LicenseService);
|
||||
jest.mocked(AgentReassignAgentPolicyModal).mockReset();
|
||||
jest.mocked(AgentReassignAgentPolicyModal).mockReturnValue(null);
|
||||
});
|
||||
|
||||
function render(props: any) {
|
||||
const renderer = createFleetTestRendererMock();
|
||||
|
||||
return renderer.render(<AgentBulkActions {...props} />);
|
||||
}
|
||||
|
||||
describe('When in manual mode', () => {
|
||||
it('should show only disabled actions if no agents are active', async () => {
|
||||
describe('When in manual selection mode', () => {
|
||||
it('should show the available actions for the selected agents', async () => {
|
||||
const results = render({
|
||||
...defaultProps,
|
||||
inactiveShownAgents: 10,
|
||||
selectedAgents: [{ id: 'agent1' }, { id: 'agent2' }] as Agent[],
|
||||
});
|
||||
|
||||
const bulkActionsButton = results.getByTestId('agentBulkActionsButton');
|
||||
await act(async () => {
|
||||
fireEvent.click(bulkActionsButton);
|
||||
});
|
||||
|
||||
expect(results.getByText('Add / remove tags').closest('button')!).toBeDisabled();
|
||||
expect(results.getByText('Assign to new policy').closest('button')!).toBeDisabled();
|
||||
expect(results.getByText('Unenroll 2 agents').closest('button')!).toBeDisabled();
|
||||
expect(results.getByText('Upgrade 2 agents').closest('button')!).toBeDisabled();
|
||||
expect(results.getByText('Schedule upgrade for 2 agents').closest('button')!).toBeDisabled();
|
||||
expect(results.queryByText('Request diagnostics for 2 agents')).toBeNull();
|
||||
expect(results.getByText('Restart upgrade 2 agents').closest('button')!).toBeDisabled();
|
||||
});
|
||||
|
||||
it('should show available actions for 2 selected agents if they are active', async () => {
|
||||
const results = render({
|
||||
...defaultProps,
|
||||
selectedAgents: [
|
||||
{ id: 'agent1', tags: ['oldTag'], active: true },
|
||||
{ id: 'agent2', active: true },
|
||||
] as Agent[],
|
||||
selectedAgents: [{ id: 'agent1', tags: ['oldTag'] }, { id: 'agent2' }] as Agent[],
|
||||
});
|
||||
|
||||
const bulkActionsButton = results.getByTestId('agentBulkActionsButton');
|
||||
|
@ -100,19 +78,19 @@ describe('AgentBulkActions', () => {
|
|||
expect(results.getByText('Upgrade 2 agents').closest('button')!).toBeEnabled();
|
||||
expect(results.getByText('Schedule upgrade for 2 agents').closest('button')!).toBeDisabled();
|
||||
expect(results.getByText('Restart upgrade 2 agents').closest('button')!).toBeEnabled();
|
||||
expect(
|
||||
results.getByText('Request diagnostics for 2 agents').closest('button')!
|
||||
).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should add actions if mockedExperimentalFeaturesService is enabled', async () => {
|
||||
mockedExperimentalFeaturesService.get.mockReturnValue({
|
||||
diagnosticFileUploadEnabled: true,
|
||||
} as any);
|
||||
it('should allow scheduled upgrades if the license allows it', async () => {
|
||||
mockedUseLicence.mockReturnValue({
|
||||
hasAtLeast: () => true,
|
||||
} as unknown as LicenseService);
|
||||
|
||||
const results = render({
|
||||
...defaultProps,
|
||||
selectedAgents: [
|
||||
{ id: 'agent1', tags: ['oldTag'], active: true },
|
||||
{ id: 'agent2', active: true },
|
||||
] as Agent[],
|
||||
selectedAgents: [{ id: 'agent1', tags: ['oldTag'] }, { id: 'agent2' }] as Agent[],
|
||||
});
|
||||
|
||||
const bulkActionsButton = results.getByTestId('agentBulkActionsButton');
|
||||
|
@ -120,18 +98,12 @@ describe('AgentBulkActions', () => {
|
|||
fireEvent.click(bulkActionsButton);
|
||||
});
|
||||
|
||||
expect(
|
||||
results.getByText('Request diagnostics for 2 agents').closest('button')!
|
||||
).toBeEnabled();
|
||||
expect(results.getByText('Schedule upgrade for 2 agents').closest('button')!).toBeEnabled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('When in query mode', () => {
|
||||
mockedExperimentalFeaturesService.get.mockReturnValue({
|
||||
diagnosticFileUploadEnabled: true,
|
||||
} as any);
|
||||
|
||||
it('should show correct actions for active agents when no managed policies exist', async () => {
|
||||
describe('When in query selection mode', () => {
|
||||
it('should show the available actions for all agents when no managed agents are listed', async () => {
|
||||
const results = render({
|
||||
...defaultProps,
|
||||
selectionMode: 'query',
|
||||
|
@ -153,7 +125,7 @@ describe('AgentBulkActions', () => {
|
|||
expect(results.getByText('Restart upgrade 10 agents').closest('button')!).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should show correct actions for the active agents and exclude the managed agents from the count', async () => {
|
||||
it('should show the available actions for all agents except managed agents', async () => {
|
||||
const results = render({
|
||||
...defaultProps,
|
||||
totalManagedAgentIds: ['agentId1', 'agentId2'],
|
||||
|
@ -176,49 +148,7 @@ describe('AgentBulkActions', () => {
|
|||
expect(results.getByText('Restart upgrade 8 agents').closest('button')!).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should show correct actions also when there are inactive managed agents', async () => {
|
||||
const results = render({
|
||||
...defaultProps,
|
||||
inactiveManagedAgentIds: ['agentId1', 'agentId2'],
|
||||
totalManagedAgentIds: ['agentId1', 'agentId2', 'agentId3'],
|
||||
selectionMode: 'query',
|
||||
});
|
||||
|
||||
const bulkActionsButton = results.getByTestId('agentBulkActionsButton');
|
||||
await act(async () => {
|
||||
fireEvent.click(bulkActionsButton);
|
||||
});
|
||||
|
||||
expect(results.getByText('Add / remove tags').closest('button')!).toBeEnabled();
|
||||
expect(results.getByText('Assign to new policy').closest('button')!).toBeEnabled();
|
||||
expect(results.getByText('Unenroll 9 agents').closest('button')!).toBeEnabled();
|
||||
expect(results.getByText('Upgrade 9 agents').closest('button')!).toBeEnabled();
|
||||
expect(results.getByText('Schedule upgrade for 9 agents').closest('button')!).toBeDisabled();
|
||||
expect(results.getByText('Restart upgrade 9 agents').closest('button')!).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should show disabled actions when only inactive agents are selected', async () => {
|
||||
const results = render({
|
||||
...defaultProps,
|
||||
inactiveShownAgents: 10,
|
||||
selectedAgents: [{ id: 'agent1' }, { id: 'agent2' }] as Agent[],
|
||||
selectionMode: 'query',
|
||||
});
|
||||
|
||||
const bulkActionsButton = results.getByTestId('agentBulkActionsButton');
|
||||
await act(async () => {
|
||||
fireEvent.click(bulkActionsButton);
|
||||
});
|
||||
|
||||
expect(results.getByText('Add / remove tags').closest('button')!).toBeDisabled();
|
||||
expect(results.getByText('Assign to new policy').closest('button')!).toBeDisabled();
|
||||
expect(results.getByText('Unenroll 0 agents').closest('button')!).toBeDisabled();
|
||||
expect(results.getByText('Upgrade 0 agents').closest('button')!).toBeDisabled();
|
||||
expect(results.getByText('Schedule upgrade for 0 agents').closest('button')!).toBeDisabled();
|
||||
expect(results.getByText('Restart upgrade 0 agents').closest('button')!).toBeDisabled();
|
||||
});
|
||||
|
||||
it('should generate a correct kuery to select agents', async () => {
|
||||
it('should generate a correct kuery to select agents when no managed agents are listed', async () => {
|
||||
const results = render({
|
||||
...defaultProps,
|
||||
selectionMode: 'query',
|
||||
|
@ -243,7 +173,7 @@ describe('AgentBulkActions', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should generate a correct kuery to select agents with managed agents too', async () => {
|
||||
it('should generate a correct kuery that excludes managed agents', async () => {
|
||||
const results = render({
|
||||
...defaultProps,
|
||||
totalManagedAgentIds: ['agentId1', 'agentId2'],
|
||||
|
@ -263,7 +193,7 @@ describe('AgentBulkActions', () => {
|
|||
|
||||
expect(jest.mocked(AgentReassignAgentPolicyModal)).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
agents: '(Base query) AND NOT (fleet-agents.agent.id : ("agentId1" or "agentId2"))',
|
||||
agents: '((Base query)) AND NOT (fleet-agents.agent.id : ("agentId1" or "agentId2"))',
|
||||
}),
|
||||
expect.anything()
|
||||
);
|
||||
|
|
|
@ -35,28 +35,24 @@ import type { SelectionMode } from './types';
|
|||
import { TagsAddRemove } from './tags_add_remove';
|
||||
|
||||
export interface Props {
|
||||
shownAgents: number;
|
||||
inactiveShownAgents: number;
|
||||
nAgentsInTable: number;
|
||||
totalManagedAgentIds: string[];
|
||||
inactiveManagedAgentIds: string[];
|
||||
selectionMode: SelectionMode;
|
||||
currentQuery: string;
|
||||
selectedAgents: Agent[];
|
||||
visibleAgents: Agent[];
|
||||
agentsOnCurrentPage: Agent[];
|
||||
refreshAgents: (args?: { refreshTags?: boolean }) => void;
|
||||
allTags: string[];
|
||||
agentPolicies: AgentPolicy[];
|
||||
}
|
||||
|
||||
export const AgentBulkActions: React.FunctionComponent<Props> = ({
|
||||
shownAgents,
|
||||
inactiveShownAgents,
|
||||
nAgentsInTable,
|
||||
totalManagedAgentIds,
|
||||
inactiveManagedAgentIds,
|
||||
selectionMode,
|
||||
currentQuery,
|
||||
selectedAgents,
|
||||
visibleAgents,
|
||||
agentsOnCurrentPage,
|
||||
refreshAgents,
|
||||
allTags,
|
||||
agentPolicies,
|
||||
|
@ -87,26 +83,17 @@ export const AgentBulkActions: React.FunctionComponent<Props> = ({
|
|||
const excludedKuery = `${AGENTS_PREFIX}.agent.id : (${totalManagedAgentIds
|
||||
.map((id) => `"${id}"`)
|
||||
.join(' or ')})`;
|
||||
return `${currentQuery} AND NOT (${excludedKuery})`;
|
||||
return `(${currentQuery}) AND NOT (${excludedKuery})`;
|
||||
} else {
|
||||
return currentQuery;
|
||||
}
|
||||
}, [currentQuery, totalManagedAgentIds]);
|
||||
|
||||
const totalActiveAgents = shownAgents - inactiveShownAgents;
|
||||
|
||||
// exclude inactive agents from the count
|
||||
const agents = selectionMode === 'manual' ? selectedAgents : selectionQuery;
|
||||
const agentCount =
|
||||
selectionMode === 'manual'
|
||||
? selectedAgents.length
|
||||
: totalActiveAgents - (totalManagedAgentIds?.length - inactiveManagedAgentIds?.length);
|
||||
|
||||
// Check if user is working with only inactive agents
|
||||
const atLeastOneActiveAgentSelected =
|
||||
selectionMode === 'manual'
|
||||
? !!selectedAgents.find((agent) => agent.active)
|
||||
: shownAgents > inactiveShownAgents;
|
||||
const agents = selectionMode === 'manual' ? selectedAgents : selectionQuery;
|
||||
: nAgentsInTable - totalManagedAgentIds?.length;
|
||||
|
||||
const [tagsPopoverButton, setTagsPopoverButton] = useState<HTMLElement>();
|
||||
const { diagnosticFileUploadEnabled } = ExperimentalFeaturesService.get();
|
||||
|
@ -121,7 +108,6 @@ export const AgentBulkActions: React.FunctionComponent<Props> = ({
|
|||
/>
|
||||
),
|
||||
icon: <EuiIcon type="tag" size="m" />,
|
||||
disabled: !atLeastOneActiveAgentSelected,
|
||||
onClick: (event: any) => {
|
||||
setTagsPopoverButton((event.target as Element).closest('button')!);
|
||||
setIsTagAddVisible(!isTagAddVisible);
|
||||
|
@ -136,7 +122,6 @@ export const AgentBulkActions: React.FunctionComponent<Props> = ({
|
|||
/>
|
||||
),
|
||||
icon: <EuiIcon type="pencil" size="m" />,
|
||||
disabled: !atLeastOneActiveAgentSelected,
|
||||
onClick: () => {
|
||||
closeMenu();
|
||||
setIsReassignFlyoutOpen(true);
|
||||
|
@ -154,7 +139,6 @@ export const AgentBulkActions: React.FunctionComponent<Props> = ({
|
|||
/>
|
||||
),
|
||||
icon: <EuiIcon type="trash" size="m" />,
|
||||
disabled: !atLeastOneActiveAgentSelected,
|
||||
onClick: () => {
|
||||
closeMenu();
|
||||
setIsUnenrollModalOpen(true);
|
||||
|
@ -172,7 +156,6 @@ export const AgentBulkActions: React.FunctionComponent<Props> = ({
|
|||
/>
|
||||
),
|
||||
icon: <EuiIcon type="refresh" size="m" />,
|
||||
disabled: !atLeastOneActiveAgentSelected,
|
||||
onClick: () => {
|
||||
closeMenu();
|
||||
setUpgradeModalState({ isOpen: true, isScheduled: false, isUpdating: false });
|
||||
|
@ -190,7 +173,7 @@ export const AgentBulkActions: React.FunctionComponent<Props> = ({
|
|||
/>
|
||||
),
|
||||
icon: <EuiIcon type="timeRefresh" size="m" />,
|
||||
disabled: !atLeastOneActiveAgentSelected || !isLicenceAllowingScheduleUpgrade,
|
||||
disabled: !isLicenceAllowingScheduleUpgrade,
|
||||
onClick: () => {
|
||||
closeMenu();
|
||||
setUpgradeModalState({ isOpen: true, isScheduled: true, isUpdating: false });
|
||||
|
@ -210,7 +193,6 @@ export const AgentBulkActions: React.FunctionComponent<Props> = ({
|
|||
/>
|
||||
),
|
||||
icon: <EuiIcon type="refresh" size="m" />,
|
||||
disabled: !atLeastOneActiveAgentSelected,
|
||||
onClick: () => {
|
||||
closeMenu();
|
||||
setUpgradeModalState({ isOpen: true, isScheduled: false, isUpdating: true });
|
||||
|
@ -230,7 +212,6 @@ export const AgentBulkActions: React.FunctionComponent<Props> = ({
|
|||
/>
|
||||
),
|
||||
icon: <EuiIcon type="download" size="m" />,
|
||||
disabled: !atLeastOneActiveAgentSelected,
|
||||
onClick: () => {
|
||||
closeMenu();
|
||||
setIsRequestDiagnosticsModalOpen(true);
|
||||
|
@ -246,8 +227,8 @@ export const AgentBulkActions: React.FunctionComponent<Props> = ({
|
|||
];
|
||||
|
||||
const getSelectedTagsFromAgents = useMemo(
|
||||
() => getCommonTags(agents, visibleAgents ?? [], agentPolicies),
|
||||
[agents, visibleAgents, agentPolicies]
|
||||
() => getCommonTags(agents, agentsOnCurrentPage ?? [], agentPolicies),
|
||||
[agents, agentsOnCurrentPage, agentPolicies]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -45,15 +45,14 @@ describe('SearchAndFilterBar', () => {
|
|||
it('should show no Actions button when no agent is selected', async () => {
|
||||
const selectedAgents: Agent[] = [];
|
||||
const props: any = {
|
||||
shownAgents: 10,
|
||||
inactiveShownAgents: 0,
|
||||
nAgentsInTable: 10,
|
||||
totalInactiveAgents: 2,
|
||||
totalManagedAgentIds: [],
|
||||
selectionMode: 'manual',
|
||||
currentQuery: '',
|
||||
selectedAgents,
|
||||
refreshAgents: () => undefined,
|
||||
visibleAgents: [],
|
||||
agentsOnCurrentPage: [],
|
||||
tags: [],
|
||||
agentPolicies: [],
|
||||
selectedStatus: [],
|
||||
|
@ -79,15 +78,14 @@ describe('SearchAndFilterBar', () => {
|
|||
},
|
||||
];
|
||||
const props: any = {
|
||||
shownAgents: 10,
|
||||
inactiveShownAgents: 0,
|
||||
nAgentsInTable: 10,
|
||||
totalInactiveAgents: 2,
|
||||
totalManagedAgentIds: [],
|
||||
selectionMode: 'manual',
|
||||
currentQuery: '',
|
||||
selectedAgents,
|
||||
refreshAgents: () => undefined,
|
||||
visibleAgents: [],
|
||||
agentsOnCurrentPage: [],
|
||||
tags: [],
|
||||
agentPolicies: [],
|
||||
selectedStatus: [],
|
||||
|
@ -101,15 +99,14 @@ describe('SearchAndFilterBar', () => {
|
|||
|
||||
it('should show an Actions button when agents selected in query mode', async () => {
|
||||
const props: any = {
|
||||
shownAgents: 10,
|
||||
inactiveShownAgents: 0,
|
||||
nAgentsInTable: 10,
|
||||
totalInactiveAgents: 2,
|
||||
totalManagedAgentIds: [],
|
||||
selectionMode: 'query',
|
||||
currentQuery: '',
|
||||
selectedAgents: [],
|
||||
refreshAgents: () => undefined,
|
||||
visibleAgents: [],
|
||||
agentsOnCurrentPage: [],
|
||||
tags: [],
|
||||
agentPolicies: [],
|
||||
selectedStatus: [],
|
||||
|
|
|
@ -46,18 +46,16 @@ export interface SearchAndFilterBarProps {
|
|||
tags: string[];
|
||||
selectedTags: string[];
|
||||
onSelectedTagsChange: (selectedTags: string[]) => void;
|
||||
shownAgents: number;
|
||||
inactiveShownAgents: number;
|
||||
nAgentsInTable: number;
|
||||
totalInactiveAgents: number;
|
||||
totalManagedAgentIds: string[];
|
||||
inactiveManagedAgentIds: string[];
|
||||
selectionMode: SelectionMode;
|
||||
currentQuery: string;
|
||||
selectedAgents: Agent[];
|
||||
refreshAgents: (args?: { refreshTags?: boolean }) => void;
|
||||
onClickAddAgent: () => void;
|
||||
onClickAddFleetServer: () => void;
|
||||
visibleAgents: Agent[];
|
||||
agentsOnCurrentPage: Agent[];
|
||||
onClickAgentActivity: () => void;
|
||||
showAgentActivityTour: { isOpen: boolean };
|
||||
}
|
||||
|
@ -76,18 +74,16 @@ export const SearchAndFilterBar: React.FunctionComponent<SearchAndFilterBarProps
|
|||
tags,
|
||||
selectedTags,
|
||||
onSelectedTagsChange,
|
||||
shownAgents,
|
||||
inactiveShownAgents,
|
||||
nAgentsInTable,
|
||||
totalInactiveAgents,
|
||||
totalManagedAgentIds,
|
||||
inactiveManagedAgentIds,
|
||||
selectionMode,
|
||||
currentQuery,
|
||||
selectedAgents,
|
||||
refreshAgents,
|
||||
onClickAddAgent,
|
||||
onClickAddFleetServer,
|
||||
visibleAgents,
|
||||
agentsOnCurrentPage,
|
||||
onClickAgentActivity,
|
||||
showAgentActivityTour,
|
||||
}) => {
|
||||
|
@ -198,17 +194,15 @@ export const SearchAndFilterBar: React.FunctionComponent<SearchAndFilterBarProps
|
|||
</EuiFilterGroup>
|
||||
</EuiFlexItem>
|
||||
{(selectionMode === 'manual' && selectedAgents.length) ||
|
||||
(selectionMode === 'query' && shownAgents > 0) ? (
|
||||
(selectionMode === 'query' && nAgentsInTable > 0) ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<AgentBulkActions
|
||||
shownAgents={shownAgents}
|
||||
inactiveShownAgents={inactiveShownAgents}
|
||||
nAgentsInTable={nAgentsInTable}
|
||||
totalManagedAgentIds={totalManagedAgentIds}
|
||||
inactiveManagedAgentIds={inactiveManagedAgentIds}
|
||||
selectionMode={selectionMode}
|
||||
currentQuery={currentQuery}
|
||||
selectedAgents={selectedAgents}
|
||||
visibleAgents={visibleAgents}
|
||||
agentsOnCurrentPage={agentsOnCurrentPage}
|
||||
refreshAgents={refreshAgents}
|
||||
allTags={tags}
|
||||
agentPolicies={agentPolicies}
|
||||
|
|
|
@ -100,11 +100,9 @@ export function useFetchAgentsData() {
|
|||
>();
|
||||
const [allTags, setAllTags] = useState<string[]>();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [shownAgents, setShownAgents] = useState(0);
|
||||
const [inactiveShownAgents, setInactiveShownAgents] = useState(0);
|
||||
const [nAgentsInTable, setNAgentsInTable] = useState(0);
|
||||
const [totalInactiveAgents, setTotalInactiveAgents] = useState(0);
|
||||
const [totalManagedAgentIds, setTotalManagedAgentIds] = useState<string[]>([]);
|
||||
const [inactiveManagedAgentIds, setinactiveManagedAgentIds] = useState<string[]>([]);
|
||||
const [managedAgentsOnCurrentPage, setManagedAgentsOnCurrentPage] = useState(0);
|
||||
|
||||
const getSortFieldForAPI = (field: keyof Agent): string => {
|
||||
|
@ -201,11 +199,8 @@ export function useFetchAgentsData() {
|
|||
}
|
||||
|
||||
setAgentsOnCurrentPage(agentsResponse.data.items);
|
||||
setShownAgents(agentsResponse.data.total);
|
||||
setNAgentsInTable(agentsResponse.data.total);
|
||||
setTotalInactiveAgents(totalInactiveAgentsResponse.data.results.inactive || 0);
|
||||
setInactiveShownAgents(
|
||||
showInactive ? totalInactiveAgentsResponse.data.results.inactive || 0 : 0
|
||||
);
|
||||
|
||||
const managedAgentPolicies = managedAgentPoliciesResponse.data?.items ?? [];
|
||||
|
||||
|
@ -227,11 +222,7 @@ export function useFetchAgentsData() {
|
|||
}
|
||||
const allManagedAgents = response.data?.items ?? [];
|
||||
const allManagedAgentIds = allManagedAgents?.map((agent) => agent.id);
|
||||
const inactiveManagedIds = allManagedAgents
|
||||
?.filter((agent) => agent.status === 'inactive')
|
||||
.map((agent) => agent.id);
|
||||
setTotalManagedAgentIds(allManagedAgentIds);
|
||||
setinactiveManagedAgentIds(inactiveManagedIds);
|
||||
|
||||
setManagedAgentsOnCurrentPage(
|
||||
agentsResponse.data.items
|
||||
|
@ -298,11 +289,9 @@ export function useFetchAgentsData() {
|
|||
agentsOnCurrentPage,
|
||||
agentsStatus,
|
||||
isLoading,
|
||||
shownAgents,
|
||||
inactiveShownAgents,
|
||||
nAgentsInTable,
|
||||
totalInactiveAgents,
|
||||
totalManagedAgentIds,
|
||||
inactiveManagedAgentIds,
|
||||
managedAgentsOnCurrentPage,
|
||||
showUpgradeable,
|
||||
setShowUpgradeable,
|
||||
|
|
|
@ -79,7 +79,13 @@ export const useUpdateTags = () => {
|
|||
errorMessage?: string
|
||||
) => {
|
||||
await wrapRequest(
|
||||
async () => await sendPostBulkAgentTagsUpdate({ agents, tagsToAdd, tagsToRemove }),
|
||||
async () =>
|
||||
await sendPostBulkAgentTagsUpdate({
|
||||
agents,
|
||||
tagsToAdd,
|
||||
tagsToRemove,
|
||||
includeInactive: true,
|
||||
}),
|
||||
onSuccess,
|
||||
successMessage,
|
||||
errorMessage
|
||||
|
|
|
@ -49,16 +49,39 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
|
|||
const [selectedAgents, setSelectedAgents] = useState<Agent[]>([]);
|
||||
const [selectionMode, setSelectionMode] = useState<SelectionMode>('manual');
|
||||
|
||||
// Agent enrollment flyout state
|
||||
const [enrollmentFlyout, setEnrollmentFlyoutState] = useState<{
|
||||
isOpen: boolean;
|
||||
selectedPolicyId?: string;
|
||||
}>({
|
||||
isOpen: false,
|
||||
});
|
||||
const [isAgentActivityFlyoutOpen, setAgentActivityFlyoutOpen] = useState(false);
|
||||
const flyoutContext = useFlyoutContext();
|
||||
|
||||
// Agent actions states
|
||||
const [agentToReassign, setAgentToReassign] = useState<Agent | undefined>(undefined);
|
||||
const [agentToUnenroll, setAgentToUnenroll] = useState<Agent | undefined>(undefined);
|
||||
const [agentToGetUninstallCommand, setAgentToGetUninstallCommand] = useState<Agent | undefined>(
|
||||
undefined
|
||||
);
|
||||
const [agentToUpgrade, setAgentToUpgrade] = useState<Agent | undefined>(undefined);
|
||||
const [agentToAddRemoveTags, setAgentToAddRemoveTags] = useState<Agent | undefined>(undefined);
|
||||
const [tagsPopoverButton, setTagsPopoverButton] = useState<HTMLElement>();
|
||||
const [showTagsAddRemove, setShowTagsAddRemove] = useState(false);
|
||||
const [agentToRequestDiagnostics, setAgentToRequestDiagnostics] = useState<Agent | undefined>(
|
||||
undefined
|
||||
);
|
||||
const [showAgentActivityTour, setShowAgentActivityTour] = useState({ isOpen: false });
|
||||
|
||||
const {
|
||||
allTags,
|
||||
agentsOnCurrentPage,
|
||||
agentsStatus,
|
||||
isLoading,
|
||||
shownAgents,
|
||||
inactiveShownAgents,
|
||||
nAgentsInTable,
|
||||
totalInactiveAgents,
|
||||
totalManagedAgentIds,
|
||||
inactiveManagedAgentIds,
|
||||
managedAgentsOnCurrentPage,
|
||||
showUpgradeable,
|
||||
setShowUpgradeable,
|
||||
|
@ -122,31 +145,6 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
|
|||
setShowUpgradeable,
|
||||
]);
|
||||
|
||||
// Agent enrollment flyout state
|
||||
const [enrollmentFlyout, setEnrollmentFlyoutState] = useState<{
|
||||
isOpen: boolean;
|
||||
selectedPolicyId?: string;
|
||||
}>({
|
||||
isOpen: false,
|
||||
});
|
||||
const [isAgentActivityFlyoutOpen, setAgentActivityFlyoutOpen] = useState(false);
|
||||
const flyoutContext = useFlyoutContext();
|
||||
|
||||
// Agent actions states
|
||||
const [agentToReassign, setAgentToReassign] = useState<Agent | undefined>(undefined);
|
||||
const [agentToUnenroll, setAgentToUnenroll] = useState<Agent | undefined>(undefined);
|
||||
const [agentToGetUninstallCommand, setAgentToGetUninstallCommand] = useState<Agent | undefined>(
|
||||
undefined
|
||||
);
|
||||
const [agentToUpgrade, setAgentToUpgrade] = useState<Agent | undefined>(undefined);
|
||||
const [agentToAddRemoveTags, setAgentToAddRemoveTags] = useState<Agent | undefined>(undefined);
|
||||
const [tagsPopoverButton, setTagsPopoverButton] = useState<HTMLElement>();
|
||||
const [showTagsAddRemove, setShowTagsAddRemove] = useState(false);
|
||||
const [agentToRequestDiagnostics, setAgentToRequestDiagnostics] = useState<Agent | undefined>(
|
||||
undefined
|
||||
);
|
||||
const [showAgentActivityTour, setShowAgentActivityTour] = useState({ isOpen: false });
|
||||
|
||||
const onTableChange = ({
|
||||
page,
|
||||
sort,
|
||||
|
@ -213,7 +211,7 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
|
|||
differenceBy(selectedAgents, agentsOnCurrentPage, 'id').length === 0;
|
||||
if (!areSelectedAgentsStillVisible) {
|
||||
// force selecting all agents on current page if staying in query mode
|
||||
return setSelectedAgents(agentsOnCurrentPage);
|
||||
return setSelectedAgents(agentsOnCurrentPage.filter((agent) => isAgentSelectable(agent)));
|
||||
} else {
|
||||
setSelectionMode('manual');
|
||||
}
|
||||
|
@ -221,6 +219,20 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
|
|||
setSelectedAgents(newAgents);
|
||||
};
|
||||
|
||||
const onSelectedStatusChange = (status: string[]) => {
|
||||
if (selectionMode === 'query') {
|
||||
setSelectionMode('manual');
|
||||
}
|
||||
setSelectedStatus(status);
|
||||
};
|
||||
|
||||
const onSelectedAgentPoliciesChange = (policies: string[]) => {
|
||||
if (selectionMode === 'query') {
|
||||
setSelectionMode('manual');
|
||||
}
|
||||
setSelectedAgentPolicies(policies);
|
||||
};
|
||||
|
||||
const agentToUnenrollHasFleetServer = useMemo(() => {
|
||||
if (!agentToUnenroll || !agentToUnenroll.policy_id) {
|
||||
return false;
|
||||
|
@ -390,26 +402,24 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
|
|||
onDraftKueryChange={setDraftKuery}
|
||||
onSubmitSearch={onSubmitSearch}
|
||||
selectedAgentPolicies={selectedAgentPolicies}
|
||||
onSelectedAgentPoliciesChange={setSelectedAgentPolicies}
|
||||
onSelectedAgentPoliciesChange={onSelectedAgentPoliciesChange}
|
||||
selectedStatus={selectedStatus}
|
||||
onSelectedStatusChange={setSelectedStatus}
|
||||
onSelectedStatusChange={onSelectedStatusChange}
|
||||
showUpgradeable={showUpgradeable}
|
||||
onShowUpgradeableChange={setShowUpgradeable}
|
||||
tags={allTags ?? []}
|
||||
selectedTags={selectedTags}
|
||||
onSelectedTagsChange={setSelectedTags}
|
||||
shownAgents={shownAgents}
|
||||
inactiveShownAgents={inactiveShownAgents}
|
||||
nAgentsInTable={nAgentsInTable}
|
||||
totalInactiveAgents={totalInactiveAgents}
|
||||
totalManagedAgentIds={totalManagedAgentIds}
|
||||
inactiveManagedAgentIds={inactiveManagedAgentIds}
|
||||
selectionMode={selectionMode}
|
||||
currentQuery={kuery}
|
||||
selectedAgents={selectedAgents}
|
||||
refreshAgents={refreshAgents}
|
||||
onClickAddAgent={() => setEnrollmentFlyoutState({ isOpen: true })}
|
||||
onClickAddFleetServer={onClickAddFleetServer}
|
||||
visibleAgents={agentsOnCurrentPage}
|
||||
agentsOnCurrentPage={agentsOnCurrentPage}
|
||||
onClickAgentActivity={onClickAgentActivity}
|
||||
showAgentActivityTour={showAgentActivityTour}
|
||||
/>
|
||||
|
@ -417,7 +427,7 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
|
|||
{/* Agent total, bulk actions and status bar */}
|
||||
<AgentTableHeader
|
||||
showInactive={showInactive}
|
||||
totalAgents={shownAgents}
|
||||
totalAgents={nAgentsInTable}
|
||||
agentStatus={agentsStatus}
|
||||
selectableAgents={agentsOnCurrentPage?.filter(isAgentSelectable).length || 0}
|
||||
managedAgentsOnCurrentPage={managedAgentsOnCurrentPage}
|
||||
|
@ -446,7 +456,7 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
|
|||
showUpgradeable={showUpgradeable}
|
||||
onTableChange={onTableChange}
|
||||
pagination={pagination}
|
||||
totalAgents={Math.min(shownAgents, SO_SEARCH_LIMIT)}
|
||||
totalAgents={Math.min(nAgentsInTable, SO_SEARCH_LIMIT)}
|
||||
isUsingFilter={isUsingFilter}
|
||||
setEnrollmentFlyoutState={setEnrollmentFlyoutState}
|
||||
clearFilters={clearFilters}
|
||||
|
|
|
@ -10,7 +10,7 @@ import type { Agent, AgentPolicy } from '../../../../types';
|
|||
import { getCommonTags } from './get_common_tags';
|
||||
|
||||
describe('getCommonTags', () => {
|
||||
it('should return common tags from visibleAgents if agents is empty string', () => {
|
||||
it('should return common tags from agentsOnCurrentPage if agents is empty string', () => {
|
||||
const result = getCommonTags(
|
||||
'',
|
||||
[{ tags: ['tag1'] }, { tags: ['tag1', 'tag2'] }] as Agent[],
|
||||
|
@ -20,7 +20,7 @@ describe('getCommonTags', () => {
|
|||
expect(result).toEqual(['tag1']);
|
||||
});
|
||||
|
||||
it('should return common tags from visibleAgents if agents is query', () => {
|
||||
it('should return common tags from agentsOnCurrentPage if agents is query', () => {
|
||||
const result = getCommonTags(
|
||||
'query',
|
||||
[{ tags: ['tag1'] }, { tags: ['tag1', 'tag2'] }] as Agent[],
|
||||
|
@ -30,7 +30,7 @@ describe('getCommonTags', () => {
|
|||
expect(result).toEqual(['tag1']);
|
||||
});
|
||||
|
||||
it('should return empty common tags if visibleAgents is empty', () => {
|
||||
it('should return empty common tags if agentsOnCurrentPage is empty', () => {
|
||||
const result = getCommonTags('', [], []);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
|
@ -52,7 +52,7 @@ describe('getCommonTags', () => {
|
|||
expect(result).toEqual(['oldTag', 'tag1']);
|
||||
});
|
||||
|
||||
it('should return common tags from old data if visibleAgents empty', () => {
|
||||
it('should return common tags from old data if agentsOnCurrentPage empty', () => {
|
||||
const result = getCommonTags(
|
||||
[
|
||||
{ id: 'agent1', tags: ['oldTag'] },
|
||||
|
|
|
@ -11,7 +11,7 @@ import type { Agent, AgentPolicy } from '../../../../types';
|
|||
|
||||
export const getCommonTags = (
|
||||
agents: string | Agent[],
|
||||
visibleAgents: Agent[],
|
||||
agentsOnCurrentPage: Agent[],
|
||||
agentPolicies: AgentPolicy[]
|
||||
): string[] => {
|
||||
const isManagedPolicy = (agent: Agent): boolean => {
|
||||
|
@ -33,12 +33,12 @@ export const getCommonTags = (
|
|||
if (!Array.isArray(agents)) {
|
||||
// in query mode, returning common tags of all agents in current page
|
||||
// this is a simplification to avoid querying all agents from backend to determine common tags
|
||||
return commonSelectedTags(visibleAgents);
|
||||
return commonSelectedTags(agentsOnCurrentPage);
|
||||
}
|
||||
// taking latest tags from freshly loaded agents data, as selected agents array does not contain the latest tags of agents
|
||||
const freshSelectedAgentsData =
|
||||
visibleAgents.length > 0
|
||||
? visibleAgents.filter((newAgent) =>
|
||||
agentsOnCurrentPage.length > 0
|
||||
? agentsOnCurrentPage.filter((newAgent) =>
|
||||
agents.find((existingAgent) => existingAgent.id === newAgent.id)
|
||||
)
|
||||
: agents;
|
||||
|
|
|
@ -77,6 +77,7 @@ export const AgentReassignAgentPolicyModal: React.FunctionComponent<Props> = ({
|
|||
: await sendPostBulkAgentReassign({
|
||||
policy_id: selectedAgentPolicyId,
|
||||
agents: Array.isArray(agents) ? agents.map((agent) => agent.id) : agents,
|
||||
includeInactive: true,
|
||||
});
|
||||
if (res.error) {
|
||||
throw res.error;
|
||||
|
|
|
@ -257,6 +257,7 @@ export const AgentUpgradeAgentModal: React.FunctionComponent<AgentUpgradeAgentMo
|
|||
version,
|
||||
agents: getQuery(isUpdating ? updatingQuery : agents),
|
||||
force: isUpdating,
|
||||
includeInactive: true,
|
||||
...rolloutOptions,
|
||||
});
|
||||
if (error) {
|
||||
|
|
|
@ -144,7 +144,7 @@ export const bulkUpdateAgentTagsHandler: RequestHandler<
|
|||
const soClient = coreContext.savedObjects.client;
|
||||
const agentOptions = Array.isArray(request.body.agents)
|
||||
? { agentIds: request.body.agents }
|
||||
: { kuery: request.body.agents };
|
||||
: { kuery: request.body.agents, showInactive: request.body.includeInactive };
|
||||
|
||||
try {
|
||||
const results = await AgentService.updateAgentTags(
|
||||
|
@ -273,7 +273,7 @@ export const postAgentsReassignHandler: RequestHandler<
|
|||
}
|
||||
};
|
||||
|
||||
export const postBulkAgentsReassignHandler: RequestHandler<
|
||||
export const postBulkAgentReassignHandler: RequestHandler<
|
||||
undefined,
|
||||
undefined,
|
||||
TypeOf<typeof PostBulkAgentReassignRequestSchema.body>
|
||||
|
@ -283,7 +283,7 @@ export const postBulkAgentsReassignHandler: RequestHandler<
|
|||
const esClient = coreContext.elasticsearch.client.asInternalUser;
|
||||
const agentOptions = Array.isArray(request.body.agents)
|
||||
? { agentIds: request.body.agents }
|
||||
: { kuery: request.body.agents };
|
||||
: { kuery: request.body.agents, showInactive: request.body.includeInactive };
|
||||
|
||||
try {
|
||||
const results = await AgentService.reassignAgents(
|
||||
|
|
|
@ -50,7 +50,7 @@ import {
|
|||
deleteAgentHandler,
|
||||
getAgentStatusForAgentPolicyHandler,
|
||||
putAgentsReassignHandlerDeprecated,
|
||||
postBulkAgentsReassignHandler,
|
||||
postBulkAgentReassignHandler,
|
||||
getAgentDataHandler,
|
||||
bulkUpdateAgentTagsHandler,
|
||||
getAvailableVersionsHandler,
|
||||
|
@ -440,7 +440,7 @@ export const registerAPIRoutes = (router: FleetAuthzRouter, config: FleetConfigT
|
|||
version: API_VERSIONS.public.v1,
|
||||
validate: { request: PostBulkAgentReassignRequestSchema },
|
||||
},
|
||||
postBulkAgentsReassignHandler
|
||||
postBulkAgentReassignHandler
|
||||
);
|
||||
|
||||
// Bulk unenroll
|
||||
|
|
|
@ -173,7 +173,9 @@ export const postBulkAgentsUpgradeHandler: RequestHandler<
|
|||
}
|
||||
|
||||
try {
|
||||
const agentOptions = Array.isArray(agents) ? { agentIds: agents } : { kuery: agents };
|
||||
const agentOptions = Array.isArray(agents)
|
||||
? { agentIds: agents }
|
||||
: { kuery: agents, showInactive: request.body.includeInactive };
|
||||
const upgradeOptions = {
|
||||
...agentOptions,
|
||||
sourceUri,
|
||||
|
|
|
@ -137,6 +137,7 @@ export const PostBulkAgentUpgradeRequestSchema = {
|
|||
})
|
||||
),
|
||||
batchSize: schema.maybe(schema.number()),
|
||||
includeInactive: schema.boolean({ defaultValue: false }),
|
||||
}),
|
||||
};
|
||||
|
||||
|
@ -189,6 +190,7 @@ export const PostBulkAgentReassignRequestSchema = {
|
|||
policy_id: schema.string(),
|
||||
agents: schema.oneOf([schema.arrayOf(schema.string()), schema.string()]),
|
||||
batchSize: schema.maybe(schema.number()),
|
||||
includeInactive: schema.boolean({ defaultValue: false }),
|
||||
}),
|
||||
};
|
||||
|
||||
|
@ -214,6 +216,7 @@ export const PostBulkUpdateAgentTagsRequestSchema = {
|
|||
tagsToAdd: schema.maybe(schema.arrayOf(schema.string())),
|
||||
tagsToRemove: schema.maybe(schema.arrayOf(schema.string())),
|
||||
batchSize: schema.maybe(schema.number()),
|
||||
includeInactive: schema.boolean({ defaultValue: false }),
|
||||
}),
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue