[8.12][Fleet] fix API not allowing to upgrade to a newer version when version differs only in patch (#175765)

Fixes https://github.com/elastic/kibana/issues/175766

[8.12][Fleet] Partial backport of
https://github.com/elastic/kibana/pull/175198

To verify:
- add a fleet-server version 8.12.0
- enroll an agent version 8.12.0-SNAPSHOT
- take the agent id and in console run this API request
```
POST kbn:/api/fleet/agents/6a56f865-a611-4921-9f24-87757259223e/upgrade
  {
    "version": "8.12.1-SNAPSHOT"
  }
```
- verify that the API returns 200 

Relates https://github.com/elastic/kibana/issues/168502
This commit is contained in:
Julia Bardi 2024-01-29 16:58:43 +01:00 committed by GitHub
parent 0b4ecdb9f3
commit 92960c4694
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 53 additions and 22 deletions

View file

@ -85,3 +85,16 @@ export function isAgentUpgrading(agent: Agent) {
}
return agent.upgrade_started_at && !agent.upgraded_at;
}
export const differsOnlyInPatch = (
versionA: string,
versionB: string,
allowEqualPatch: boolean = true
): boolean => {
const [majorA, minorA, patchA] = versionA.split('.');
const [majorB, minorB, patchB] = versionB.split('.');
return (
majorA === majorB && minorA === minorB && (allowEqualPatch ? patchA >= patchB : patchA > patchB)
);
};

View file

@ -27,7 +27,10 @@ import type { EuiComboBoxOptionOption } from '@elastic/eui';
import semverGt from 'semver/functions/gt';
import semverLt from 'semver/functions/lt';
import { AGENT_UPGRADE_COOLDOWN_IN_MIN } from '../../../../../../../common/services';
import {
AGENT_UPGRADE_COOLDOWN_IN_MIN,
differsOnlyInPatch,
} from '../../../../../../../common/services';
import { getMinVersion } from '../../../../../../../common/services/get_min_max_version';
import {
@ -44,7 +47,6 @@ import {
useConfig,
sendGetAgentStatus,
useAgentVersion,
differsOnlyInPatch,
} from '../../../../hooks';
import { sendGetAgentsAvailableVersions } from '../../../../hooks';

View file

@ -8,6 +8,8 @@ import { useEffect, useState } from 'react';
import semverRcompare from 'semver/functions/rcompare';
import semverLt from 'semver/functions/lt';
import { differsOnlyInPatch } from '../../common/services';
import { useKibanaVersion } from './use_kibana_version';
import { sendGetAgentsAvailableVersions } from './use_request';
@ -50,16 +52,3 @@ export const useAgentVersion = (): string | undefined => {
return agentVersion;
};
export const differsOnlyInPatch = (
versionA: string,
versionB: string,
allowEqualPatch: boolean = true
): boolean => {
const [majorA, minorA, patchA] = versionA.split('.');
const [majorB, minorB, patchB] = versionB.split('.');
return (
majorA === majorB && minorA === minorB && (allowEqualPatch ? patchA >= patchB : patchA > patchB)
);
};

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { checkKibanaVersion } from './upgrade_handler';
import { checkFleetServerVersion, checkKibanaVersion } from './upgrade_handler';
describe('upgrade handler', () => {
describe('checkKibanaVersion', () => {
@ -15,7 +15,7 @@ describe('upgrade handler', () => {
it('should throw if upgrade version is higher than kibana version', () => {
expect(() => checkKibanaVersion('8.5.0', '8.4.0')).toThrowError(
'cannot upgrade agent to 8.5.0 because it is higher than the installed kibana version 8.4.0'
'Cannot upgrade agent to 8.5.0 because it is higher than the installed kibana version 8.4.0'
);
});
@ -28,6 +28,11 @@ describe('upgrade handler', () => {
expect(() => checkKibanaVersion('8.4.1-SNAPSHOT', '8.4.0', true)).not.toThrowError();
});
it('should not throw if not force is specified and patch is newer', () => {
expect(() => checkKibanaVersion('8.4.1', '8.4.0', false)).not.toThrowError();
expect(() => checkKibanaVersion('8.4.1-SNAPSHOT', '8.4.0', false)).not.toThrowError();
});
it('should throw if force is specified and minor is newer', () => {
expect(() => checkKibanaVersion('8.5.0', '8.4.0', true)).toThrowError();
});
@ -37,4 +42,17 @@ describe('upgrade handler', () => {
expect(() => checkKibanaVersion('8.4.0', '8.4.0', true)).not.toThrowError();
});
});
describe('checkFleetServerVersion', () => {
it('should not throw if no force is specified and patch is newer', () => {
const fleetServers = [
{ local_metadata: { elastic: { agent: { version: '8.3.0' } } } },
{ local_metadata: { elastic: { agent: { version: '8.4.0' } } } },
] as any;
expect(() => checkFleetServerVersion('8.4.1', fleetServers, false)).not.toThrowError();
expect(() =>
checkFleetServerVersion('8.4.1-SNAPSHOT', fleetServers, false)
).not.toThrowError();
});
});
});

View file

@ -25,6 +25,7 @@ import {
isAgentUpgradeable,
AGENT_UPGRADE_COOLDOWN_IN_MIN,
isAgentUpgrading,
differsOnlyInPatch,
} from '../../../common/services';
import { getMaxVersion } from '../../../common/services/get_min_max_version';
import { getAgentById } from '../../services/agents';
@ -192,9 +193,13 @@ export const checkKibanaVersion = (version: string, kibanaVersion: string, force
if (!versionToUpgradeNumber)
throw new Error(`version to upgrade ${versionToUpgradeNumber} is not valid`);
if (!force && semverGt(versionToUpgradeNumber, kibanaVersionNumber)) {
if (
!force &&
semverGt(versionToUpgradeNumber, kibanaVersionNumber) &&
!differsOnlyInPatch(versionToUpgradeNumber, kibanaVersionNumber)
) {
throw new Error(
`cannot upgrade agent to ${versionToUpgradeNumber} because it is higher than the installed kibana version ${kibanaVersionNumber}`
`Cannot upgrade agent to ${versionToUpgradeNumber} because it is higher than the installed kibana version ${kibanaVersionNumber}`
);
}
@ -212,7 +217,7 @@ export const checkKibanaVersion = (version: string, kibanaVersion: string, force
};
// Check the installed fleet server version
const checkFleetServerVersion = (
export const checkFleetServerVersion = (
versionToUpgradeNumber: string,
fleetServerAgents: Agent[],
force = false
@ -227,7 +232,11 @@ const checkFleetServerVersion = (
return;
}
if (!force && semverGt(versionToUpgradeNumber, maxFleetServerVersion)) {
if (
!force &&
semverGt(versionToUpgradeNumber, maxFleetServerVersion) &&
!differsOnlyInPatch(versionToUpgradeNumber, maxFleetServerVersion)
) {
throw new Error(
`cannot upgrade agent to ${versionToUpgradeNumber} because it is higher than the latest fleet server version ${maxFleetServerVersion}`
);

View file

@ -1226,7 +1226,7 @@ export default function (providerContext: FtrProviderContext) {
});
it('should respond 400 if trying to bulk upgrade to a version that is higher than the latest fleet server version', async () => {
const higherVersion = semver.inc(fleetServerVersion, 'patch');
const higherVersion = '7.17.0';
await es.update({
id: 'agent1',
refresh: 'wait_for',