-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Fleet] Bulk upgrade api response change #95236
Merged
jfsiii
merged 13 commits into
elastic:master
from
jfsiii:bulk-upgrade-api-response-change
Mar 26, 2021
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
0cbbb41
Rearrange agent upgrade tests into groups for upgrade & bulk upgrade
059ed4f
Respect force flag in /agents/bulk_upgrade. Add test
e1b1142
WIP. reassign service is returning correct array of objects
ffd72d8
Fix response & add tests
338b2ec
Make test more explicit. Add variant for managed
a2d33cd
Merge branch 'master' into bulk-reassign-response-should-include-all-…
kibanamachine f88a90d
Merge branch 'master' into bulk-reassign-response-should-include-all-…
3f0381c
/bulk_reassign API error is string; not object
82bef5d
Add new response & test for bulk_uprade API
871ebcb
Unskip test & fix logic for force ugrade
3334dd1
Fix test for force upgrade
c4bfdf9
Merge branch 'master' into bulk-upgrade-api-response-change
374bb47
Merge branch 'master' into bulk-upgrade-api-response-change
kibanamachine File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,16 +7,23 @@ | |
|
||
import type { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; | ||
|
||
import type { AgentAction, AgentActionSOAttributes } from '../../types'; | ||
import type { Agent, AgentAction, AgentActionSOAttributes, BulkActionResult } from '../../types'; | ||
import { AGENT_ACTION_SAVED_OBJECT_TYPE } from '../../constants'; | ||
import { agentPolicyService } from '../../services'; | ||
import { IngestManagerError } from '../../errors'; | ||
import { AgentReassignmentError, IngestManagerError } from '../../errors'; | ||
import { isAgentUpgradeable } from '../../../common/services'; | ||
import { appContextService } from '../app_context'; | ||
|
||
import { bulkCreateAgentActions, createAgentAction } from './actions'; | ||
import type { GetAgentsOptions } from './crud'; | ||
import { getAgents, updateAgent, bulkUpdateAgents, getAgentPolicyForAgent } from './crud'; | ||
import { | ||
getAgentDocuments, | ||
getAgents, | ||
updateAgent, | ||
bulkUpdateAgents, | ||
getAgentPolicyForAgent, | ||
} from './crud'; | ||
import { searchHitToAgent } from './helpers'; | ||
|
||
export async function sendUpgradeAgentAction({ | ||
soClient, | ||
|
@@ -77,39 +84,75 @@ export async function ackAgentUpgraded( | |
export async function sendUpgradeAgentsActions( | ||
soClient: SavedObjectsClientContract, | ||
esClient: ElasticsearchClient, | ||
options: GetAgentsOptions & { | ||
options: ({ agents: Agent[] } | GetAgentsOptions) & { | ||
sourceUri: string | undefined; | ||
version: string; | ||
force?: boolean; | ||
} | ||
) { | ||
// Full set of agents | ||
const agentsGiven = await getAgents(esClient, options); | ||
const outgoingErrors: Record<Agent['id'], Error> = {}; | ||
let givenAgents: Agent[] = []; | ||
if ('agents' in options) { | ||
givenAgents = options.agents; | ||
} else if ('agentIds' in options) { | ||
const givenAgentsResults = await getAgentDocuments(esClient, options.agentIds); | ||
for (const agentResult of givenAgentsResults) { | ||
if (agentResult.found === false) { | ||
outgoingErrors[agentResult._id] = new AgentReassignmentError( | ||
`Cannot find agent ${agentResult._id}` | ||
); | ||
} else { | ||
givenAgents.push(searchHitToAgent(agentResult)); | ||
} | ||
} | ||
} else if ('kuery' in options) { | ||
givenAgents = await getAgents(esClient, options); | ||
} | ||
const givenOrder = | ||
'agentIds' in options ? options.agentIds : givenAgents.map((agent) => agent.id); | ||
|
||
// get any policy ids from upgradable agents | ||
const policyIdsToGet = new Set( | ||
givenAgents.filter((agent) => agent.policy_id).map((agent) => agent.policy_id!) | ||
); | ||
|
||
// get the agent policies for those ids | ||
const agentPolicies = await agentPolicyService.getByIDs(soClient, Array.from(policyIdsToGet), { | ||
fields: ['is_managed'], | ||
}); | ||
const managedPolicies = agentPolicies.reduce<Record<string, boolean>>((acc, policy) => { | ||
acc[policy.id] = policy.is_managed; | ||
return acc; | ||
}, {}); | ||
|
||
// Filter out agents currently unenrolling, unenrolled, or not upgradeable b/c of version check | ||
const kibanaVersion = appContextService.getKibanaVersion(); | ||
const upgradeableAgents = options.force | ||
? agentsGiven | ||
: agentsGiven.filter((agent) => isAgentUpgradeable(agent, kibanaVersion)); | ||
|
||
if (!options.force) { | ||
// get any policy ids from upgradable agents | ||
const policyIdsToGet = new Set( | ||
upgradeableAgents.filter((agent) => agent.policy_id).map((agent) => agent.policy_id!) | ||
); | ||
|
||
// get the agent policies for those ids | ||
const agentPolicies = await agentPolicyService.getByIDs(soClient, Array.from(policyIdsToGet), { | ||
fields: ['is_managed'], | ||
}); | ||
const agentResults = await Promise.allSettled( | ||
givenAgents.map(async (agent) => { | ||
const isAllowed = options.force || isAgentUpgradeable(agent, kibanaVersion); | ||
if (!isAllowed) { | ||
throw new IngestManagerError(`${agent.id} is not upgradeable`); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Needs |
||
} | ||
|
||
// throw if any of those agent policies are managed | ||
for (const policy of agentPolicies) { | ||
if (policy.is_managed) { | ||
throw new IngestManagerError(`Cannot upgrade agent in managed policy ${policy.id}`); | ||
if (!options.force && agent.policy_id && managedPolicies[agent.policy_id]) { | ||
throw new IngestManagerError(`Cannot upgrade agent in managed policy ${agent.policy_id}`); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Needs |
||
} | ||
return agent; | ||
}) | ||
); | ||
|
||
// Filter to agents that do not already use the new agent policy ID | ||
const agentsToUpdate = agentResults.reduce<Agent[]>((agents, result, index) => { | ||
if (result.status === 'fulfilled') { | ||
agents.push(result.value); | ||
} else { | ||
const id = givenAgents[index].id; | ||
outgoingErrors[id] = result.reason; | ||
} | ||
} | ||
return agents; | ||
}, []); | ||
|
||
// Create upgrade action for each agent | ||
const now = new Date().toISOString(); | ||
const data = { | ||
|
@@ -120,7 +163,7 @@ export async function sendUpgradeAgentsActions( | |
await bulkCreateAgentActions( | ||
soClient, | ||
esClient, | ||
upgradeableAgents.map((agent) => ({ | ||
agentsToUpdate.map((agent) => ({ | ||
agent_id: agent.id, | ||
created_at: now, | ||
data, | ||
|
@@ -129,14 +172,27 @@ export async function sendUpgradeAgentsActions( | |
})) | ||
); | ||
|
||
return await bulkUpdateAgents( | ||
await bulkUpdateAgents( | ||
esClient, | ||
upgradeableAgents.map((agent) => ({ | ||
agentsToUpdate.map((agent) => ({ | ||
agentId: agent.id, | ||
data: { | ||
upgraded_at: null, | ||
upgrade_started_at: now, | ||
}, | ||
})) | ||
); | ||
const orderedOut = givenOrder.map((agentId) => { | ||
const hasError = agentId in outgoingErrors; | ||
const result: BulkActionResult = { | ||
id: agentId, | ||
success: !hasError, | ||
}; | ||
if (hasError) { | ||
result.error = outgoingErrors[agentId]; | ||
} | ||
return result; | ||
}); | ||
|
||
return { items: orderedOut }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This error message needs to be wrapped in
i18n.translate