Skip to content

Commit

Permalink
Collapse two unenroll functions into one (elastic#94848)
Browse files Browse the repository at this point in the history
## Summary

Refactoring in progress towards elastic#90437 

Collapse `forceUninstallAgents` into `uninstallAgents` into one function with an option. The pseudocode diff is

```diff
- function uninstallAgents() {
-   // filtering logic A
-   // side effects A
- }
- function forceUninstallAgents() {
-   // filtering logic A
-   // side effects B
- }

+ function uninstallAgents({ flag: boolean}) {
+   // filtering logic A
+   // if flag: side effects B
+   // else: side effects A
+ }
```

actually, there is [_one difference_](https://github.com/elastic/kibana/pull/94848/files/5564f253831a8a94e2b2bbb808afe6d2019016d7#diff-ecc3c625f2366949f1723e56b8477f6afb552ccfbcf3a71e0c28b2062fd05195) in the filtering logic
 
### Checklist
- [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
  • Loading branch information
John Schulz authored and kibanamachine committed Mar 18, 2021
1 parent 6f38b96 commit aae86af
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 72 deletions.
17 changes: 9 additions & 8 deletions x-pack/plugins/fleet/server/routes/agent/unenroll_handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,20 @@ export const postBulkAgentsUnenrollHandler: RequestHandler<
body: { message: 'Requires Gold license' },
});
}

const soClient = context.core.savedObjects.client;
const esClient = context.core.elasticsearch.client.asInternalUser;
const unenrollAgents =
request.body?.force === true ? AgentService.forceUnenrollAgents : AgentService.unenrollAgents;
const agentOptions = Array.isArray(request.body.agents)
? { agentIds: request.body.agents }
: { kuery: request.body.agents };

try {
if (Array.isArray(request.body.agents)) {
await unenrollAgents(soClient, esClient, { agentIds: request.body.agents });
} else {
await unenrollAgents(soClient, esClient, { kuery: request.body.agents });
}

await AgentService.unenrollAgents(soClient, esClient, {
...agentOptions,
force: request.body?.force,
});
const body: PostBulkAgentUnenrollResponse = {};

return response.ok({ body });
} catch (error) {
return defaultIngestErrorHandler({ error, response });
Expand Down
123 changes: 59 additions & 64 deletions x-pack/plugins/fleet/server/services/agents/unenroll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,45 +56,78 @@ export async function unenrollAgent(
export async function unenrollAgents(
soClient: SavedObjectsClientContract,
esClient: ElasticsearchClient,
options: GetAgentsOptions
options: GetAgentsOptions & { force?: boolean }
) {
// start with all agents specified
const agents = await getAgents(esClient, options);

// Filter to agents that are not already unenrolled, or unenrolling
const agentsEnrolled = agents.filter(
(agent) => !agent.unenrollment_started_at && !agent.unenrolled_at
);
// Filter to those not already unenrolled, or unenrolling
const agentsEnrolled = agents.filter((agent) => {
if (options.force) {
return !agent.unenrolled_at;
}
return !agent.unenrollment_started_at && !agent.unenrolled_at;
});
// And which are allowed to unenroll
const settled = await Promise.allSettled(
agentsEnrolled.map((agent) =>
unenrollAgentIsAllowed(soClient, esClient, agent.id).then((_) => agent)
)
);
const agentsToUpdate = agentsEnrolled.filter((_, index) => settled[index].status === 'fulfilled');

const now = new Date().toISOString();

// Create unenroll action for each agent
await bulkCreateAgentActions(
soClient,
esClient,
agentsToUpdate.map((agent) => ({
agent_id: agent.id,
created_at: now,
type: 'UNENROLL',
}))
);
if (options.force) {
// Get all API keys that need to be invalidated
const apiKeys = agentsToUpdate.reduce<string[]>((keys, agent) => {
if (agent.access_api_key_id) {
keys.push(agent.access_api_key_id);
}
if (agent.default_api_key_id) {
keys.push(agent.default_api_key_id);
}

return keys;
}, []);

// Invalidate all API keys
if (apiKeys.length) {
await APIKeyService.invalidateAPIKeys(soClient, apiKeys);
}
// Update the necessary agents
return bulkUpdateAgents(
esClient,
agentsToUpdate.map((agent) => ({
agentId: agent.id,
data: {
active: false,
unenrolled_at: now,
},
}))
);
} else {
// Create unenroll action for each agent
await bulkCreateAgentActions(
soClient,
esClient,
agentsToUpdate.map((agent) => ({
agent_id: agent.id,
created_at: now,
type: 'UNENROLL',
}))
);

// Update the necessary agents
return bulkUpdateAgents(
esClient,
agentsToUpdate.map((agent) => ({
agentId: agent.id,
data: {
unenrollment_started_at: now,
},
}))
);
// Update the necessary agents
return bulkUpdateAgents(
esClient,
agentsToUpdate.map((agent) => ({
agentId: agent.id,
data: {
unenrollment_started_at: now,
},
}))
);
}
}

export async function forceUnenrollAgent(
Expand All @@ -118,41 +151,3 @@ export async function forceUnenrollAgent(
unenrolled_at: new Date().toISOString(),
});
}

export async function forceUnenrollAgents(
soClient: SavedObjectsClientContract,
esClient: ElasticsearchClient,
options: GetAgentsOptions
) {
// Filter to agents that are not already unenrolled
const agents = await getAgents(esClient, options);
const agentsToUpdate = agents.filter((agent) => !agent.unenrolled_at);
const now = new Date().toISOString();
const apiKeys: string[] = [];

// Get all API keys that need to be invalidated
agentsToUpdate.forEach((agent) => {
if (agent.access_api_key_id) {
apiKeys.push(agent.access_api_key_id);
}
if (agent.default_api_key_id) {
apiKeys.push(agent.default_api_key_id);
}
});

// Invalidate all API keys
if (apiKeys.length) {
APIKeyService.invalidateAPIKeys(soClient, apiKeys);
}
// Update the necessary agents
return bulkUpdateAgents(
esClient,
agentsToUpdate.map((agent) => ({
agentId: agent.id,
data: {
active: false,
unenrolled_at: now,
},
}))
);
}

0 comments on commit aae86af

Please sign in to comment.