Skip to content

Commit da3877c

Browse files
jillguyonnetkibanamachineelasticmachine
authored andcommitted
[Fleet] Implement agent upgrade rollback (elastic#247398)
## Summary Closes elastic/ingest-dev#6212 This PR adds Fleet support for Elastic Agent upgrade rollback (main issue: elastic/ingest-dev#3971). Key points: * Feature is behind `enableAgentRollback` feature flag * Feature is only available for Enterprise license * New single agent upgrade rollback API endpoint: `POST /api/fleet/agents/{agentId}/rollback` * New bulk agent upgrade rollback API endpoint: `POST /api/fleet/agents/bulk_rollback` * Upgrade rollback generates an `UPGRADE` type agent action with `rollback: true` and the rollback version set by the agent * For multiple agents, one action per rollback version is generated * Single agent and bulk upgrade rollback available from Fleet UI ### Screenshots Single agent confirmation modal: <img width="1919" height="773" alt="Screenshot 2025-12-23 at 16 06 09" src="https://github.com/user-attachments/assets/6b3948b6-b530-4c18-b2b8-8763234ae75d" /> Multiple agents confirmation modal: <img width="1919" height="773" alt="Screenshot 2025-12-23 at 16 02 29" src="https://github.com/user-attachments/assets/1b7e36b2-7c67-4b17-aae5-75a86fb7e015" /> Action result example: <img width="1919" height="881" alt="Screenshot 2025-12-23 at 16 01 24" src="https://github.com/user-attachments/assets/f84eb8bb-fc55-4fb7-ac22-48042da87718" /> If action was correctly created and rollback failed, it shows in the agent's upgrade details: <img width="1919" height="881" alt="Screenshot 2025-12-23 at 15 40 41" src="https://github.com/user-attachments/assets/3aa51fcb-12ac-4c59-920f-6d685605fe09" /> ### Testing Properly testing agent rollback is not straightforward right now as the feature is new and requires a "rollbackable" upgrade. See elastic/elastic-agent#11143 for steps. Alternatively, the agent document can be directly edited with fake upgrade rollback information in order to test that Fleet behaves correctly (the action will fail in this case). Scenarios to check: * Single agent * If no upgrade rollback, UI element should be disabled and API request should fail fast * Same if agent has expired rollback * If agent has a valid rollback, an `UPGRADE` type action should be created with `rollback: true` and the correct rollback version * Multiple agents * Should work with list of agent ids (manual selection in the UI) * Should work with agent kuery (select all in the UI) * If agents have different rollback versions, there should be one action per version * Agents not found and agents with no (valid) rollback should generate corresponding action errors ### Checklist - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [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 - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) - [x] Review the [backport guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing) and apply applicable `backport:*` labels. ### Identify risks This is a new experimental feature behind the `enableAgentRollback` feature flag and leaves existing flows largely untouched. ## Release note Adds capability for rolling back a recent upgrade of a Fleet-managed Elastic Agent upgrade using Fleet UI or API. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
1 parent 495350f commit da3877c

File tree

38 files changed

+2900
-35
lines changed

38 files changed

+2900
-35
lines changed

oas_docs/bundle.json

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22070,6 +22070,32 @@
2207022070
"nullable": true,
2207122071
"type": "array"
2207222072
},
22073+
"upgrade": {
22074+
"additionalProperties": false,
22075+
"properties": {
22076+
"rollbacks": {
22077+
"items": {
22078+
"additionalProperties": false,
22079+
"properties": {
22080+
"valid_until": {
22081+
"type": "string"
22082+
},
22083+
"version": {
22084+
"type": "string"
22085+
}
22086+
},
22087+
"required": [
22088+
"valid_until",
22089+
"version"
22090+
],
22091+
"type": "object"
22092+
},
22093+
"maxItems": 100,
22094+
"type": "array"
22095+
}
22096+
},
22097+
"type": "object"
22098+
},
2207322099
"upgrade_attempts": {
2207422100
"items": {
2207522101
"type": "string"
@@ -24418,6 +24444,32 @@
2441824444
"nullable": true,
2441924445
"type": "array"
2442024446
},
24447+
"upgrade": {
24448+
"additionalProperties": false,
24449+
"properties": {
24450+
"rollbacks": {
24451+
"items": {
24452+
"additionalProperties": false,
24453+
"properties": {
24454+
"valid_until": {
24455+
"type": "string"
24456+
},
24457+
"version": {
24458+
"type": "string"
24459+
}
24460+
},
24461+
"required": [
24462+
"valid_until",
24463+
"version"
24464+
],
24465+
"type": "object"
24466+
},
24467+
"maxItems": 100,
24468+
"type": "array"
24469+
}
24470+
},
24471+
"type": "object"
24472+
},
2442124473
"upgrade_attempts": {
2442224474
"items": {
2442324475
"type": "string"
@@ -24925,6 +24977,32 @@
2492524977
"nullable": true,
2492624978
"type": "array"
2492724979
},
24980+
"upgrade": {
24981+
"additionalProperties": false,
24982+
"properties": {
24983+
"rollbacks": {
24984+
"items": {
24985+
"additionalProperties": false,
24986+
"properties": {
24987+
"valid_until": {
24988+
"type": "string"
24989+
},
24990+
"version": {
24991+
"type": "string"
24992+
}
24993+
},
24994+
"required": [
24995+
"valid_until",
24996+
"version"
24997+
],
24998+
"type": "object"
24999+
},
25000+
"maxItems": 100,
25001+
"type": "array"
25002+
}
25003+
},
25004+
"type": "object"
25005+
},
2492825006
"upgrade_attempts": {
2492925007
"items": {
2493025008
"type": "string"

oas_docs/bundle.serverless.json

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22070,6 +22070,32 @@
2207022070
"nullable": true,
2207122071
"type": "array"
2207222072
},
22073+
"upgrade": {
22074+
"additionalProperties": false,
22075+
"properties": {
22076+
"rollbacks": {
22077+
"items": {
22078+
"additionalProperties": false,
22079+
"properties": {
22080+
"valid_until": {
22081+
"type": "string"
22082+
},
22083+
"version": {
22084+
"type": "string"
22085+
}
22086+
},
22087+
"required": [
22088+
"valid_until",
22089+
"version"
22090+
],
22091+
"type": "object"
22092+
},
22093+
"maxItems": 100,
22094+
"type": "array"
22095+
}
22096+
},
22097+
"type": "object"
22098+
},
2207322099
"upgrade_attempts": {
2207422100
"items": {
2207522101
"type": "string"
@@ -24418,6 +24444,32 @@
2441824444
"nullable": true,
2441924445
"type": "array"
2442024446
},
24447+
"upgrade": {
24448+
"additionalProperties": false,
24449+
"properties": {
24450+
"rollbacks": {
24451+
"items": {
24452+
"additionalProperties": false,
24453+
"properties": {
24454+
"valid_until": {
24455+
"type": "string"
24456+
},
24457+
"version": {
24458+
"type": "string"
24459+
}
24460+
},
24461+
"required": [
24462+
"valid_until",
24463+
"version"
24464+
],
24465+
"type": "object"
24466+
},
24467+
"maxItems": 100,
24468+
"type": "array"
24469+
}
24470+
},
24471+
"type": "object"
24472+
},
2442124473
"upgrade_attempts": {
2442224474
"items": {
2442324475
"type": "string"
@@ -24925,6 +24977,32 @@
2492524977
"nullable": true,
2492624978
"type": "array"
2492724979
},
24980+
"upgrade": {
24981+
"additionalProperties": false,
24982+
"properties": {
24983+
"rollbacks": {
24984+
"items": {
24985+
"additionalProperties": false,
24986+
"properties": {
24987+
"valid_until": {
24988+
"type": "string"
24989+
},
24990+
"version": {
24991+
"type": "string"
24992+
}
24993+
},
24994+
"required": [
24995+
"valid_until",
24996+
"version"
24997+
],
24998+
"type": "object"
24999+
},
25000+
"maxItems": 100,
25001+
"type": "array"
25002+
}
25003+
},
25004+
"type": "object"
25005+
},
2492825006
"upgrade_attempts": {
2492925007
"items": {
2493025008
"type": "string"

oas_docs/output/kibana.serverless.yaml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29185,6 +29185,24 @@ paths:
2918529185
maxItems: 3
2918629186
nullable: true
2918729187
type: array
29188+
upgrade:
29189+
additionalProperties: false
29190+
type: object
29191+
properties:
29192+
rollbacks:
29193+
items:
29194+
additionalProperties: false
29195+
type: object
29196+
properties:
29197+
valid_until:
29198+
type: string
29199+
version:
29200+
type: string
29201+
required:
29202+
- valid_until
29203+
- version
29204+
maxItems: 100
29205+
type: array
2918829206
upgrade_attempts:
2918929207
items:
2919029208
type: string
@@ -29708,6 +29726,24 @@ paths:
2970829726
maxItems: 3
2970929727
nullable: true
2971029728
type: array
29729+
upgrade:
29730+
additionalProperties: false
29731+
type: object
29732+
properties:
29733+
rollbacks:
29734+
items:
29735+
additionalProperties: false
29736+
type: object
29737+
properties:
29738+
valid_until:
29739+
type: string
29740+
version:
29741+
type: string
29742+
required:
29743+
- valid_until
29744+
- version
29745+
maxItems: 100
29746+
type: array
2971129747
upgrade_attempts:
2971229748
items:
2971329749
type: string
@@ -30087,6 +30123,24 @@ paths:
3008730123
maxItems: 3
3008830124
nullable: true
3008930125
type: array
30126+
upgrade:
30127+
additionalProperties: false
30128+
type: object
30129+
properties:
30130+
rollbacks:
30131+
items:
30132+
additionalProperties: false
30133+
type: object
30134+
properties:
30135+
valid_until:
30136+
type: string
30137+
version:
30138+
type: string
30139+
required:
30140+
- valid_until
30141+
- version
30142+
maxItems: 100
30143+
type: array
3009030144
upgrade_attempts:
3009130145
items:
3009230146
type: string

oas_docs/output/kibana.yaml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31744,6 +31744,24 @@ paths:
3174431744
maxItems: 3
3174531745
nullable: true
3174631746
type: array
31747+
upgrade:
31748+
additionalProperties: false
31749+
type: object
31750+
properties:
31751+
rollbacks:
31752+
items:
31753+
additionalProperties: false
31754+
type: object
31755+
properties:
31756+
valid_until:
31757+
type: string
31758+
version:
31759+
type: string
31760+
required:
31761+
- valid_until
31762+
- version
31763+
maxItems: 100
31764+
type: array
3174731765
upgrade_attempts:
3174831766
items:
3174931767
type: string
@@ -32267,6 +32285,24 @@ paths:
3226732285
maxItems: 3
3226832286
nullable: true
3226932287
type: array
32288+
upgrade:
32289+
additionalProperties: false
32290+
type: object
32291+
properties:
32292+
rollbacks:
32293+
items:
32294+
additionalProperties: false
32295+
type: object
32296+
properties:
32297+
valid_until:
32298+
type: string
32299+
version:
32300+
type: string
32301+
required:
32302+
- valid_until
32303+
- version
32304+
maxItems: 100
32305+
type: array
3227032306
upgrade_attempts:
3227132307
items:
3227232308
type: string
@@ -32646,6 +32682,24 @@ paths:
3264632682
maxItems: 3
3264732683
nullable: true
3264832684
type: array
32685+
upgrade:
32686+
additionalProperties: false
32687+
type: object
32688+
properties:
32689+
rollbacks:
32690+
items:
32691+
additionalProperties: false
32692+
type: object
32693+
properties:
32694+
valid_until:
32695+
type: string
32696+
version:
32697+
type: string
32698+
required:
32699+
- valid_until
32700+
- version
32701+
maxItems: 100
32702+
type: array
3264932703
upgrade_attempts:
3265032704
items:
3265132705
type: string

x-pack/platform/plugins/shared/fleet/common/constants/agent_policy.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export const AGENT_POLICY_DEFAULT_MONITORING_DATASETS = [
3636

3737
export const LICENSE_FOR_SCHEDULE_UPGRADE = 'platinum';
3838
export const LICENSE_FOR_AGENT_MIGRATION = 'enterprise';
39+
export const LICENSE_FOR_AGENT_ROLLBACK = 'enterprise';
3940

4041
export const DEFAULT_MAX_AGENT_POLICIES_WITH_INACTIVITY_TIMEOUT = 750;
4142

x-pack/platform/plugins/shared/fleet/common/constants/routes.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ export const AGENT_API_ROUTES = {
193193
DELETE_UPLOAD_FILE_PATTERN: `${API_ROOT}/agents/files/{fileId}`,
194194
PRIVILEGE_LEVEL_CHANGE_PATTERN: `${API_ROOT}/agents/{agentId}/privilege_level_change`,
195195
BULK_PRIVILEGE_LEVEL_CHANGE_PATTERN: `${API_ROOT}/agents/bulk_privilege_level_change`,
196+
ROLLBACK_PATTERN: `${API_ROOT}/agents/{agentId}/rollback`,
197+
BULK_ROLLBACK_PATTERN: `${API_ROOT}/agents/bulk_rollback`,
196198
};
197199

198200
export const AGENTLESS_POLICIES_ROUTES = {

x-pack/platform/plugins/shared/fleet/common/experimental_features.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const _allowedExperimentalValues = {
2626
enableEsqlViewInstall: false,
2727
enableSloTemplates: false,
2828
newBrowseIntegrationUx: false, // When enabled integrations, browse integrations page will use the new UX.
29+
enableAgentRollback: false, // When enabled, agent upgrade rollback will be available in the API and UI.
2930
};
3031

3132
/**

x-pack/platform/plugins/shared/fleet/common/services/routes.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,9 @@ export const agentRouteService = {
351351
postChangeAgentPrivilegeLevel: (agentId: string) =>
352352
AGENT_API_ROUTES.PRIVILEGE_LEVEL_CHANGE_PATTERN.replace('{agentId}', agentId),
353353
postBulkChangeAgentPrivilegeLevel: () => AGENT_API_ROUTES.BULK_PRIVILEGE_LEVEL_CHANGE_PATTERN,
354+
postAgentRollback: (agentId: string) =>
355+
AGENT_API_ROUTES.ROLLBACK_PATTERN.replace('{agentId}', agentId),
356+
postBulkAgentRollback: () => AGENT_API_ROUTES.BULK_ROLLBACK_PATTERN,
354357
};
355358

356359
export const outputRoutesService = {

0 commit comments

Comments
 (0)