[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:
Kibana Machine 2024-03-20 04:47:43 -04:00 committed by GitHub
parent 24e7c9ab38
commit 01e897bb0d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 133 additions and 216 deletions

View file

@ -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;
};
}

View file

@ -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()
);

View file

@ -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 (

View file

@ -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: [],

View file

@ -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}

View file

@ -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,

View file

@ -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

View file

@ -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}

View file

@ -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'] },

View file

@ -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;

View file

@ -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;

View file

@ -257,6 +257,7 @@ export const AgentUpgradeAgentModal: React.FunctionComponent<AgentUpgradeAgentMo
version,
agents: getQuery(isUpdating ? updatingQuery : agents),
force: isUpdating,
includeInactive: true,
...rolloutOptions,
});
if (error) {

View file

@ -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(

View file

@ -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

View file

@ -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,

View file

@ -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 }),
}),
};