Repository navigation
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Credential retention and state-transition issues remain, alongside action-delivery defects and an invalid integration-test fixture.
6 open issues
UNENROLL incorrectly retains credentials for ordinary unenrollment · New UNENROLL action lacks expiration and is never delivered · New Re-enrollment fails to clear pending uninstall state · New Persisted rejection does not prevent later action delivery · New Action filtering uses stale agent state during long polling · New Filtering after fetch limit can block UNINSTALL delivery · New
1 file not reviewed
internal/pkg/model/schema.go: Generated file
What changed in this PR
Adds a pending-uninstall state so Fleet Server can retain agent credentials between UNENROLL and a follow-up UNINSTALL.
Changes:
- Defers credential invalidation until UNINSTALL acknowledgement.
- Restricts pending agents to UNINSTALL/CANCEL and records premature UNINSTALL failures.
- Adds schema support, tests, and a changelog entry.
| File | Description |
|---|---|
| model/schema.json | Defines pending-uninstall state. |
| internal/pkg/server/uninstall_action_integration_test.go | Tests the two-step uninstall flow. |
| internal/pkg/model/schema.go | Adds the generated agent field. |
| internal/pkg/dl/constants.go | Adds the persistence field constant. |
| internal/pkg/api/handleCheckin.go | Filters actions and records uninstall rejections. |
| internal/pkg/api/handleCheckin_test.go | Tests filtering and rejection results. |
| internal/pkg/api/handleAck.go | Separates pending and terminal acknowledgements. |
| internal/pkg/api/handleAck_test.go | Tests credential retention after UNENROLL. |
| changelog/fragments/1792579200-keep-credentials-after-unenroll-for-uninstall.yaml | Documents the lifecycle changes. |
Review effort: Balanced
| Agents: []string{resp.Item.Id}, | ||
| ActionID: unenrollActionID, | ||
| Type: string(api.UNENROLL), |
|
|
||
| now := time.Now().UTC().Format(time.RFC3339) | ||
| doc := bulk.UpdateFields{ | ||
| dl.FieldUninstallPending: true, |
| pendingActions, rejectedUninstalls := filterActions(zlog, agent.Id, pendingActions, agent.UninstallPending) | ||
| ct.rejectUninstallActions(r.Context(), zlog, agent.Id, rejectedUninstalls) | ||
| actions, ackToken = convertActions(zlog, agent.Id, pendingActions) |
| acdocs, rejectedUninstalls := filterActions(zlog, agent.Id, acdocs, agent.UninstallPending) | ||
| ct.rejectUninstallActions(ctx, zlog, agent.Id, rejectedUninstalls) |
| if action.Type != string(UNINSTALL) && action.Type != string(CANCEL) { | ||
| zlog.Info().Str(ecs.AgentID, agentID).Str(ecs.ActionID, action.ActionID).Str(ecs.ActionType, action.Type).Msg("Withholding non-uninstall action from pending-uninstall agent") | ||
| continue |
|
This pull request does not have a backport label. Could you fix it @rdner? 🙏
|
b456166 to
ba0701b
Compare
There was a problem hiding this comment.
🟡 Changes recommended
The integration fixture lacks the required uninstall capability, so it takes the legacy invalidation path rather than exercising the intended flow.
6 open issues
Test agent lacks the uninstallable capability metadata · New UNENROLL action lacks expiration and is never delivered Filtering after fetch limit can block UNINSTALL delivery Action filtering uses stale agent state during long polling Persisted rejection does not prevent later action delivery Re-enrollment fails to clear pending uninstall state
1 resolved since last review
1 file not reviewed
internal/pkg/model/schema.go: Generated file
Review effort: Balanced
| AgentAck(t, ctx, srv, gotUnenrollID, resp.Item.Id, resp.Item.AccessApiKey) | ||
| AssertAgentPendingUninstall(t, ctx, srv, resp.Item.Id) |
|
I haven't read this yet since it's a draft, but if we are changing the behaviour of the existing UNENROLL action, make sure it and the code that interacts with it in fleet-server tolerates older agent versions that will be missing any of the agent side changes you've made. |
- UNENROLL keeps the agent active and its access key valid, sets uninstall_pending instead of invalidating; only UNINSTALL/CANCEL are delivered afterwards. - UNENROLL invalidates the agent's output keys (e.g. Elasticsearch) so the unenrolled agent immediately stops shipping data. - UNINSTALL is rejected with a failed action result unless the agent is unenrolled; terminal invalidation happens on the UNINSTALL ack. Co-authored-by: Cursor <cursoragent@cursor.com>
ba0701b to
abd448c
Compare
|
@cmacknz it's already taken care of. The plan is that new agents will report The recover from UNENROLL will come as a separate follow up PR, otherwise this one would be hard to review. |
There was a problem hiding this comment.
🟡 Changes recommended
A failed rejection-result write can be permanently skipped when check-in advances the action cursor.
7 open issues
Test agent lacks the uninstallable capability metadata UNENROLL action lacks expiration and is never delivered Propagate rejection persistence failures · New Filtering after fetch limit can block UNINSTALL delivery Action filtering uses stale agent state during long polling Persisted rejection does not prevent later action delivery Re-enrollment fails to clear pending uninstall state
1 file not reviewed
internal/pkg/model/schema.go: Generated file
Review effort: Balanced
| if err := dl.CreateActionResult(ctx, ct.bulker, acr); err != nil { | ||
| zlog.Error().Err(err).Str(ecs.AgentID, agentID).Str(ecs.ActionID, action.ActionID).Msg("failed to write rejected UNINSTALL action result") |


What is the problem this PR solves?
The UNENROLL action is now the first step of the remote-uninstall flow. This PR changes how Fleet Server handles it:
uninstall_pendinginstead of invalidating, so the agent can still receive the follow-up UNINSTALL/CANCEL. While pending, only UNINSTALL/CANCEL are delivered (policy changes and all other actions are withheld)..fleet-actions-results. Terminal invalidation (access key invalidated, agent marked inactive) happens on the UNINSTALL ack.uninstallableflag in theirlocal_metadataenter the pending-uninstall flow. Older agents without the flag keep the previous behavior and have their credentials invalidated immediately on UNENROLL.How to test this PR locally
Tests have been added.
Design Checklist
- [ ] I have included fail safe mechanisms to limit the load on fleet-server: rate limiting, circuit breakers, caching, load shedding, etc.Checklist
- [ ] I have made corresponding changes to the documentation- [ ] I have made corresponding change to the default configuration files./changelog/fragmentsusing the changelog toolRelated issues
This is a follow up to #7895
Prerequisite to elastic/elastic-agent#16540