diff --git a/x-pack/plugins/fleet/common/services/is_agent_upgradeable.test.ts b/x-pack/plugins/fleet/common/services/is_agent_upgradeable.test.ts index 7ab016239702de..dbc5b5515b6bb0 100644 --- a/x-pack/plugins/fleet/common/services/is_agent_upgradeable.test.ts +++ b/x-pack/plugins/fleet/common/services/is_agent_upgradeable.test.ts @@ -11,6 +11,7 @@ import { getRecentUpgradeInfoForAgent, isAgentUpgradeable, isAgentUpgrading, + getNotUpgradeableMessage, } from './is_agent_upgradeable'; const getAgent = ({ @@ -241,6 +242,154 @@ describe('Fleet - isAgentUpgradeable', () => { }); }); +describe('Fleet - getNotUpgradeableMessage', () => { + it('if agent reports not upgradeable with agent version < latest agent version', () => { + expect(getNotUpgradeableMessage(getAgent({ version: '7.9.0' }), '8.0.0')).toBe( + 'agent cannot be upgraded through Fleet. It may be running in a container or it is not installed as a service.' + ); + }); + + it('if agent reports not upgradeable with agent version > latest agent version', () => { + expect(getNotUpgradeableMessage(getAgent({ version: '8.0.0' }), '7.9.0')).toBe( + 'agent cannot be upgraded through Fleet. It may be running in a container or it is not installed as a service.' + ); + }); + + it('returns false if agent reports not upgradeable with agent version === latest agent version', () => { + expect(getNotUpgradeableMessage(getAgent({ version: '8.0.0' }), '8.0.0')).toBe( + 'agent cannot be upgraded through Fleet. It may be running in a container or it is not installed as a service.' + ); + }); + + it('if agent reports upgradeable, with agent version === latest agent version', () => { + expect( + getNotUpgradeableMessage(getAgent({ version: '8.0.0', upgradeable: true }), '8.0.0') + ).toBe('agent is already running on the latest available version.'); + }); + + it('if agent reports upgradeable, with agent version > latest agent version', () => { + expect( + getNotUpgradeableMessage(getAgent({ version: '8.0.0', upgradeable: true }), '7.9.0') + ).toBe('agent is running on a version greater than the latest available version.'); + }); + + it('if agent reports upgradeable, but agent is unenrolling', () => { + expect( + getNotUpgradeableMessage( + getAgent({ version: '7.9.0', upgradeable: true, unenrolling: true }), + '8.0.0' + ) + ).toBe('agent is being unenrolled.'); + }); + + it('if agent reports upgradeable, but agent is unenrolled', () => { + expect( + getNotUpgradeableMessage( + getAgent({ version: '7.9.0', upgradeable: true, unenrolled: true }), + '8.0.0' + ) + ).toBe('agent has been unenrolled.'); + }); + + it('Returns no error message if agent reports upgradeable, with agent version < latest agent version', () => { + expect( + getNotUpgradeableMessage(getAgent({ version: '7.9.0', upgradeable: true }), '8.0.0') + ).toBeUndefined(); + }); + + it('if agent reports upgradeable, with agent snapshot version === latest agent version', () => { + expect( + getNotUpgradeableMessage(getAgent({ version: '7.9.0-SNAPSHOT', upgradeable: true }), '7.9.0') + ).toBe('agent is already running on the latest available version.'); + }); + + it('it does not return message if agent reports upgradeable, with upgrade to agent snapshot version newer than latest agent version', () => { + expect( + getNotUpgradeableMessage( + getAgent({ version: '8.10.2', upgradeable: true }), + '8.10.2', + '8.11.0-SNAPSHOT' + ) + ).toBeUndefined(); + }); + + it('if agent reports upgradeable, with target version < current agent version ', () => { + expect( + getNotUpgradeableMessage(getAgent({ version: '7.9.0', upgradeable: true }), '8.0.0', '7.8.0') + ).toBe('agent does not support downgrades.'); + }); + + it('if agent reports upgradeable, with target version == current agent version ', () => { + expect( + getNotUpgradeableMessage(getAgent({ version: '7.9.0', upgradeable: true }), '8.0.0', '7.9.0') + ).toBe('agent is already running on the selected version.'); + }); + + it('if agent with no upgrade details reports upgradeable, but is already upgrading', () => { + expect( + getNotUpgradeableMessage( + getAgent({ version: '7.9.0', upgradeable: true, upgrading: true }), + '8.0.0' + ) + ).toBe('agent is already being upgraded.'); + }); + + it('if agent reports upgradeable, but has an upgrade status other than failed', () => { + expect( + getNotUpgradeableMessage( + getAgent({ + version: '7.9.0', + upgradeable: true, + upgradeDetails: { + target_version: '8.0.0', + action_id: 'XXX', + state: 'UPG_REQUESTED', + }, + }), + '8.0.0' + ) + ).toBe('agent is already being upgraded.'); + }); + + it('it does not return a message if agent reports upgradeable and has a failed upgrade status', () => { + expect( + getNotUpgradeableMessage( + getAgent({ + version: '7.9.0', + upgradeable: true, + upgradeDetails: { + target_version: '8.0.0', + action_id: 'XXX', + state: 'UPG_FAILED', + metadata: { + error_msg: 'Upgrade timed out', + }, + }, + }), + '8.0.0' + ) + ).toBeUndefined(); + }); + + it('if the agent reports upgradeable but was upgraded less than 10 minutes ago', () => { + expect( + getNotUpgradeableMessage( + getAgent({ version: '7.9.0', upgradeable: true, minutesSinceUpgrade: 9 }), + '8.0.0' + ) + ).toContain('please wait'); + }); + + it('if agent reports upgradeable and was upgraded more than 10 minutes ago', () => { + expect( + getNotUpgradeableMessage( + getAgent({ version: '7.9.0', upgradeable: true, minutesSinceUpgrade: 11 }), + '8.0.0' + ) + ).toBeUndefined(); + }); +}); + describe('hasAgentBeenUpgradedRecently', () => { it('returns true if the agent was upgraded less than 10 minutes ago', () => { expect( diff --git a/x-pack/plugins/fleet/common/services/is_agent_upgradeable.ts b/x-pack/plugins/fleet/common/services/is_agent_upgradeable.ts index ac21e24520c768..d36010aa13b981 100644 --- a/x-pack/plugins/fleet/common/services/is_agent_upgradeable.ts +++ b/x-pack/plugins/fleet/common/services/is_agent_upgradeable.ts @@ -8,11 +8,27 @@ import semverCoerce from 'semver/functions/coerce'; import semverLt from 'semver/functions/lt'; import semverGt from 'semver/functions/gt'; +import semverEq from 'semver/functions/eq'; +import moment from 'moment'; import type { Agent } from '../types'; export const AGENT_UPGRADE_COOLDOWN_IN_MIN = 10; +// Error messages for agent not upgradeable +export const VERSION_MISSING_ERROR = `agent version is missing.`; +export const UNENROLLED_ERROR = `agent has been unenrolled.`; +export const ONGOING_UNEROLLMENT_ERROR = `agent is being unenrolled.`; +export const NOT_UPGRADEABLE_ERROR = `agent cannot be upgraded through Fleet. It may be running in a container or it is not installed as a service.`; +export const ALREADY_UPGRADED_ERROR = `agent is already being upgraded.`; +export const INVALID_VERSION_ERROR = 'agent version is not valid.'; +export const SELECTED_VERSION_ERROR = 'the selected version is not valid.'; +export const RUNNING_SELECTED_VERSION_ERROR = `agent is already running on the selected version.`; +export const DOWNGRADE_NOT_ALLOWED_ERROR = `agent does not support downgrades.`; +export const LATEST_VERSION_NOT_VALID_ERROR = 'latest version is not valid.'; +export const AGENT_ALREADY_ON_LATEST_ERROR = `agent is already running on the latest available version.`; +export const AGENT_ON_GREATER_VERSION_ERROR = `agent is running on a version greater than the latest available version.`; + export function isAgentUpgradeable( agent: Agent, latestAgentVersion: string, @@ -42,21 +58,76 @@ export function isAgentUpgradeable( return isAgentVersionLessThanLatest(agentVersion, latestAgentVersion); } +// Based on the previous, returns a detailed message explaining why the agent is not upgradeable +export const getNotUpgradeableMessage = ( + 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; + } else { + return VERSION_MISSING_ERROR; + } + if (agent.unenrolled_at) { + return UNENROLLED_ERROR; + } + if (agent.unenrollment_started_at) { + return ONGOING_UNEROLLMENT_ERROR; + } + if (!agent.local_metadata.elastic.agent.upgradeable) { + return NOT_UPGRADEABLE_ERROR; + } + if (isAgentUpgrading(agent)) { + return ALREADY_UPGRADED_ERROR; + } + if (getRecentUpgradeInfoForAgent(agent).hasBeenUpgradedRecently) { + const timeToWaitMins = getRecentUpgradeInfoForAgent(agent).timeToWaitMins; + const elapsedMinsSinceUpgrade = getRecentUpgradeInfoForAgent(agent).elapsedMinsSinceUpgrade; + return `agent was upgraded ${elapsedMinsSinceUpgrade} minutes ago, please wait ${timeToWaitMins} minutes before attempting the upgrade again.`; + } + const agentVersionNumber = semverCoerce(agentVersion); + if (!agentVersionNumber) return INVALID_VERSION_ERROR; + + if (versionToUpgrade !== undefined) { + const versionToUpgradeNumber = semverCoerce(versionToUpgrade); + if (!versionToUpgradeNumber) return SELECTED_VERSION_ERROR; + + if (semverEq(agentVersionNumber, versionToUpgradeNumber)) return RUNNING_SELECTED_VERSION_ERROR; + + if (semverLt(versionToUpgradeNumber, agentVersionNumber)) return DOWNGRADE_NOT_ALLOWED_ERROR; + + // explicitly allow this case - the agent is upgradeable + if (semverGt(versionToUpgradeNumber, agentVersionNumber)) return undefined; + } + + const latestAgentVersionNumber = semverCoerce(latestAgentVersion); + if (!latestAgentVersionNumber) return LATEST_VERSION_NOT_VALID_ERROR; + + if (semverEq(agentVersionNumber, latestAgentVersionNumber)) return AGENT_ALREADY_ON_LATEST_ERROR; + + if (semverGt(agentVersionNumber, latestAgentVersionNumber)) return AGENT_ON_GREATER_VERSION_ERROR; + + // in all the other cases, the agent is upgradeable; don't return any message. + return undefined; +}; + 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'); + if (!agentVersionNumber) throw new Error(`${INVALID_VERSION_ERROR}`); const latestAgentVersionNumber = semverCoerce(latestAgentVersion); - if (!latestAgentVersionNumber) throw new Error('latest version is not valid'); + if (!latestAgentVersionNumber) throw new Error(`${LATEST_VERSION_NOT_VALID_ERROR}`); return semverLt(agentVersionNumber, latestAgentVersionNumber); }; const isNotDowngrade = (agentVersion: string, versionToUpgrade: string) => { const agentVersionNumber = semverCoerce(agentVersion); - if (!agentVersionNumber) throw new Error('agent version is not valid'); + if (!agentVersionNumber) throw new Error(`${INVALID_VERSION_ERROR}`); const versionToUpgradeNumber = semverCoerce(versionToUpgrade); - if (!versionToUpgradeNumber) throw new Error('target version is not valid'); + if (!versionToUpgradeNumber) throw new Error(`${SELECTED_VERSION_ERROR}`); return semverGt(versionToUpgradeNumber, agentVersionNumber); }; @@ -64,19 +135,27 @@ const isNotDowngrade = (agentVersion: string, versionToUpgrade: string) => { export function getRecentUpgradeInfoForAgent(agent: Agent): { hasBeenUpgradedRecently: boolean; timeToWaitMs: number; + elapsedMinsSinceUpgrade: number; + timeToWaitMins: number; } { if (!agent.upgraded_at) { return { hasBeenUpgradedRecently: false, timeToWaitMs: 0, + timeToWaitMins: 0, + elapsedMinsSinceUpgrade: 0, }; } - const elaspedSinceUpgradeInMillis = Date.now() - Date.parse(agent.upgraded_at); - const timeToWaitMs = AGENT_UPGRADE_COOLDOWN_IN_MIN * 6e4 - elaspedSinceUpgradeInMillis; - const hasBeenUpgradedRecently = elaspedSinceUpgradeInMillis / 6e4 < AGENT_UPGRADE_COOLDOWN_IN_MIN; + const elapsedSinceUpgradeInMillis = Date.now() - Date.parse(agent.upgraded_at); + const elapsedMins = moment.duration(elapsedSinceUpgradeInMillis, 'milliseconds').asMinutes(); + const elapsedMinsSinceUpgrade = Math.ceil(elapsedMins); - return { hasBeenUpgradedRecently, timeToWaitMs }; + const timeToWaitMs = AGENT_UPGRADE_COOLDOWN_IN_MIN * 6e4 - elapsedSinceUpgradeInMillis; + const hasBeenUpgradedRecently = elapsedSinceUpgradeInMillis / 6e4 < AGENT_UPGRADE_COOLDOWN_IN_MIN; + const timeToWait = moment.duration(timeToWaitMs, 'milliseconds').asMinutes(); + const timeToWaitMins = Math.ceil(timeToWait); + return { hasBeenUpgradedRecently, timeToWaitMs, elapsedMinsSinceUpgrade, timeToWaitMins }; } export function isAgentUpgrading(agent: Agent) { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_list_table.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_list_table.tsx index c52f1bb6588aee..d555c163c0fc00 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_list_table.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_list_table.tsx @@ -31,6 +31,8 @@ import { Tags } from '../../components/tags'; import type { AgentMetrics } from '../../../../../../../common/types'; import { formatAgentCPU, formatAgentMemory } from '../../services/agent_metrics'; +import { getNotUpgradeableMessage } from '../../../../../../../common/services/is_agent_upgradeable'; + import { AgentUpgradeStatus } from './agent_upgrade_status'; import { EmptyPrompt } from './empty_prompt'; @@ -303,6 +305,7 @@ export const AgentListTable: React.FC = (props: Props) => { agentUpgradeStartedAt={agent.upgrade_started_at} agentUpgradedAt={agent.upgraded_at} agentUpgradeDetails={agent.upgrade_details} + notUpgradeableMessage={getNotUpgradeableMessage(agent, latestAgentVersion)} /> diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_upgrade_status.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_upgrade_status.tsx index ab4835757f94d2..203b490f9f3ef0 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_upgrade_status.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_upgrade_status.tsx @@ -219,7 +219,14 @@ export const AgentUpgradeStatus: React.FC<{ agentUpgradeStartedAt?: string | null; agentUpgradedAt?: string | null; agentUpgradeDetails?: AgentUpgradeDetails; -}> = ({ isAgentUpgradable, agentUpgradeStartedAt, agentUpgradedAt, agentUpgradeDetails }) => { + notUpgradeableMessage?: string | null; +}> = ({ + isAgentUpgradable, + agentUpgradeStartedAt, + agentUpgradedAt, + agentUpgradeDetails, + notUpgradeableMessage, +}) => { const isAgentUpgrading = useMemo( () => agentUpgradeStartedAt && !agentUpgradedAt, [agentUpgradeStartedAt, agentUpgradedAt] @@ -227,6 +234,24 @@ export const AgentUpgradeStatus: React.FC<{ const status = useMemo(() => getStatusComponents(agentUpgradeDetails), [agentUpgradeDetails]); const minVersion = '8.12'; + if (!isAgentUpgradable && notUpgradeableMessage) { + return ( + + } + color="subdued" + /> + ); + } + if (isAgentUpgradable) { return ( diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.test.tsx index 1a18b6e27f5ebc..17e415691a8265 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.test.tsx @@ -29,6 +29,7 @@ jest.mock('../../../../hooks', () => { }), sendPostBulkAgentUpgrade: jest.fn(), useAgentVersion: jest.fn().mockReturnValue('8.10.2'), + useKibanaVersion: jest.fn().mockReturnValue('8.10.2'), }; }); @@ -203,4 +204,28 @@ describe('AgentUpgradeAgentModal', () => { expect(el).toBeDisabled(); }); }); + + it('should disable submit button and display a warning for a single agent that is not upgradeable', async () => { + const { utils } = renderAgentUpgradeAgentModal({ + agents: [ + { + status: 'offline', + upgrade_started_at: '2022-11-21T12:27:24Z', + id: 'agent1', + local_metadata: { elastic: { agent: { version: '8.9.0' } } }, + }, + ] as any, + agentCount: 2, + }); + await waitFor(() => { + expect(utils.queryByText(/The selected agent is not upgradeable/)).toBeInTheDocument(); + expect( + utils.queryByText( + /Reason: agent cannot be upgraded through Fleet. It may be running in a container or it is not installed as a service./ + ) + ).toBeInTheDocument(); + const el = utils.getByTestId('confirmModalConfirmButton'); + expect(el).toBeDisabled(); + }); + }); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.tsx index 0acb4296befdc4..28bb86fdb53828 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.tsx @@ -48,6 +48,10 @@ import { } from '../../../../hooks'; import { sendGetAgentsAvailableVersions } from '../../../../hooks'; +import { + isAgentUpgradeable, + getNotUpgradeableMessage, +} from '../../../../../../../common/services/is_agent_upgradeable'; import { FALLBACK_VERSIONS, @@ -127,7 +131,6 @@ export const AgentUpgradeAgentModal: React.FunctionComponent Array.isArray(agentsOrQuery) ? agentsOrQuery.map((agent) => agent.id) : agentsOrQuery; const { error } = - isSingleAgent && !isScheduled + isSingleAgent && + !isScheduled && + isAgentUpgradeable(agents[0], latestAgentVersion || '', selectedVersion[0].value) ? await sendPostAgentUpgrade((agents[0] as Agent).id, { version, force: isUpdating, @@ -332,7 +337,13 @@ export const AgentUpgradeAgentModal: React.FunctionComponent } - confirmButtonDisabled={isSubmitting || noVersions || (isUpdating && updatingAgents === 0)} + confirmButtonDisabled={ + isSubmitting || + noVersions || + (isUpdating && updatingAgents === 0) || + (isSingleAgent && + !isAgentUpgradeable(agents[0], latestAgentVersion || '', selectedVersion[0].value)) + } confirmButtonText={ isSingleAgent ? ( ) : isSingleAgent ? ( - <> -

+ !isAgentUpgradeable(agents[0], latestAgentVersion || '', selectedVersion[0].value) ? ( + + } + > -

- {isUpdating && ( + + ) : ( + <>

- - - +

- )} - + {isUpdating && ( +

+ + + +

+ )} + + ) ) : ( + > + + ) : null} diff --git a/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts b/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts index 391c721e2ef9fb..3b1cd89ff1364f 100644 --- a/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts +++ b/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts @@ -25,6 +25,7 @@ import { isAgentUpgradeable, AGENT_UPGRADE_COOLDOWN_IN_MIN, isAgentUpgrading, + getNotUpgradeableMessage, } from '../../../common/services'; import { getMaxVersion } from '../../../common/services/get_min_max_version'; import { getAgentById } from '../../services/agents'; @@ -114,7 +115,11 @@ export const postAgentUpgradeHandler: RequestHandler< return response.customError({ statusCode: 400, body: { - message: `agent ${request.params.agentId} is not upgradeable`, + message: `Agent ${request.params.agentId} is not upgradeable: ${getNotUpgradeableMessage( + agent, + latestAgentVersion, + version + )}`, }, }); } diff --git a/x-pack/plugins/fleet/server/services/agents/upgrade_action_runner.ts b/x-pack/plugins/fleet/server/services/agents/upgrade_action_runner.ts index 157eaa688c746c..61e3ad42c8aaec 100644 --- a/x-pack/plugins/fleet/server/services/agents/upgrade_action_runner.ts +++ b/x-pack/plugins/fleet/server/services/agents/upgrade_action_runner.ts @@ -10,7 +10,11 @@ import type { SavedObjectsClientContract, ElasticsearchClient } from '@kbn/core/ import { v4 as uuidv4 } from 'uuid'; import moment from 'moment'; -import { getRecentUpgradeInfoForAgent, isAgentUpgradeable } from '../../../common/services'; +import { + getRecentUpgradeInfoForAgent, + isAgentUpgradeable, + getNotUpgradeableMessage, +} from '../../../common/services'; import type { Agent } from '../../types'; @@ -86,7 +90,13 @@ export async function upgradeBatch( getRecentUpgradeInfoForAgent(agent).hasBeenUpgradedRecently || (!options.force && !isAgentUpgradeable(agent, latestAgentVersion, options.version)); if (isNotAllowed) { - throw new FleetError(`Agent ${agent.id} is not upgradeable`); + throw new FleetError( + `Agent ${agent.id} is not upgradeable: ${getNotUpgradeableMessage( + agent, + latestAgentVersion, + options.version + )}` + ); } if (!options.force && isHostedAgent(hostedPolicies, agent)) { diff --git a/x-pack/test/fleet_api_integration/apis/agents/upgrade.ts b/x-pack/test/fleet_api_integration/apis/agents/upgrade.ts index 786733b0011e2c..a18e24f360be94 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/upgrade.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/upgrade.ts @@ -141,13 +141,16 @@ export default function (providerContext: FtrProviderContext) { }, }, }); - await supertest + const res = await supertest .post(`/api/fleet/agents/agent1/upgrade`) .set('kbn-xsrf', 'xxx') .send({ version: fleetServerVersionSnapshot, }) .expect(400); + expect(res.body.message).to.equal( + 'Agent agent1 is not upgradeable: agent is already running on the selected version.' + ); }); it('should respond 200 if upgrading agent with version the same as snapshot version and force flag is passed', async () => { @@ -248,26 +251,30 @@ export default function (providerContext: FtrProviderContext) { }, }, }); - await supertest + const res = await supertest .post(`/api/fleet/agents/agent1/upgrade`) .set('kbn-xsrf', 'xxx') .send({ version: '6.0.0', }) .expect(400); + expect(res.body.message).to.equal( + 'Agent agent1 is not upgradeable: agent does not support downgrades.' + ); }); it('should respond 400 if trying to upgrade an agent that is unenrolling', async () => { await supertest.post(`/api/fleet/agents/agent1/unenroll`).set('kbn-xsrf', 'xxx').send({ revoke: true, }); - await supertest + const res = await supertest .post(`/api/fleet/agents/agent1/upgrade`) .set('kbn-xsrf', 'xxx') .send({ version: fleetServerVersion, }) .expect(400); + expect(res.body.message).to.equal('cannot upgrade an unenrolling or unenrolled agent'); }); it('should respond 400 if trying to upgrade an agent that is unenrolled', async () => { @@ -281,13 +288,14 @@ export default function (providerContext: FtrProviderContext) { }, }, }); - await supertest + const res = await supertest .post(`/api/fleet/agents/agent1/upgrade`) .set('kbn-xsrf', 'xxx') .send({ version: fleetServerVersion, }) .expect(400); + expect(res.body.message).to.equal('cannot upgrade an unenrolling or unenrolled agent'); }); it('should respond 400 if trying to upgrade an agent that is not upgradeable', async () => { @@ -298,7 +306,9 @@ export default function (providerContext: FtrProviderContext) { version: fleetServerVersion, }) .expect(400); - expect(res.body.message).to.equal('agent agent1 is not upgradeable'); + expect(res.body.message).to.equal( + 'Agent agent1 is not upgradeable: agent cannot be upgraded through Fleet. It may be running in a container or it is not installed as a service.' + ); }); it('enrolled in a hosted agent policy should respond 400 to upgrade and not update the agent SOs', async () => {