mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Fleet] fix UI error when entering an invalid semver (#179631)
## Summary Closes https://github.com/elastic/kibana/issues/179592 The UI calls the semver version check functions on every keystroke, it seems the semver functions throw error if the input is not a valid semver (e.g. `8.14` instead of `8.14.0`). Added try-catch around the logic which is called from the UI. To verify: - run a stack 8.14.0-SNAPSHOT with fleet-server - enroll an agent with version 8.13.0 - upgrade agent, type in `8.14` - verify that the UI error is no longer visible - the submit button should only be enabled if typing a valid semver e.g. `8.14.0` <img width="1139" alt="image" src="756e1995
-f16b-4689-af19-5ffcdb57b18e"> <img width="829" alt="image" src="1dd7ea6f
-5686-4163-b367-850563abc336"> ### 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
622380d50b
commit
a1abf16b77
4 changed files with 120 additions and 30 deletions
|
@ -7,6 +7,7 @@
|
|||
|
||||
import {
|
||||
checkFleetServerVersion,
|
||||
getFleetServerVersionMessage,
|
||||
isAgentVersionLessThanFleetServer,
|
||||
} from './check_fleet_server_versions';
|
||||
|
||||
|
@ -66,4 +67,30 @@ describe('isAgentVersionLessThanFleetServer', () => {
|
|||
] as any;
|
||||
expect(isAgentVersionLessThanFleetServer('8.5.0', fleetServers)).toBe(false);
|
||||
});
|
||||
|
||||
it('should not throw if version is not a semver', () => {
|
||||
const fleetServers = [
|
||||
{ local_metadata: { elastic: { agent: { version: '8.13.0' } } } },
|
||||
{ local_metadata: { elastic: { agent: { version: '8.14.0' } } } },
|
||||
] as any;
|
||||
const version = '8.14';
|
||||
|
||||
const result = isAgentVersionLessThanFleetServer(version, fleetServers);
|
||||
|
||||
expect(result).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFleetServerVersionMessage', () => {
|
||||
it('should not throw if version is not a semver', () => {
|
||||
const fleetServers = [
|
||||
{ local_metadata: { elastic: { agent: { version: '8.13.0' } } } },
|
||||
{ local_metadata: { elastic: { agent: { version: '8.14.0' } } } },
|
||||
] as any;
|
||||
const version = '8.14';
|
||||
|
||||
const result = getFleetServerVersionMessage(version, fleetServers);
|
||||
|
||||
expect(result).toEqual('Invalid Version: 8.14');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -41,24 +41,27 @@ export const getFleetServerVersionMessage = (
|
|||
if (!maxFleetServerVersion || !versionToUpgradeNumber) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (
|
||||
!force &&
|
||||
semverGt(versionToUpgradeNumber, maxFleetServerVersion) &&
|
||||
!differsOnlyInPatch(versionToUpgradeNumber, maxFleetServerVersion)
|
||||
) {
|
||||
return `Cannot upgrade to version ${versionToUpgradeNumber} because it is higher than the latest fleet server version ${maxFleetServerVersion}.`;
|
||||
}
|
||||
|
||||
if (
|
||||
!force &&
|
||||
semverGt(versionToUpgradeNumber, maxFleetServerVersion) &&
|
||||
!differsOnlyInPatch(versionToUpgradeNumber, maxFleetServerVersion)
|
||||
) {
|
||||
return `Cannot upgrade to version ${versionToUpgradeNumber} because it is higher than the latest fleet server version ${maxFleetServerVersion}.`;
|
||||
}
|
||||
const fleetServerMajorGt =
|
||||
semverMajor(maxFleetServerVersion) > semverMajor(versionToUpgradeNumber);
|
||||
const fleetServerMajorEqMinorGte =
|
||||
semverMajor(maxFleetServerVersion) === semverMajor(versionToUpgradeNumber) &&
|
||||
semverMinor(maxFleetServerVersion) >= semverMinor(versionToUpgradeNumber);
|
||||
|
||||
const fleetServerMajorGt =
|
||||
semverMajor(maxFleetServerVersion) > semverMajor(versionToUpgradeNumber);
|
||||
const fleetServerMajorEqMinorGte =
|
||||
semverMajor(maxFleetServerVersion) === semverMajor(versionToUpgradeNumber) &&
|
||||
semverMinor(maxFleetServerVersion) >= semverMinor(versionToUpgradeNumber);
|
||||
|
||||
// When force is enabled, only the major and minor versions are checked
|
||||
if (force && !(fleetServerMajorGt || fleetServerMajorEqMinorGte)) {
|
||||
return `Cannot force upgrade to version ${versionToUpgradeNumber} because it does not satisfy the major and minor of the latest fleet server version ${maxFleetServerVersion}.`;
|
||||
// When force is enabled, only the major and minor versions are checked
|
||||
if (force && !(fleetServerMajorGt || fleetServerMajorEqMinorGte)) {
|
||||
return `Cannot force upgrade to version ${versionToUpgradeNumber} because it does not satisfy the major and minor of the latest fleet server version ${maxFleetServerVersion}.`;
|
||||
}
|
||||
} catch (e) {
|
||||
return e.message;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -80,21 +83,25 @@ export const isAgentVersionLessThanFleetServer = (
|
|||
if (!maxFleetServerVersion || !versionToUpgradeNumber) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
!force &&
|
||||
semverGt(versionToUpgradeNumber, maxFleetServerVersion) &&
|
||||
!differsOnlyInPatch(versionToUpgradeNumber, maxFleetServerVersion)
|
||||
)
|
||||
return false;
|
||||
try {
|
||||
if (
|
||||
!force &&
|
||||
semverGt(versionToUpgradeNumber, maxFleetServerVersion) &&
|
||||
!differsOnlyInPatch(versionToUpgradeNumber, maxFleetServerVersion)
|
||||
)
|
||||
return false;
|
||||
|
||||
const fleetServerMajorGt =
|
||||
semverMajor(maxFleetServerVersion) > semverMajor(versionToUpgradeNumber);
|
||||
const fleetServerMajorEqMinorGte =
|
||||
semverMajor(maxFleetServerVersion) === semverMajor(versionToUpgradeNumber) &&
|
||||
semverMinor(maxFleetServerVersion) >= semverMinor(versionToUpgradeNumber);
|
||||
const fleetServerMajorGt =
|
||||
semverMajor(maxFleetServerVersion) > semverMajor(versionToUpgradeNumber);
|
||||
const fleetServerMajorEqMinorGte =
|
||||
semverMajor(maxFleetServerVersion) === semverMajor(versionToUpgradeNumber) &&
|
||||
semverMinor(maxFleetServerVersion) >= semverMinor(versionToUpgradeNumber);
|
||||
|
||||
// When force is enabled, only the major and minor versions are checked
|
||||
if (force && !(fleetServerMajorGt || fleetServerMajorEqMinorGte)) {
|
||||
// When force is enabled, only the major and minor versions are checked
|
||||
if (force && !(fleetServerMajorGt || fleetServerMajorEqMinorGte)) {
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -169,6 +169,46 @@ describe('AgentUpgradeAgentModal', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should display invalid input if version is not a valid semver', async () => {
|
||||
const { utils } = renderAgentUpgradeAgentModal({
|
||||
agents: [
|
||||
{
|
||||
id: 'agent1',
|
||||
local_metadata: { host: 'abc', elastic: { agent: { version: '8.12.0' } } },
|
||||
},
|
||||
] as any,
|
||||
agentCount: 1,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
const input = utils.getByTestId('agentUpgradeModal.VersionInput');
|
||||
fireEvent.input(input, { target: { value: '8.14' } });
|
||||
expect(
|
||||
utils.getByText('Invalid version, please use a valid semver version, e.g. 8.14.0')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not display invalid input if version is a valid semver', async () => {
|
||||
const { utils } = renderAgentUpgradeAgentModal({
|
||||
agents: [
|
||||
{
|
||||
id: 'agent1',
|
||||
local_metadata: { host: 'abc', elastic: { agent: { version: '8.12.0' } } },
|
||||
},
|
||||
] as any,
|
||||
agentCount: 1,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
const input = utils.getByTestId('agentUpgradeModal.VersionInput');
|
||||
fireEvent.input(input, { target: { value: '8.14.0+build123456789' } });
|
||||
expect(
|
||||
utils.queryByText('Invalid version, please use a valid semver version, e.g. 8.14.0')
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it('should display available version options', async () => {
|
||||
mockSendGetAgentsAvailableVersions.mockClear();
|
||||
mockSendGetAgentsAvailableVersions.mockResolvedValue({
|
||||
|
|
|
@ -28,6 +28,7 @@ import type { EuiComboBoxOptionOption } from '@elastic/eui';
|
|||
|
||||
import semverGt from 'semver/functions/gt';
|
||||
import semverLt from 'semver/functions/lt';
|
||||
import semverValid from 'semver/functions/valid';
|
||||
|
||||
import {
|
||||
AGENT_UPGRADE_COOLDOWN_IN_MIN,
|
||||
|
@ -267,6 +268,18 @@ export const AgentUpgradeAgentModal: React.FunctionComponent<AgentUpgradeAgentMo
|
|||
}
|
||||
}, [agents, fleetServerAgents, isSingleAgent, latestAgentVersion, selectedVersion]);
|
||||
|
||||
const semverErrors = useMemo(() => {
|
||||
if (!selectedVersion[0].value) return undefined;
|
||||
if (!semverValid(selectedVersion[0].value)) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.upgradeAgents.invalidSemverError"
|
||||
defaultMessage="Invalid version, please use a valid semver version, e.g. 8.14.0"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}, [selectedVersion]);
|
||||
|
||||
const [selectedMaintenanceWindow, setSelectedMaintenanceWindow] = useState([
|
||||
isSmallBatch ? maintenanceOptions[0] : maintenanceOptions[1],
|
||||
]);
|
||||
|
@ -501,13 +514,15 @@ export const AgentUpgradeAgentModal: React.FunctionComponent<AgentUpgradeAgentMo
|
|||
defaultMessage: 'Upgrade version',
|
||||
})}
|
||||
fullWidth
|
||||
isInvalid={isInvalid}
|
||||
isInvalid={isInvalid || !!semverErrors}
|
||||
error={
|
||||
isInvalid ? (
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.upgradeAgents.versionRequiredText"
|
||||
defaultMessage="Version is required"
|
||||
/>
|
||||
) : !!semverErrors ? (
|
||||
semverErrors
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
|
@ -522,6 +537,7 @@ export const AgentUpgradeAgentModal: React.FunctionComponent<AgentUpgradeAgentMo
|
|||
setSelectedVersionStr(newValue);
|
||||
setSelectedVersion([{ label: newValue, value: newValue }]);
|
||||
}}
|
||||
isInvalid={!!semverErrors}
|
||||
/>
|
||||
) : (
|
||||
<EuiComboBox
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue