Skip to content

Commit 5aed1a1

Browse files
authored
[Ingest Manager] add upgrade action (#77412) (#78411)
* at agents/agent-id/upgrade endpoint * handle upgrade ack * let upgrade endpoint accept url and version * wrap action data in data prop * decrypt data of actions * type upgrade action and decrypt data in ack * error if trying to update to diff version of kibana * add some integration tests * untype * fix test * update integration test * reset upgraded_at when upgrading * use defaultIngestErrorHandler * use ack_data instead of data * copy data to ack_data
1 parent 6a62246 commit 5aed1a1

File tree

16 files changed

+292
-11
lines changed

16 files changed

+292
-11
lines changed

x-pack/plugins/ingest_manager/common/constants/routes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export const AGENT_API_ROUTES = {
9090
REASSIGN_PATTERN: `${FLEET_API_ROOT}/agents/{agentId}/reassign`,
9191
BULK_REASSIGN_PATTERN: `${FLEET_API_ROOT}/agents/bulk_reassign`,
9292
STATUS_PATTERN: `${FLEET_API_ROOT}/agent-status`,
93+
UPGRADE_PATTERN: `${FLEET_API_ROOT}/agents/{agentId}/upgrade`,
9394
};
9495

9596
export const ENROLLMENT_API_KEY_ROUTES = {

x-pack/plugins/ingest_manager/common/services/agent_status.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ export function getAgentStatus(agent: Agent, now: number = Date.now()): AgentSta
1919
if (!agent.last_checkin) {
2020
return 'enrolling';
2121
}
22+
if (agent.upgrade_started_at && !agent.upgraded_at) {
23+
return 'upgrading';
24+
}
2225

2326
const msLastCheckIn = new Date(lastCheckIn || 0).getTime();
2427
const msSinceLastCheckIn = new Date().getTime() - msLastCheckIn;

x-pack/plugins/ingest_manager/common/types/models/agent.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ export type AgentStatus =
1919
| 'warning'
2020
| 'enrolling'
2121
| 'unenrolling'
22+
| 'upgrading'
2223
| 'degraded';
2324

24-
export type AgentActionType = 'CONFIG_CHANGE' | 'UNENROLL';
25-
25+
export type AgentActionType = 'CONFIG_CHANGE' | 'UNENROLL' | 'UPGRADE';
2626
export interface NewAgentAction {
2727
type: AgentActionType;
2828
data?: any;
@@ -65,7 +65,6 @@ export type AgentPolicyActionSOAttributes = CommonAgentActionSOAttributes & {
6565
policy_id: string;
6666
policy_revision: number;
6767
};
68-
6968
export type BaseAgentActionSOAttributes = AgentActionSOAttributes | AgentPolicyActionSOAttributes;
7069

7170
export interface NewAgentEvent {
@@ -110,6 +109,8 @@ interface AgentBase {
110109
enrolled_at: string;
111110
unenrolled_at?: string;
112111
unenrollment_started_at?: string;
112+
upgraded_at?: string;
113+
upgrade_started_at?: string;
113114
shared_id?: string;
114115
access_api_key_id?: string;
115116
default_api_key?: string;

x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,20 @@ export interface PostAgentUnenrollRequest {
113113
// eslint-disable-next-line @typescript-eslint/no-empty-interface
114114
export interface PostAgentUnenrollResponse {}
115115

116+
export interface PostAgentUpgradeRequest {
117+
params: {
118+
agentId: string;
119+
};
120+
}
116121
export interface PostBulkAgentUnenrollRequest {
117122
body: {
118123
agents: string[] | string;
119124
force?: boolean;
120125
};
121126
}
122127

128+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
129+
export interface PostAgentUpgradeResponse {}
123130
// eslint-disable-next-line @typescript-eslint/no-empty-interface
124131
export interface PostBulkAgentUnenrollResponse {}
125132

x-pack/plugins/ingest_manager/server/routes/agent/index.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
PutAgentReassignRequestSchema,
3030
PostBulkAgentReassignRequestSchema,
3131
PostAgentEnrollRequestBodyJSONSchema,
32+
PostAgentUpgradeRequestSchema,
3233
} from '../../types';
3334
import {
3435
getAgentsHandler,
@@ -48,6 +49,7 @@ import { postNewAgentActionHandlerBuilder } from './actions_handlers';
4849
import { appContextService } from '../../services';
4950
import { postAgentUnenrollHandler, postBulkAgentsUnenrollHandler } from './unenroll_handler';
5051
import { IngestManagerConfigType } from '../..';
52+
import { postAgentUpgradeHandler } from './upgrade_handler';
5153

5254
const ajv = new Ajv({
5355
coerceTypes: true,
@@ -215,7 +217,15 @@ export const registerRoutes = (router: IRouter, config: IngestManagerConfigType)
215217
},
216218
getAgentStatusForAgentPolicyHandler
217219
);
218-
220+
// upgrade agent
221+
router.post(
222+
{
223+
path: AGENT_API_ROUTES.UPGRADE_PATTERN,
224+
validate: PostAgentUpgradeRequestSchema,
225+
options: { tags: [`access:${PLUGIN_ID}-all`] },
226+
},
227+
postAgentUpgradeHandler
228+
);
219229
// Bulk reassign
220230
router.post(
221231
{
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { RequestHandler } from 'src/core/server';
8+
import { TypeOf } from '@kbn/config-schema';
9+
import { PostAgentUpgradeResponse } from '../../../common/types';
10+
import { PostAgentUpgradeRequestSchema } from '../../types';
11+
import * as AgentService from '../../services/agents';
12+
import { appContextService } from '../../services';
13+
import { defaultIngestErrorHandler } from '../../errors';
14+
15+
export const postAgentUpgradeHandler: RequestHandler<
16+
TypeOf<typeof PostAgentUpgradeRequestSchema.params>,
17+
undefined,
18+
TypeOf<typeof PostAgentUpgradeRequestSchema.body>
19+
> = async (context, request, response) => {
20+
const soClient = context.core.savedObjects.client;
21+
const { version, source_uri: sourceUri } = request.body;
22+
23+
// temporarily only allow upgrading to the same version as the installed kibana version
24+
const kibanaVersion = appContextService.getKibanaVersion();
25+
if (kibanaVersion !== version) {
26+
return response.customError({
27+
statusCode: 400,
28+
body: {
29+
message: `cannot upgrade agent to ${version} because it is different than the installed kibana version ${kibanaVersion}`,
30+
},
31+
});
32+
}
33+
34+
try {
35+
await AgentService.sendUpgradeAgentAction({
36+
soClient,
37+
agentId: request.params.agentId,
38+
version,
39+
sourceUri,
40+
});
41+
42+
const body: PostAgentUpgradeResponse = {};
43+
return response.ok({ body });
44+
} catch (error) {
45+
return defaultIngestErrorHandler({ error, response });
46+
}
47+
};

x-pack/plugins/ingest_manager/server/saved_objects/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ const savedObjectTypes: { [key: string]: SavedObjectsType } = {
6868
enrolled_at: { type: 'date' },
6969
unenrolled_at: { type: 'date' },
7070
unenrollment_started_at: { type: 'date' },
71+
upgraded_at: { type: 'date' },
72+
upgrade_started_at: { type: 'date' },
7173
access_api_key_id: { type: 'keyword' },
7274
version: { type: 'keyword' },
7375
user_provided_metadata: { type: 'flattened' },

x-pack/plugins/ingest_manager/server/services/agents/acks.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
} from '../../constants';
2929
import { getAgentActionByIds } from './actions';
3030
import { forceUnenrollAgent } from './unenroll';
31+
import { ackAgentUpgraded } from './upgrade';
3132

3233
const ALLOWED_ACKNOWLEDGEMENT_TYPE: string[] = ['ACTION_RESULT'];
3334

@@ -80,6 +81,11 @@ export async function acknowledgeAgentActions(
8081
await forceUnenrollAgent(soClient, agent.id);
8182
}
8283

84+
const upgradeAction = actions.find((action) => action.type === 'UPGRADE');
85+
if (upgradeAction) {
86+
await ackAgentUpgraded(soClient, upgradeAction);
87+
}
88+
8389
const configChangeAction = getLatestConfigChangePolicyActionIfUpdated(agent, actions);
8490

8591
await soClient.bulkUpdate<AgentSOAttributes | AgentActionSOAttributes>([

x-pack/plugins/ingest_manager/server/services/agents/actions.ts

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,11 @@ export async function getAgentPolicyActionByIds(
225225
);
226226
}
227227

228-
export async function getNewActionsSince(soClient: SavedObjectsClientContract, timestamp: string) {
228+
export async function getNewActionsSince(
229+
soClient: SavedObjectsClientContract,
230+
timestamp: string,
231+
decryptData: boolean = true
232+
) {
229233
const filter = nodeTypes.function.buildNode('and', [
230234
nodeTypes.function.buildNode(
231235
'not',
@@ -243,14 +247,33 @@ export async function getNewActionsSince(soClient: SavedObjectsClientContract, t
243247
}
244248
),
245249
]);
246-
const res = await soClient.find<AgentActionSOAttributes>({
247-
type: AGENT_ACTION_SAVED_OBJECT_TYPE,
248-
filter,
249-
});
250250

251-
return res.saved_objects
251+
const actions = (
252+
await soClient.find<AgentActionSOAttributes>({
253+
type: AGENT_ACTION_SAVED_OBJECT_TYPE,
254+
filter,
255+
})
256+
).saved_objects
252257
.filter(isAgentActionSavedObject)
253258
.map((so) => savedObjectToAgentAction(so));
259+
260+
if (!decryptData) {
261+
return actions;
262+
}
263+
264+
return await Promise.all(
265+
actions.map(async (action) => {
266+
// Get decrypted actions
267+
return savedObjectToAgentAction(
268+
await appContextService
269+
.getEncryptedSavedObjects()
270+
.getDecryptedAsInternalUser<AgentActionSOAttributes>(
271+
AGENT_ACTION_SAVED_OBJECT_TYPE,
272+
action.id
273+
)
274+
);
275+
})
276+
);
254277
}
255278

256279
export async function getLatestConfigChangeAction(

x-pack/plugins/ingest_manager/server/services/agents/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export * from './events';
99
export * from './checkin';
1010
export * from './enroll';
1111
export * from './unenroll';
12+
export * from './upgrade';
1213
export * from './status';
1314
export * from './crud';
1415
export * from './update';

0 commit comments

Comments
 (0)