77
88import type { RequestHandler } from '@kbn/core/server' ;
99import type { TypeOf } from '@kbn/config-schema' ;
10+ import type { SavedObjectsClientContract , ElasticsearchClient } from '@kbn/core/server' ;
11+
1012import semverCoerce from 'semver/functions/coerce' ;
13+ import semverGt from 'semver/functions/gt' ;
1114
1215import type { PostAgentUpgradeResponse , PostBulkAgentUpgradeResponse } from '../../../common/types' ;
1316import type { PostAgentUpgradeRequestSchema , PostBulkAgentUpgradeRequestSchema } from '../../types' ;
1417import * as AgentService from '../../services/agents' ;
1518import { appContextService } from '../../services' ;
1619import { defaultIngestErrorHandler } from '../../errors' ;
20+ import { SO_SEARCH_LIMIT } from '../../../common' ;
1721import { isAgentUpgradeable } from '../../../common/services' ;
18- import { getAgentById } from '../../services/agents' ;
22+ import { getAgentById , getAgentsByKuery } from '../../services/agents' ;
23+ import { PACKAGE_POLICY_SAVED_OBJECT_TYPE , AGENTS_PREFIX } from '../../constants' ;
24+
25+ import { getMaxVersion } from '../../../common/services/get_max_version' ;
26+
27+ import { packagePolicyService } from '../../services/package_policy' ;
1928
2029export const postAgentUpgradeHandler : RequestHandler <
2130 TypeOf < typeof PostAgentUpgradeRequestSchema . params > ,
@@ -28,7 +37,7 @@ export const postAgentUpgradeHandler: RequestHandler<
2837 const { version, source_uri : sourceUri , force } = request . body ;
2938 const kibanaVersion = appContextService . getKibanaVersion ( ) ;
3039 try {
31- checkVersionIsSame ( version , kibanaVersion ) ;
40+ checkKibanaVersion ( version , kibanaVersion ) ;
3241 checkSourceUriAllowed ( sourceUri ) ;
3342 } catch ( err ) {
3443 return response . customError ( {
@@ -90,8 +99,9 @@ export const postBulkAgentsUpgradeHandler: RequestHandler<
9099 } = request . body ;
91100 const kibanaVersion = appContextService . getKibanaVersion ( ) ;
92101 try {
93- checkVersionIsSame ( version , kibanaVersion ) ;
102+ checkKibanaVersion ( version , kibanaVersion ) ;
94103 checkSourceUriAllowed ( sourceUri ) ;
104+ await checkFleetServerVersion ( version , agents , soClient , esClient ) ;
95105 } catch ( err ) {
96106 return response . customError ( {
97107 statusCode : 400 ,
@@ -125,17 +135,17 @@ export const postBulkAgentsUpgradeHandler: RequestHandler<
125135 }
126136} ;
127137
128- export const checkVersionIsSame = ( version : string , kibanaVersion : string ) => {
138+ export const checkKibanaVersion = ( version : string , kibanaVersion : string ) => {
129139 // get version number only in case "-SNAPSHOT" is in it
130140 const kibanaVersionNumber = semverCoerce ( kibanaVersion ) ?. version ;
131141 if ( ! kibanaVersionNumber ) throw new Error ( `kibanaVersion ${ kibanaVersionNumber } is not valid` ) ;
132142 const versionToUpgradeNumber = semverCoerce ( version ) ?. version ;
133143 if ( ! versionToUpgradeNumber )
134144 throw new Error ( `version to upgrade ${ versionToUpgradeNumber } is not valid` ) ;
135- // temporarily only allow upgrading to the same version as the installed kibana version
136- if ( kibanaVersionNumber !== versionToUpgradeNumber )
145+
146+ if ( semverGt ( version , kibanaVersion ) )
137147 throw new Error (
138- `cannot upgrade agent to ${ versionToUpgradeNumber } because it is different than the installed kibana version ${ kibanaVersionNumber } `
148+ `cannot upgrade agent to ${ versionToUpgradeNumber } because it is higher than the installed kibana version ${ kibanaVersionNumber } `
139149 ) ;
140150} ;
141151
@@ -146,3 +156,67 @@ const checkSourceUriAllowed = (sourceUri?: string) => {
146156 ) ;
147157 }
148158} ;
159+
160+ // Check the installed fleet server versions
161+ // Allow upgrading if the agents to upgrade include fleet server agents
162+ const checkFleetServerVersion = async (
163+ versionToUpgradeNumber : string ,
164+ agentsIds : string | string [ ] ,
165+ soClient : SavedObjectsClientContract ,
166+ esClient : ElasticsearchClient
167+ ) => {
168+ let packagePolicyData ;
169+ try {
170+ packagePolicyData = await packagePolicyService . list ( soClient , {
171+ perPage : SO_SEARCH_LIMIT ,
172+ kuery : `${ PACKAGE_POLICY_SAVED_OBJECT_TYPE } .package.name: fleet_server` ,
173+ } ) ;
174+ } catch ( error ) {
175+ throw new Error ( error . message ) ;
176+ }
177+ const agentPoliciesIds = packagePolicyData ?. items . map ( ( item ) => item . policy_id ) ;
178+
179+ if ( agentPoliciesIds . length === 0 ) {
180+ return ;
181+ }
182+
183+ let agentsResponse ;
184+ try {
185+ agentsResponse = await getAgentsByKuery ( esClient , {
186+ showInactive : false ,
187+ perPage : SO_SEARCH_LIMIT ,
188+ kuery : `${ AGENTS_PREFIX } .policy_id:${ agentPoliciesIds . map ( ( id ) => `"${ id } "` ) . join ( ' or ' ) } ` ,
189+ } ) ;
190+ } catch ( error ) {
191+ throw new Error ( error . message ) ;
192+ }
193+
194+ const { agents : fleetServerAgents } = agentsResponse ;
195+
196+ if ( fleetServerAgents . length === 0 ) {
197+ return ;
198+ }
199+ const fleetServerIds = fleetServerAgents . map ( ( agent ) => agent . id ) ;
200+
201+ let hasFleetServerAgents : boolean ;
202+ if ( Array . isArray ( agentsIds ) ) {
203+ hasFleetServerAgents = agentsIds . some ( ( id ) => fleetServerIds . includes ( id ) ) ;
204+ } else {
205+ hasFleetServerAgents = fleetServerIds . includes ( agentsIds ) ;
206+ }
207+ if ( hasFleetServerAgents ) {
208+ return ;
209+ }
210+
211+ const fleetServerVersions = fleetServerAgents . map (
212+ ( agent ) => agent . local_metadata . elastic . agent . version
213+ ) as string [ ] ;
214+
215+ const maxFleetServerVersion = getMaxVersion ( fleetServerVersions ) ;
216+
217+ if ( semverGt ( versionToUpgradeNumber , maxFleetServerVersion ) ) {
218+ throw new Error (
219+ `cannot upgrade agent to ${ versionToUpgradeNumber } because it is higher than the latest fleet server version ${ maxFleetServerVersion } `
220+ ) ;
221+ }
222+ } ;
0 commit comments