mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
[Fleet] Agent upgrade available should use latest agent version (#167410)
## Summary Closes https://github.com/elastic/kibana/issues/167387 Replaced using kibana version when deciding if agent upgrade is available (only in serverless, in stateful kibana version is still returned as an available version). To verify locally: - [to test stateless] add this to `kibana.dev.yml`: `xpack.fleet.internal.onlyAllowAgentUpgradeToKnownVersions: true` - extract the `agent_versions_list.json` to local kibana folder `~/kibana/x-pack/plugins/fleet/target` [agent_versions_list.json.zip](12739519/agent_versions_list.json.zip
) - verify that upgrade available warnings still work if agent is < latest agent version (8.10.2) - when trying to upgrade agent, verify that the default version is the latest agent version, and 8.11 is not in the list Agent list: <img width="1475" alt="image" src="f06b7bc8
-97e6-4ff9-b872-736ede5e969a"> Upgrade available filter - 1 agent on latest version, 9 upgradeable: <img width="1314" alt="image" src="4ff5ac02
-903b-493b-94df-68b1b7ad6846"> Agent details: <img width="1512" alt="image" src="3ff6e1d5
-2ccc-4814-83e5-c4760ad63722"> Agent on latest version has disable `Upgrade agent` action: <img width="1322" alt="image" src="f461dbf5
-04e5-4bcc-8801-48c2b1a90225"> Bulk action with one agent that is not upgradeable (already on latest version), expected error: <img width="1597" alt="image" src="8bfa46ae
-6684-4748-9fca-e908c142b642"> ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
This commit is contained in:
parent
c7bb851ded
commit
474c8eaf52
14 changed files with 223 additions and 65 deletions
|
@ -11,7 +11,11 @@ import semverGt from 'semver/functions/gt';
|
|||
|
||||
import type { Agent } from '../types';
|
||||
|
||||
export function isAgentUpgradeable(agent: Agent, kibanaVersion: string, versionToUpgrade?: string) {
|
||||
export function isAgentUpgradeable(
|
||||
agent: Agent,
|
||||
latestAgentVersion: string,
|
||||
versionToUpgrade?: string
|
||||
) {
|
||||
let agentVersion: string;
|
||||
if (typeof agent?.local_metadata?.elastic?.agent?.version === 'string') {
|
||||
agentVersion = agent.local_metadata.elastic.agent.version;
|
||||
|
@ -31,23 +35,23 @@ export function isAgentUpgradeable(agent: Agent, kibanaVersion: string, versionT
|
|||
if (versionToUpgrade !== undefined) {
|
||||
return (
|
||||
isNotDowngrade(agentVersion, versionToUpgrade) &&
|
||||
isAgentVersionLessThanKibana(agentVersion, kibanaVersion)
|
||||
isAgentVersionLessThanLatest(agentVersion, latestAgentVersion)
|
||||
);
|
||||
}
|
||||
return isAgentVersionLessThanKibana(agentVersion, kibanaVersion);
|
||||
return isAgentVersionLessThanLatest(agentVersion, latestAgentVersion);
|
||||
}
|
||||
|
||||
export const isAgentVersionLessThanKibana = (agentVersion: string, kibanaVersion: string) => {
|
||||
const isAgentVersionLessThanLatest = (agentVersion: string, latestAgentVersion: string) => {
|
||||
// make sure versions are only the number before comparison
|
||||
const agentVersionNumber = semverCoerce(agentVersion);
|
||||
if (!agentVersionNumber) throw new Error('agent version is not valid');
|
||||
const kibanaVersionNumber = semverCoerce(kibanaVersion);
|
||||
if (!kibanaVersionNumber) throw new Error('kibana version is not valid');
|
||||
const latestAgentVersionNumber = semverCoerce(latestAgentVersion);
|
||||
if (!latestAgentVersionNumber) throw new Error('latest version is not valid');
|
||||
|
||||
return semverLt(agentVersionNumber, kibanaVersionNumber);
|
||||
return semverLt(agentVersionNumber, latestAgentVersionNumber);
|
||||
};
|
||||
|
||||
export const isNotDowngrade = (agentVersion: string, versionToUpgrade: string) => {
|
||||
const isNotDowngrade = (agentVersion: string, versionToUpgrade: string) => {
|
||||
const agentVersionNumber = semverCoerce(agentVersion);
|
||||
if (!agentVersionNumber) throw new Error('agent version is not valid');
|
||||
const versionToUpgradeNumber = semverCoerce(versionToUpgrade);
|
||||
|
|
|
@ -12,14 +12,17 @@ import { createFleetTestRendererMock } from '../../../../../../mock';
|
|||
import type { Agent, AgentPolicy } from '../../../../types';
|
||||
import { ExperimentalFeaturesService } from '../../../../services';
|
||||
import { useAuthz } from '../../../../../../hooks/use_authz';
|
||||
import { useAgentVersion } from '../../../../../../hooks/use_agent_version';
|
||||
|
||||
import { AgentDetailsActionMenu } from './actions_menu';
|
||||
|
||||
jest.mock('../../../../../../services/experimental_features');
|
||||
jest.mock('../../../../../../hooks/use_authz');
|
||||
jest.mock('../../../../../../hooks/use_agent_version');
|
||||
|
||||
const mockedExperimentalFeaturesService = jest.mocked(ExperimentalFeaturesService);
|
||||
const mockedUseAuthz = jest.mocked(useAuthz);
|
||||
const mockedUseAgentVersion = jest.mocked(useAgentVersion);
|
||||
|
||||
function renderActions({ agent, agentPolicy }: { agent: Agent; agentPolicy?: AgentPolicy }) {
|
||||
const renderer = createFleetTestRendererMock();
|
||||
|
@ -48,6 +51,7 @@ describe('AgentDetailsActionMenu', () => {
|
|||
all: true,
|
||||
},
|
||||
} as any);
|
||||
mockedUseAgentVersion.mockReturnValue('8.10.2');
|
||||
});
|
||||
|
||||
describe('Request Diagnotics action', () => {
|
||||
|
@ -162,45 +166,90 @@ describe('AgentDetailsActionMenu', () => {
|
|||
expect(res).toBeEnabled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Restart upgrade action', () => {
|
||||
function renderAndGetRestartUpgradeButton({
|
||||
agent,
|
||||
agentPolicy,
|
||||
}: {
|
||||
agent: Agent;
|
||||
agentPolicy?: AgentPolicy;
|
||||
}) {
|
||||
const { utils } = renderActions({
|
||||
describe('Restart upgrade action', () => {
|
||||
function renderAndGetRestartUpgradeButton({
|
||||
agent,
|
||||
agentPolicy,
|
||||
}: {
|
||||
agent: Agent;
|
||||
agentPolicy?: AgentPolicy;
|
||||
}) {
|
||||
const { utils } = renderActions({
|
||||
agent,
|
||||
agentPolicy,
|
||||
});
|
||||
|
||||
return utils.queryByTestId('restartUpgradeBtn');
|
||||
}
|
||||
|
||||
it('should render an active button', async () => {
|
||||
const res = renderAndGetRestartUpgradeButton({
|
||||
agent: {
|
||||
status: 'updating',
|
||||
upgrade_started_at: '2022-11-21T12:27:24Z',
|
||||
} as any,
|
||||
agentPolicy: {} as AgentPolicy,
|
||||
});
|
||||
|
||||
expect(res).not.toBe(null);
|
||||
expect(res).toBeEnabled();
|
||||
});
|
||||
|
||||
return utils.queryByTestId('restartUpgradeBtn');
|
||||
}
|
||||
|
||||
it('should render an active button', async () => {
|
||||
const res = renderAndGetRestartUpgradeButton({
|
||||
agent: {
|
||||
status: 'updating',
|
||||
upgrade_started_at: '2022-11-21T12:27:24Z',
|
||||
} as any,
|
||||
agentPolicy: {} as AgentPolicy,
|
||||
it('should not render action if agent is not stuck in updating', async () => {
|
||||
const res = renderAndGetRestartUpgradeButton({
|
||||
agent: {
|
||||
status: 'updating',
|
||||
upgrade_started_at: new Date().toISOString(),
|
||||
} as any,
|
||||
agentPolicy: {} as AgentPolicy,
|
||||
});
|
||||
expect(res).toBe(null);
|
||||
});
|
||||
|
||||
expect(res).not.toBe(null);
|
||||
expect(res).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should not render action if agent is not stuck in updating', async () => {
|
||||
const res = renderAndGetRestartUpgradeButton({
|
||||
agent: {
|
||||
status: 'updating',
|
||||
upgrade_started_at: new Date().toISOString(),
|
||||
} as any,
|
||||
agentPolicy: {} as AgentPolicy,
|
||||
describe('Upgrade action', () => {
|
||||
function renderAndGetUpgradeButton({
|
||||
agent,
|
||||
agentPolicy,
|
||||
}: {
|
||||
agent: Agent;
|
||||
agentPolicy?: AgentPolicy;
|
||||
}) {
|
||||
const { utils } = renderActions({
|
||||
agent,
|
||||
agentPolicy,
|
||||
});
|
||||
|
||||
return utils.queryByTestId('upgradeBtn');
|
||||
}
|
||||
|
||||
it('should render an active action button if agent version is not the latest', async () => {
|
||||
const res = renderAndGetUpgradeButton({
|
||||
agent: {
|
||||
active: true,
|
||||
status: 'online',
|
||||
local_metadata: { elastic: { agent: { version: '8.8.0', upgradeable: true } } },
|
||||
} as any,
|
||||
agentPolicy: {} as AgentPolicy,
|
||||
});
|
||||
|
||||
expect(res).not.toBe(null);
|
||||
expect(res).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should render a disabled action button if agent version is latest', async () => {
|
||||
const res = renderAndGetUpgradeButton({
|
||||
agent: {
|
||||
active: true,
|
||||
status: 'online',
|
||||
local_metadata: { elastic: { agent: { version: '8.10.2', upgradeable: true } } },
|
||||
} as any,
|
||||
agentPolicy: {} as AgentPolicy,
|
||||
});
|
||||
|
||||
expect(res).not.toBe(null);
|
||||
expect(res).not.toBeEnabled();
|
||||
});
|
||||
expect(res).toBe(null);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -13,7 +13,7 @@ import { isAgentRequestDiagnosticsSupported } from '../../../../../../../common/
|
|||
import { isStuckInUpdating } from '../../../../../../../common/services/agent_status';
|
||||
|
||||
import type { Agent, AgentPolicy } from '../../../../types';
|
||||
import { useAuthz, useKibanaVersion } from '../../../../hooks';
|
||||
import { useAgentVersion, useAuthz } from '../../../../hooks';
|
||||
import { ContextMenuActions } from '../../../../components';
|
||||
import {
|
||||
AgentUnenrollAgentModal,
|
||||
|
@ -34,7 +34,7 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{
|
|||
onCancelReassign?: () => void;
|
||||
}> = memo(({ agent, assignFlyoutOpenByDefault = false, onCancelReassign, agentPolicy }) => {
|
||||
const hasFleetAllPrivileges = useAuthz().fleet.all;
|
||||
const kibanaVersion = useKibanaVersion();
|
||||
const latestAgentVersion = useAgentVersion();
|
||||
const refreshAgent = useAgentRefresh();
|
||||
const [isReassignFlyoutOpen, setIsReassignFlyoutOpen] = useState(assignFlyoutOpenByDefault);
|
||||
const [isUnenrollModalOpen, setIsUnenrollModalOpen] = useState(false);
|
||||
|
@ -102,11 +102,12 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{
|
|||
</EuiContextMenuItem>,
|
||||
<EuiContextMenuItem
|
||||
icon="refresh"
|
||||
disabled={!isAgentUpgradeable(agent, kibanaVersion)}
|
||||
disabled={!!latestAgentVersion && !isAgentUpgradeable(agent, latestAgentVersion)}
|
||||
onClick={() => {
|
||||
setIsUpgradeModalOpen(true);
|
||||
}}
|
||||
key="upgradeAgent"
|
||||
data-test-subj="upgradeBtn"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentList.upgradeOneButton"
|
||||
|
|
|
@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { FormattedMessage, FormattedRelative } from '@kbn/i18n-react';
|
||||
|
||||
import type { Agent, AgentPolicy } from '../../../../../types';
|
||||
import { useKibanaVersion } from '../../../../../hooks';
|
||||
import { useAgentVersion } from '../../../../../hooks';
|
||||
import { ExperimentalFeaturesService, isAgentUpgradeable } from '../../../../../services';
|
||||
import { AgentPolicySummaryLine } from '../../../../../components';
|
||||
import { AgentHealth } from '../../../components';
|
||||
|
@ -39,7 +39,7 @@ export const AgentDetailsOverviewSection: React.FunctionComponent<{
|
|||
agent: Agent;
|
||||
agentPolicy?: AgentPolicy;
|
||||
}> = memo(({ agent, agentPolicy }) => {
|
||||
const kibanaVersion = useKibanaVersion();
|
||||
const latestAgentVersion = useAgentVersion();
|
||||
const { displayAgentMetrics } = ExperimentalFeaturesService.get();
|
||||
|
||||
return (
|
||||
|
@ -173,7 +173,7 @@ export const AgentDetailsOverviewSection: React.FunctionComponent<{
|
|||
<EuiFlexItem grow={false} className="eui-textNoWrap">
|
||||
{agent.local_metadata.elastic.agent.version}
|
||||
</EuiFlexItem>
|
||||
{isAgentUpgradeable(agent, kibanaVersion) ? (
|
||||
{latestAgentVersion && isAgentUpgradeable(agent, latestAgentVersion) ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiToolTip
|
||||
position="right"
|
||||
|
|
|
@ -23,7 +23,8 @@ import { isAgentUpgradeable, ExperimentalFeaturesService } from '../../../../ser
|
|||
import { AgentHealth } from '../../components';
|
||||
|
||||
import type { Pagination } from '../../../../hooks';
|
||||
import { useLink, useKibanaVersion, useAuthz } from '../../../../hooks';
|
||||
import { useAgentVersion } from '../../../../hooks';
|
||||
import { useLink, useAuthz } from '../../../../hooks';
|
||||
|
||||
import { AgentPolicySummaryLine } from '../../../../components';
|
||||
import { Tags } from '../../components/tags';
|
||||
|
@ -91,7 +92,7 @@ export const AgentListTable: React.FC<Props> = (props: Props) => {
|
|||
const { displayAgentMetrics } = ExperimentalFeaturesService.get();
|
||||
|
||||
const { getHref } = useLink();
|
||||
const kibanaVersion = useKibanaVersion();
|
||||
const latestAgentVersion = useAgentVersion();
|
||||
|
||||
const isAgentSelectable = (agent: Agent) => {
|
||||
if (!agent.active) return false;
|
||||
|
@ -284,7 +285,9 @@ export const AgentListTable: React.FC<Props> = (props: Props) => {
|
|||
<EuiFlexItem grow={false} className="eui-textNoWrap">
|
||||
{safeMetadata(version)}
|
||||
</EuiFlexItem>
|
||||
{isAgentSelectable(agent) && isAgentUpgradeable(agent, kibanaVersion) ? (
|
||||
{isAgentSelectable(agent) &&
|
||||
latestAgentVersion &&
|
||||
isAgentUpgradeable(agent, latestAgentVersion) ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText color="subdued" size="xs" className="eui-textNoWrap">
|
||||
<EuiIcon size="m" type="warning" color="warning" />
|
||||
|
@ -324,7 +327,10 @@ export const AgentListTable: React.FC<Props> = (props: Props) => {
|
|||
totalAgents
|
||||
? showUpgradeable
|
||||
? agents.filter(
|
||||
(agent) => isAgentSelectable(agent) && isAgentUpgradeable(agent, kibanaVersion)
|
||||
(agent) =>
|
||||
isAgentSelectable(agent) &&
|
||||
latestAgentVersion &&
|
||||
isAgentUpgradeable(agent, latestAgentVersion)
|
||||
)
|
||||
: agents
|
||||
: []
|
||||
|
|
|
@ -12,14 +12,17 @@ import { createFleetTestRendererMock } from '../../../../../../mock';
|
|||
import type { Agent, AgentPolicy } from '../../../../types';
|
||||
import { ExperimentalFeaturesService } from '../../../../services';
|
||||
import { useAuthz } from '../../../../../../hooks/use_authz';
|
||||
import { useAgentVersion } from '../../../../../../hooks/use_agent_version';
|
||||
|
||||
import { TableRowActions } from './table_row_actions';
|
||||
|
||||
jest.mock('../../../../../../services/experimental_features');
|
||||
jest.mock('../../../../../../hooks/use_authz');
|
||||
jest.mock('../../../../../../hooks/use_agent_version');
|
||||
|
||||
const mockedExperimentalFeaturesService = jest.mocked(ExperimentalFeaturesService);
|
||||
const mockedUseAuthz = jest.mocked(useAuthz);
|
||||
const mockedUseAgentVersion = jest.mocked(useAgentVersion);
|
||||
|
||||
function renderTableRowActions({
|
||||
agent,
|
||||
|
@ -57,6 +60,7 @@ describe('TableRowActions', () => {
|
|||
all: true,
|
||||
},
|
||||
} as any);
|
||||
mockedUseAgentVersion.mockReturnValue('8.10.2');
|
||||
});
|
||||
|
||||
describe('Request Diagnotics action', () => {
|
||||
|
@ -179,4 +183,53 @@ describe('TableRowActions', () => {
|
|||
expect(res).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Upgrade action', () => {
|
||||
function renderAndGetUpgradeButton({
|
||||
agent,
|
||||
agentPolicy,
|
||||
}: {
|
||||
agent: Agent;
|
||||
agentPolicy?: AgentPolicy;
|
||||
}) {
|
||||
const { utils } = renderTableRowActions({
|
||||
agent,
|
||||
agentPolicy,
|
||||
});
|
||||
|
||||
return utils.queryByTestId('upgradeBtn');
|
||||
}
|
||||
|
||||
it('should render an active action button if agent version is not the latest', async () => {
|
||||
const res = renderAndGetUpgradeButton({
|
||||
agent: {
|
||||
active: true,
|
||||
status: 'online',
|
||||
local_metadata: { elastic: { agent: { version: '8.8.0', upgradeable: true } } },
|
||||
} as any,
|
||||
agentPolicy: {
|
||||
is_managed: false,
|
||||
} as AgentPolicy,
|
||||
});
|
||||
|
||||
expect(res).not.toBe(null);
|
||||
expect(res).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should render a disabled action button if agent version is latest', async () => {
|
||||
const res = renderAndGetUpgradeButton({
|
||||
agent: {
|
||||
active: true,
|
||||
status: 'online',
|
||||
local_metadata: { elastic: { agent: { version: '8.10.2', upgradeable: true } } },
|
||||
} as any,
|
||||
agentPolicy: {
|
||||
is_managed: false,
|
||||
} as AgentPolicy,
|
||||
});
|
||||
|
||||
expect(res).not.toBe(null);
|
||||
expect(res).not.toBeEnabled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,7 +14,7 @@ import { isAgentRequestDiagnosticsSupported } from '../../../../../../../common/
|
|||
import { isStuckInUpdating } from '../../../../../../../common/services/agent_status';
|
||||
|
||||
import type { Agent, AgentPolicy } from '../../../../types';
|
||||
import { useAuthz, useLink, useKibanaVersion } from '../../../../hooks';
|
||||
import { useAuthz, useLink, useAgentVersion } from '../../../../hooks';
|
||||
import { ContextMenuActions } from '../../../../components';
|
||||
import { isAgentUpgradeable } from '../../../../services';
|
||||
import { ExperimentalFeaturesService } from '../../../../services';
|
||||
|
@ -42,7 +42,7 @@ export const TableRowActions: React.FunctionComponent<{
|
|||
const hasFleetAllPrivileges = useAuthz().fleet.all;
|
||||
|
||||
const isUnenrolling = agent.status === 'unenrolling';
|
||||
const kibanaVersion = useKibanaVersion();
|
||||
const latestAgentVersion = useAgentVersion();
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const { diagnosticFileUploadEnabled, agentTamperProtectionEnabled } =
|
||||
ExperimentalFeaturesService.get();
|
||||
|
@ -107,10 +107,11 @@ export const TableRowActions: React.FunctionComponent<{
|
|||
<EuiContextMenuItem
|
||||
key="agentUpgradeBtn"
|
||||
icon="refresh"
|
||||
disabled={!isAgentUpgradeable(agent, kibanaVersion)}
|
||||
disabled={!!latestAgentVersion && !isAgentUpgradeable(agent, latestAgentVersion)}
|
||||
onClick={() => {
|
||||
onUpgradeClick();
|
||||
}}
|
||||
data-test-subj="upgradeBtn"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentList.upgradeOneButton"
|
||||
|
|
|
@ -21,13 +21,14 @@ jest.mock('../../../../hooks', () => {
|
|||
...jest.requireActual('../../../../hooks'),
|
||||
sendGetAgentsAvailableVersions: jest.fn().mockResolvedValue({
|
||||
data: {
|
||||
items: ['8.7.0'],
|
||||
items: ['8.10.2', '8.7.0'],
|
||||
},
|
||||
}),
|
||||
sendGetAgentStatus: jest.fn().mockResolvedValue({
|
||||
data: { results: { updating: 2 } },
|
||||
}),
|
||||
sendPostBulkAgentUpgrade: jest.fn(),
|
||||
useAgentVersion: jest.fn().mockReturnValue('8.10.2'),
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -86,6 +87,18 @@ describe('AgentUpgradeAgentModal', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should default the version combo to latest agent version', async () => {
|
||||
const { utils } = renderAgentUpgradeAgentModal({
|
||||
agents: [{ id: 'agent1', local_metadata: { host: 'abc' } }] as any,
|
||||
agentCount: 1,
|
||||
});
|
||||
|
||||
const el = utils.getByTestId('agentUpgradeModal.VersionCombobox');
|
||||
await waitFor(() => {
|
||||
expect(el.textContent).toEqual('8.10.2');
|
||||
});
|
||||
});
|
||||
|
||||
it('should restart uprade on updating agents if some agents in updating', async () => {
|
||||
const { utils } = renderAgentUpgradeAgentModal({
|
||||
agents: [
|
||||
|
|
|
@ -41,6 +41,7 @@ import {
|
|||
useKibanaVersion,
|
||||
useConfig,
|
||||
sendGetAgentStatus,
|
||||
useAgentVersion,
|
||||
} from '../../../../hooks';
|
||||
|
||||
import { sendGetAgentsAvailableVersions } from '../../../../hooks';
|
||||
|
@ -196,6 +197,20 @@ export const AgentUpgradeAgentModal: React.FunctionComponent<AgentUpgradeAgentMo
|
|||
},
|
||||
];
|
||||
const [selectedVersion, setSelectedVersion] = useState(preselected);
|
||||
|
||||
// latest agent version might be earlier than kibana version
|
||||
const latestAgentVersion = useAgentVersion();
|
||||
useEffect(() => {
|
||||
if (latestAgentVersion) {
|
||||
setSelectedVersion([
|
||||
{
|
||||
label: latestAgentVersion,
|
||||
value: latestAgentVersion,
|
||||
},
|
||||
]);
|
||||
}
|
||||
}, [latestAgentVersion]);
|
||||
|
||||
const [selectedMaintenanceWindow, setSelectedMaintenanceWindow] = useState([
|
||||
isSmallBatch ? maintenanceOptions[0] : maintenanceOptions[1],
|
||||
]);
|
||||
|
|
|
@ -24,6 +24,7 @@ import { getAgentById } from '../../services/agents';
|
|||
import type { Agent } from '../../types';
|
||||
|
||||
import { getAllFleetServerAgents } from '../../collectors/get_all_fleet_server_agents';
|
||||
import { getLatestAvailableVersion } from '../../services/agents/versions';
|
||||
|
||||
export const postAgentUpgradeHandler: RequestHandler<
|
||||
TypeOf<typeof PostAgentUpgradeRequestSchema.params>,
|
||||
|
@ -35,6 +36,7 @@ export const postAgentUpgradeHandler: RequestHandler<
|
|||
const esClient = coreContext.elasticsearch.client.asInternalUser;
|
||||
const { version, source_uri: sourceUri, force } = request.body;
|
||||
const kibanaVersion = appContextService.getKibanaVersion();
|
||||
const latestAgentVersion = await getLatestAvailableVersion();
|
||||
try {
|
||||
checkKibanaVersion(version, kibanaVersion, force);
|
||||
} catch (err) {
|
||||
|
@ -73,7 +75,7 @@ export const postAgentUpgradeHandler: RequestHandler<
|
|||
},
|
||||
});
|
||||
}
|
||||
if (!force && !isAgentUpgradeable(agent, kibanaVersion, version)) {
|
||||
if (!force && !isAgentUpgradeable(agent, latestAgentVersion, version)) {
|
||||
return response.customError({
|
||||
statusCode: 400,
|
||||
body: {
|
||||
|
|
|
@ -24,6 +24,7 @@ import { auditLoggingService } from '../audit_logging';
|
|||
import { searchHitToAgent, agentSOAttributesToFleetServerAgentDoc } from './helpers';
|
||||
|
||||
import { buildAgentStatusRuntimeField } from './build_status_runtime_field';
|
||||
import { getLatestAvailableVersion } from './versions';
|
||||
|
||||
const INACTIVE_AGENT_CONDITION = `status:inactive OR status:unenrolled`;
|
||||
const ACTIVE_AGENT_CONDITION = `NOT (${INACTIVE_AGENT_CONDITION})`;
|
||||
|
@ -302,6 +303,7 @@ export async function getAgentsByKuery(
|
|||
// filtering for a range on the version string will not work,
|
||||
// nor does filtering on a flattened field (local_metadata), so filter here
|
||||
if (showUpgradeable) {
|
||||
const latestAgentVersion = await getLatestAvailableVersion();
|
||||
// fixing a bug where upgradeable filter was not returning right results https://github.com/elastic/kibana/issues/117329
|
||||
// query all agents, then filter upgradeable, and return the requested page and correct total
|
||||
// if there are more than SO_SEARCH_LIMIT agents, the logic falls back to same as before
|
||||
|
@ -309,14 +311,12 @@ export async function getAgentsByKuery(
|
|||
const response = await queryAgents(0, SO_SEARCH_LIMIT);
|
||||
agents = response.hits.hits
|
||||
.map(searchHitToAgent)
|
||||
.filter((agent) => isAgentUpgradeable(agent, appContextService.getKibanaVersion()));
|
||||
.filter((agent) => isAgentUpgradeable(agent, latestAgentVersion));
|
||||
total = agents.length;
|
||||
const start = (page - 1) * perPage;
|
||||
agents = agents.slice(start, start + perPage);
|
||||
} else {
|
||||
agents = agents.filter((agent) =>
|
||||
isAgentUpgradeable(agent, appContextService.getKibanaVersion())
|
||||
);
|
||||
agents = agents.filter((agent) => isAgentUpgradeable(agent, latestAgentVersion));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import { createErrorActionResults, createAgentAction } from './actions';
|
|||
import { getHostedPolicies, isHostedAgent } from './hosted_agent';
|
||||
import { BulkActionTaskType } from './bulk_action_types';
|
||||
import { getCancelledActions } from './action_status';
|
||||
import { getLatestAvailableVersion } from './versions';
|
||||
|
||||
export class UpgradeActionRunner extends ActionRunner {
|
||||
protected async processAgents(agents: Agent[]): Promise<{ actionId: string }> {
|
||||
|
@ -72,12 +73,12 @@ export async function upgradeBatch(
|
|||
? givenAgents.filter((agent: Agent) => !isHostedAgent(hostedPolicies, agent))
|
||||
: givenAgents;
|
||||
|
||||
const kibanaVersion = appContextService.getKibanaVersion();
|
||||
const latestAgentVersion = await getLatestAvailableVersion();
|
||||
const upgradeableResults = await Promise.allSettled(
|
||||
agentsToCheckUpgradeable.map(async (agent) => {
|
||||
// Filter out agents currently unenrolling, unenrolled, or not upgradeable b/c of version check
|
||||
const isNotAllowed =
|
||||
!options.force && !isAgentUpgradeable(agent, kibanaVersion, options.version);
|
||||
!options.force && !isAgentUpgradeable(agent, latestAgentVersion, options.version);
|
||||
if (isNotAllowed) {
|
||||
throw new FleetError(`Agent ${agent.id} is not upgradeable`);
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ describe('getAvailableVersions', () => {
|
|||
mockKibanaVersion = '300.0.0';
|
||||
mockedReadFile.mockResolvedValue(`["8.1.0", "8.0.0", "7.17.0", "7.16.0"]`);
|
||||
|
||||
const res = await getAvailableVersions({ cached: false });
|
||||
const res = await getAvailableVersions({ cached: false, includeCurrentVersion: true });
|
||||
|
||||
expect(res).toEqual(['300.0.0', '8.1.0', '8.0.0', '7.17.0']);
|
||||
});
|
||||
|
@ -39,11 +39,11 @@ describe('getAvailableVersions', () => {
|
|||
mockKibanaVersion = '300.0.0-SNAPSHOT';
|
||||
mockedReadFile.mockResolvedValue(`["8.1.0", "8.0.0", "7.17.0", "7.16.0"]`);
|
||||
|
||||
const res = await getAvailableVersions({ cached: false });
|
||||
const res = await getAvailableVersions({ cached: false, includeCurrentVersion: true });
|
||||
expect(res).toEqual(['300.0.0-SNAPSHOT', '8.1.0', '8.0.0', '7.17.0']);
|
||||
});
|
||||
|
||||
it('should not include the current version if onlyAllowAgentUpgradeToKnownVersions = true', async () => {
|
||||
it('should not include the current version if includeCurrentVersion is not set', async () => {
|
||||
mockKibanaVersion = '300.0.0-SNAPSHOT';
|
||||
mockConfig = {
|
||||
internal: {
|
||||
|
@ -65,4 +65,18 @@ describe('getAvailableVersions', () => {
|
|||
|
||||
expect(res).toEqual(['8.1.0', '8.0.0', '7.17.0']);
|
||||
});
|
||||
|
||||
it('should return kibana version only if cannot read versions', async () => {
|
||||
mockKibanaVersion = '300.0.0';
|
||||
mockConfig = {
|
||||
internal: {
|
||||
onlyAllowAgentUpgradeToKnownVersions: false,
|
||||
},
|
||||
};
|
||||
mockedReadFile.mockRejectedValue({ code: 'ENOENT' });
|
||||
|
||||
const res = await getAvailableVersions({ cached: false });
|
||||
|
||||
expect(res).toEqual(['300.0.0']);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -60,8 +60,7 @@ export const getAvailableVersions = async ({
|
|||
.sort((a: any, b: any) => (semverGt(a, b) ? -1 : 1));
|
||||
versionsToDisplay = uniq(versions) as string[];
|
||||
|
||||
const appendCurrentVersion =
|
||||
includeCurrentVersion ?? !config?.internal?.onlyAllowAgentUpgradeToKnownVersions;
|
||||
const appendCurrentVersion = includeCurrentVersion;
|
||||
|
||||
if (appendCurrentVersion) {
|
||||
// Add current version if not already present
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue