From 56a88605ae10b17b5749282f58b8a0e4b2b83cf0 Mon Sep 17 00:00:00 2001 From: Julia Bardi <90178898+juliaElastic@users.noreply.github.com> Date: Mon, 20 Mar 2023 16:32:27 +0100 Subject: [PATCH] [Fleet] Displaying policy changes in Agent activity (#153237) ## Summary Closes https://github.com/elastic/kibana/issues/141206 Added queries to `/action_status` API to query policy updates and query the corresponding revisions of agents. See more explanation here: https://github.com/elastic/kibana/issues/141206#issuecomment-1469723504 On UI displaying these `POLICY_CHANGE` actions with some additional info (policy name and new revision). To test: - Create agent policies and update them - See that the policy change appears in agent activity - Enroll agents to an agent policy and then update the policy - See that the policy change appears in agent activity, should go through In progress and then Complete state as the Agents receive the new policy change revision. image image ### Checklist - [x] 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/packages/kbn-i18n/README.md) - [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 --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../plugins/fleet/common/openapi/bundled.json | 49 ++++- .../plugins/fleet/common/openapi/bundled.yaml | 26 +++ .../openapi/paths/agents@action_status.yaml | 26 +++ .../fleet/common/types/models/agent.ts | 8 +- .../components/agent_activity_flyout.test.tsx | 68 ++++++ .../components/agent_activity_flyout.tsx | 73 ++++--- .../server/services/agents/action_status.ts | 160 +++++++++++++- x-pack/plugins/fleet/server/types/index.tsx | 1 + .../apis/agents/action_status.ts | 197 +++++++++++++----- 9 files changed, 520 insertions(+), 88 deletions(-) diff --git a/x-pack/plugins/fleet/common/openapi/bundled.json b/x-pack/plugins/fleet/common/openapi/bundled.json index 785ff50287cfa2c..9b9e9a64f2acf0f 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.json +++ b/x-pack/plugins/fleet/common/openapi/bundled.json @@ -1791,25 +1791,43 @@ ] }, "nbAgentsActioned": { - "type": "number" + "type": "number", + "description": "number of agents actioned" }, "nbAgentsActionCreated": { - "type": "number" + "type": "number", + "description": "number of agents included in action from kibana" }, "nbAgentsAck": { - "type": "number" + "type": "number", + "description": "number of agents that acknowledged the action" }, "nbAgentsFailed": { - "type": "number" + "type": "number", + "description": "number of agents that failed to execute the action" }, "version": { - "type": "string" + "type": "string", + "description": "agent version number (UPGRADE action)" }, "startTime": { - "type": "string" + "type": "string", + "description": "start time of action (scheduled actions)" }, "type": { - "type": "string" + "type": "string", + "enum": [ + "POLICY_REASSIGN", + "UPGRADE", + "UNENROLL", + "FORCE_UNENROLL", + "UPDATE_TAGS", + "CANCEL", + "REQUEST_DIAGNOSTICS", + "SETTINGS", + "POLICY_CHANGE", + "INPUT_ACTION" + ] }, "expiration": { "type": "string" @@ -1821,10 +1839,20 @@ "type": "string" }, "newPolicyId": { - "type": "string" + "type": "string", + "description": "new policy id (POLICY_REASSIGN action)" + }, + "policyId": { + "type": "string", + "description": "policy id (POLICY_CHANGE action)" + }, + "revision": { + "type": "string", + "description": "new policy revision (POLICY_CHANGE action)" }, "creationTime": { - "type": "string" + "type": "string", + "description": "creation time of action" }, "latestErrors": { "type": "array", @@ -1853,7 +1881,8 @@ "nbAgentsAck", "nbAgentsFailed", "status", - "creationTime" + "creationTime", + "type" ] } } diff --git a/x-pack/plugins/fleet/common/openapi/bundled.yaml b/x-pack/plugins/fleet/common/openapi/bundled.yaml index be098e146a90f59..8f5436703f6f77a 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.yaml +++ b/x-pack/plugins/fleet/common/openapi/bundled.yaml @@ -1123,18 +1123,35 @@ paths: - ROLLOUT_PASSED nbAgentsActioned: type: number + description: number of agents actioned nbAgentsActionCreated: type: number + description: number of agents included in action from kibana nbAgentsAck: type: number + description: number of agents that acknowledged the action nbAgentsFailed: type: number + description: number of agents that failed to execute the action version: type: string + description: agent version number (UPGRADE action) startTime: type: string + description: start time of action (scheduled actions) type: type: string + enum: + - POLICY_REASSIGN + - UPGRADE + - UNENROLL + - FORCE_UNENROLL + - UPDATE_TAGS + - CANCEL + - REQUEST_DIAGNOSTICS + - SETTINGS + - POLICY_CHANGE + - INPUT_ACTION expiration: type: string completionTime: @@ -1143,8 +1160,16 @@ paths: type: string newPolicyId: type: string + description: new policy id (POLICY_REASSIGN action) + policyId: + type: string + description: policy id (POLICY_CHANGE action) + revision: + type: string + description: new policy revision (POLICY_CHANGE action) creationTime: type: string + description: creation time of action latestErrors: type: array description: >- @@ -1168,6 +1193,7 @@ paths: - nbAgentsFailed - status - creationTime + - type required: - items '400': diff --git a/x-pack/plugins/fleet/common/openapi/paths/agents@action_status.yaml b/x-pack/plugins/fleet/common/openapi/paths/agents@action_status.yaml index 6daea7e6ebb2bbd..89bc0d6c891788c 100644 --- a/x-pack/plugins/fleet/common/openapi/paths/agents@action_status.yaml +++ b/x-pack/plugins/fleet/common/openapi/paths/agents@action_status.yaml @@ -36,18 +36,35 @@ get: - ROLLOUT_PASSED nbAgentsActioned: type: number + description: number of agents actioned nbAgentsActionCreated: type: number + description: number of agents included in action from kibana nbAgentsAck: type: number + description: number of agents that acknowledged the action nbAgentsFailed: type: number + description: number of agents that failed to execute the action version: type: string + description: agent version number (UPGRADE action) startTime: type: string + description: start time of action (scheduled actions) type: type: string + enum: + - POLICY_REASSIGN + - UPGRADE + - UNENROLL + - FORCE_UNENROLL + - UPDATE_TAGS + - CANCEL + - REQUEST_DIAGNOSTICS + - SETTINGS + - POLICY_CHANGE + - INPUT_ACTION expiration: type: string completionTime: @@ -56,8 +73,16 @@ get: type: string newPolicyId: type: string + description: new policy id (POLICY_REASSIGN action) + policyId: + type: string + description: policy id (POLICY_CHANGE action) + revision: + type: string + description: new policy revision (POLICY_CHANGE action) creationTime: type: string + description: creation time of action latestErrors: type: array description: latest errors that happened when the agents executed the action @@ -79,6 +104,7 @@ get: - nbAgentsFailed - status - creationTime + - type required: - items '400': diff --git a/x-pack/plugins/fleet/common/types/models/agent.ts b/x-pack/plugins/fleet/common/types/models/agent.ts index 0d5e15088219e48..11f259e95910210 100644 --- a/x-pack/plugins/fleet/common/types/models/agent.ts +++ b/x-pack/plugins/fleet/common/types/models/agent.ts @@ -44,7 +44,9 @@ export type AgentActionType = | 'CANCEL' | 'FORCE_UNENROLL' | 'UPDATE_TAGS' - | 'REQUEST_DIAGNOSTICS'; + | 'REQUEST_DIAGNOSTICS' + | 'POLICY_CHANGE' + | 'INPUT_ACTION'; type FleetServerAgentComponentStatusTuple = typeof FleetServerAgentComponentStatuses; export type FleetServerAgentComponentStatus = FleetServerAgentComponentStatusTuple[number]; @@ -152,7 +154,7 @@ export interface ActionStatus { nbAgentsFailed: number; version?: string; startTime?: string; - type?: string; + type: AgentActionType; // how many agents were actioned by the user nbAgentsActioned: number; status: 'COMPLETE' | 'EXPIRED' | 'CANCELLED' | 'FAILED' | 'IN_PROGRESS' | 'ROLLOUT_PASSED'; @@ -163,6 +165,8 @@ export interface ActionStatus { creationTime: string; hasRolloutPeriod?: boolean; latestErrors?: ActionErrorResult[]; + revision?: number; + policyId?: string; } export interface AgentDiagnostics { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_activity_flyout.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_activity_flyout.test.tsx index 40cdb35d9d42777..8f3473eb703a535 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_activity_flyout.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_activity_flyout.test.tsx @@ -426,4 +426,72 @@ describe('AgentActivityFlyout', () => { .textContent?.replace(/\s/g, '') ).toContain('Completed Sep 15, 2022 12:00 PM'.replace(/\s/g, '')); }); + + it('should render agent activity for policy change no agents', () => { + const mockActionStatuses = [ + { + actionId: 'action8', + nbAgentsActionCreated: 0, + nbAgentsAck: 0, + type: 'POLICY_CHANGE', + nbAgentsActioned: 0, + status: 'COMPLETE', + expiration: '2099-09-16T10:00:00.000Z', + policyId: 'policy1', + revision: 2, + creationTime: '2022-09-15T10:00:00.000Z', + nbAgentsFailed: 0, + completionTime: '2022-09-15T11:00:00.000Z', + }, + ]; + mockUseActionStatus.mockReturnValue({ + currentActions: mockActionStatuses, + abortUpgrade: mockAbortUpgrade, + isFirstLoading: true, + }); + const result = renderComponent(); + + expect(result.container.querySelector('[data-test-subj="statusTitle"]')!.textContent).toEqual( + 'Policy changed' + ); + expect( + result.container + .querySelector('[data-test-subj="statusDescription"]')! + .textContent?.replace(/\s/g, '') + ).toContain('Policy1 changed to revision 2 at Sep 15, 2022 10:00 AM.'.replace(/\s/g, '')); + }); + + it('should render agent activity for policy change with agents', () => { + const mockActionStatuses = [ + { + actionId: 'action8', + nbAgentsActionCreated: 3, + nbAgentsAck: 3, + type: 'POLICY_CHANGE', + nbAgentsActioned: 3, + status: 'COMPLETE', + expiration: '2099-09-16T10:00:00.000Z', + policyId: 'policy1', + revision: 2, + creationTime: '2022-09-15T10:00:00.000Z', + nbAgentsFailed: 0, + completionTime: '2022-09-15T11:00:00.000Z', + }, + ]; + mockUseActionStatus.mockReturnValue({ + currentActions: mockActionStatuses, + abortUpgrade: mockAbortUpgrade, + isFirstLoading: true, + }); + const result = renderComponent(); + + expect(result.container.querySelector('[data-test-subj="statusTitle"]')!.textContent).toEqual( + '3 agents applied policy change' + ); + expect( + result.container + .querySelector('[data-test-subj="statusDescription"]')! + .textContent?.replace(/\s/g, '') + ).toContain('Policy1 changed to revision 2 at Sep 15, 2022 10:00 AM.'.replace(/\s/g, '')); + }); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_activity_flyout.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_activity_flyout.tsx index 10033b51dc982d6..2c1def346111841 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_activity_flyout.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_activity_flyout.tsx @@ -82,6 +82,7 @@ export const AgentActivityFlyout: React.FunctionComponent<{ const currentActionsEnriched: ActionStatus[] = currentActions.map((a) => ({ ...a, newPolicyId: getAgentPolicyName(a.newPolicyId ?? ''), + policyId: getAgentPolicyName(a.policyId ?? ''), })); const inProgressActions = currentActionsEnriched.filter((a) => a.status === 'IN_PROGRESS'); @@ -309,9 +310,9 @@ const actionNames: { cancelledText: 'update settings', }, POLICY_CHANGE: { - inProgressText: 'Changing policy of', - completedText: 'changed policy', - cancelledText: 'change policy', + inProgressText: 'Applying policy change on', + completedText: 'applied policy change', + cancelledText: 'policy change', }, INPUT_ACTION: { inProgressText: 'Input action in progress of', @@ -370,28 +371,36 @@ const ActivityItem: React.FunctionComponent<{ action: ActionStatus; onClickViewAgents: (action: ActionStatus) => void; }> = ({ action, onClickViewAgents }) => { - const completeTitle = ( - - 0 - ? `, ${ - action.nbAgentsActioned - action.nbAgentsAck - } agent(s) offline during the rollout period` - : '', - }} - /> - - ); + const completeTitle = + action.type === 'POLICY_CHANGE' && action.nbAgentsActioned === 0 ? ( + + + + ) : ( + + 0 + ? `, ${ + action.nbAgentsActioned - action.nbAgentsAck + } agent(s) offline during the rollout period` + : '', + }} + /> + + ); const completedDescription = ( + ) : action.type === 'POLICY_CHANGE' ? ( + +

+ {action.policyId}{' '} + +

+
) : ( {completedDescription} ), diff --git a/x-pack/plugins/fleet/server/services/agents/action_status.ts b/x-pack/plugins/fleet/server/services/agents/action_status.ts index 0c31baab77d4ed6..58a144ad1b4c302 100644 --- a/x-pack/plugins/fleet/server/services/agents/action_status.ts +++ b/x-pack/plugins/fleet/server/services/agents/action_status.ts @@ -14,8 +14,14 @@ import type { ActionStatus, ActionErrorResult, ListWithKuery, + AgentActionType, } from '../../types'; -import { AGENT_ACTIONS_INDEX, AGENT_ACTIONS_RESULTS_INDEX, AGENTS_INDEX } from '../../../common'; +import { + AGENT_ACTIONS_INDEX, + AGENT_ACTIONS_RESULTS_INDEX, + AGENTS_INDEX, + AGENT_POLICY_INDEX, +} from '../../../common'; import { appContextService } from '..'; const PRECISION_THRESHOLD = 40000; @@ -172,7 +178,10 @@ export async function getActionStatuses( }); } - return results; + const policyChangeActions = await getPolicyChangeActions(esClient); + return [...results, ...policyChangeActions].sort((a: ActionStatus, b: ActionStatus) => + b.creationTime > a.creationTime ? 1 : -1 + ); } async function getHostNames(esClient: ElasticsearchClient, agentIds: string[]) { @@ -268,7 +277,7 @@ async function _getActions( nbAgentsAck: 0, version: hit._source.data?.version as string, startTime: source.start_time, - type: source.type, + type: source.type as AgentActionType, nbAgentsActioned: source.total ?? 0, status: isExpired ? 'EXPIRED' @@ -297,3 +306,148 @@ export const hasRolloutPeriodPassed = (source: FleetServerAgentAction) => .add(source.rollout_duration_seconds, 'seconds') .valueOf() : false; + +async function getPolicyChangeActions(esClient: ElasticsearchClient): Promise { + const latestAgentPoliciesRes = await esClient.search({ + index: AGENT_POLICY_INDEX, + size: 10, + query: { + bool: { + filter: [ + { + range: { + revision_idx: { + gt: 1, + }, + }, + }, + ], + }, + }, + sort: [ + { + '@timestamp': { + order: 'desc', + }, + }, + ], + _source: ['revision_idx', '@timestamp', 'policy_id'], + }); + + interface AgentPolicyRevision { + policyId: string; + revision: number; + timestamp: string; + agentsAssignedToPolicy: number; + agentsOnAtLeastThisRevision: number; + } + + const latestAgentPolicies: { [key: string]: AgentPolicyRevision } = + latestAgentPoliciesRes.hits.hits.reduce((acc, curr) => { + const hit = curr._source! as any; + acc[`${hit.policy_id}:${hit.revision_idx}`] = { + policyId: hit.policy_id, + revision: hit.revision_idx, + timestamp: hit['@timestamp'], + agentsAssignedToPolicy: 0, + agentsOnAtLeastThisRevision: 0, + }; + return acc; + }, {} as { [key: string]: AgentPolicyRevision }); + + const agentsPerPolicyRevisionRes = await esClient.search({ + index: AGENTS_INDEX, + size: 0, + // ignore unenrolled agents + query: { + bool: { must_not: [{ exists: { field: 'unenrolled_at' } }] }, + }, + aggs: { + policies: { + terms: { + field: 'policy_id', + size: 10, + }, + aggs: { + agents_per_rev: { + terms: { + field: 'policy_revision_idx', + size: 10, + }, + }, + }, + }, + }, + }); + + interface AgentsPerPolicyRev { + total: number; + agentsPerRev: Array<{ + revision: number; + agents: number; + }>; + } + + const agentsPerPolicyRevisionMap: { [key: string]: AgentsPerPolicyRev } = ( + agentsPerPolicyRevisionRes.aggregations!.policies as any + ).buckets.reduce( + ( + acc: { [key: string]: AgentsPerPolicyRev }, + policyBucket: { key: string; doc_count: number; agents_per_rev: any } + ) => { + const policyId = policyBucket.key; + const policyAgentCount = policyBucket.doc_count; + if (!acc[policyId]) + acc[policyId] = { + total: 0, + agentsPerRev: [], + }; + acc[policyId].total = policyAgentCount; + acc[policyId].agentsPerRev = policyBucket.agents_per_rev.buckets.map( + (agentsPerRev: { key: string; doc_count: number }) => { + return { + revision: agentsPerRev.key, + agents: agentsPerRev.doc_count, + }; + } + ); + return acc; + }, + {} + ); + + Object.values(latestAgentPolicies).forEach((agentPolicyRev) => { + const agentsPerPolicyRev = agentsPerPolicyRevisionMap[agentPolicyRev.policyId]; + if (agentsPerPolicyRev) { + agentPolicyRev.agentsAssignedToPolicy = agentsPerPolicyRev.total; + agentsPerPolicyRev.agentsPerRev.forEach((item) => { + if (agentPolicyRev.revision <= item.revision) { + agentPolicyRev.agentsOnAtLeastThisRevision += item.agents; + } + }); + } + }); + + const agentPolicyUpdateActions: ActionStatus[] = Object.entries(latestAgentPolicies).map( + ([updateKey, updateObj]: [updateKey: string, updateObj: AgentPolicyRevision]) => { + return { + actionId: updateKey, + creationTime: updateObj.timestamp, + completionTime: updateObj.timestamp, + type: 'POLICY_CHANGE', + nbAgentsActioned: updateObj.agentsAssignedToPolicy, + nbAgentsAck: updateObj.agentsOnAtLeastThisRevision, + nbAgentsActionCreated: updateObj.agentsAssignedToPolicy, + nbAgentsFailed: 0, + status: + updateObj.agentsAssignedToPolicy === updateObj.agentsOnAtLeastThisRevision + ? 'COMPLETE' + : 'IN_PROGRESS', + policyId: updateObj.policyId, + revision: updateObj.revision, + }; + } + ); + + return agentPolicyUpdateActions; +} diff --git a/x-pack/plugins/fleet/server/types/index.tsx b/x-pack/plugins/fleet/server/types/index.tsx index 82447d7c2ed1750..f51579186efe54c 100644 --- a/x-pack/plugins/fleet/server/types/index.tsx +++ b/x-pack/plugins/fleet/server/types/index.tsx @@ -12,6 +12,7 @@ export type { AgentStatus, AgentType, AgentAction, + AgentActionType, ActionStatus, ActionErrorResult, CurrentUpgrade, diff --git a/x-pack/test/fleet_api_integration/apis/agents/action_status.ts b/x-pack/test/fleet_api_integration/apis/agents/action_status.ts index dac5871e702db79..ca2fd2bb941b7de 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/action_status.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/action_status.ts @@ -5,7 +5,12 @@ * 2.0. */ import expect from '@kbn/expect'; -import { AGENT_ACTIONS_INDEX, AGENT_ACTIONS_RESULTS_INDEX } from '@kbn/fleet-plugin/common'; +import { + AGENT_ACTIONS_INDEX, + AGENT_ACTIONS_RESULTS_INDEX, + AGENTS_INDEX, + AGENT_POLICY_INDEX, +} from '@kbn/fleet-plugin/common'; import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; import { setupFleetAndAgents } from './services'; import { skipIfNoDockerRegistry } from '../../helpers'; @@ -35,6 +40,10 @@ export default function (providerContext: FtrProviderContext) { index: AGENT_ACTIONS_INDEX, q: '*', }); + await es.deleteByQuery({ + index: AGENT_POLICY_INDEX, + q: '*', + }); try { await es.deleteByQuery( { @@ -82,7 +91,7 @@ export default function (providerContext: FtrProviderContext) { type: 'UPGRADE', action_id: 'action3', agents: ['agent1', 'agent2'], - '@timestamp': '2022-09-15T10:00:00.000Z', + '@timestamp': '2022-09-15T10:05:00.000Z', expiration: '2099-09-16T10:00:00.000Z', }, }); @@ -119,7 +128,7 @@ export default function (providerContext: FtrProviderContext) { type: 'UNENROLL', action_id: 'action4', agents: ['agent1', 'agent2', 'agent3'], - '@timestamp': '2022-09-15T10:00:00.000Z', + '@timestamp': '2022-09-15T10:10:00.000Z', expiration: '2022-09-14T10:00:00.000Z', }, }); @@ -132,8 +141,8 @@ export default function (providerContext: FtrProviderContext) { type: 'UPGRADE', action_id: 'action5', agents: ['agent1', 'agent2', 'agent3'], - '@timestamp': '2022-09-15T10:00:00.000Z', - start_time: '2022-09-15T10:00:00.000Z', + '@timestamp': '2022-09-15T10:20:00.000Z', + start_time: '2022-09-15T10:20:00.000Z', expiration: '2099-09-16T10:00:00.000Z', }, }); @@ -160,7 +169,7 @@ export default function (providerContext: FtrProviderContext) { type: 'POLICY_REASSIGN', action_id: 'action7', agents: ['agent1'], - '@timestamp': '2022-09-15T10:00:00.000Z', + '@timestamp': '2022-09-15T10:30:00.000Z', expiration: '2099-09-16T10:00:00.000Z', data: { policy_id: 'policy1', @@ -180,36 +189,137 @@ export default function (providerContext: FtrProviderContext) { }, ES_INDEX_OPTIONS ); + await es.index({ + refresh: 'wait_for', + index: AGENT_POLICY_INDEX, + document: { + revision_idx: 2, + policy_id: 'policy3', + '@timestamp': '2023-03-15T13:00:00.000Z', + }, + }); + await es.index({ + refresh: 'wait_for', + index: AGENT_POLICY_INDEX, + document: { + revision_idx: 2, + policy_id: 'policy2', + '@timestamp': '2023-03-15T14:00:00.000Z', + }, + }); + await es.index({ + refresh: 'wait_for', + index: AGENT_POLICY_INDEX, + document: { + revision_idx: 3, + policy_id: 'policy2', + '@timestamp': '2023-03-15T15:00:00.000Z', + }, + }); + await es.index({ + refresh: 'wait_for', + index: AGENTS_INDEX, + document: { + policy_revision_idx: 2, + policy_id: 'policy2', + }, + }); + await es.index({ + refresh: 'wait_for', + index: AGENTS_INDEX, + document: { + policy_revision_idx: 3, + policy_id: 'policy2', + }, + }); + // unenrolled agent should be ignored + await es.index({ + refresh: 'wait_for', + index: AGENTS_INDEX, + document: { + policy_revision_idx: 1, + policy_id: 'policy2', + unenrolled_at: '2023-03-15T10:00:00.000Z', + }, + }); }); it('should respond 200 and the action statuses', async () => { const res = await supertest.get(`/api/fleet/agents/action_status`).expect(200); expect(res.body.items).to.eql([ { - actionId: 'action2', - nbAgentsActionCreated: 5, - nbAgentsAck: 0, - version: '8.5.0', - startTime: '2022-09-15T10:00:00.000Z', - type: 'UPGRADE', - nbAgentsActioned: 5, + actionId: 'policy2:3', + creationTime: '2023-03-15T15:00:00.000Z', + completionTime: '2023-03-15T15:00:00.000Z', + type: 'POLICY_CHANGE', + nbAgentsActioned: 2, + nbAgentsAck: 1, + nbAgentsActionCreated: 2, + nbAgentsFailed: 0, status: 'IN_PROGRESS', - creationTime: '2022-09-15T10:00:00.000Z', + policyId: 'policy2', + revision: 3, + }, + { + actionId: 'policy2:2', + creationTime: '2023-03-15T14:00:00.000Z', + completionTime: '2023-03-15T14:00:00.000Z', + type: 'POLICY_CHANGE', + nbAgentsActioned: 2, + nbAgentsAck: 2, + nbAgentsActionCreated: 2, nbAgentsFailed: 0, + status: 'COMPLETE', + policyId: 'policy2', + revision: 2, + }, + { + actionId: 'policy3:2', + creationTime: '2023-03-15T13:00:00.000Z', + completionTime: '2023-03-15T13:00:00.000Z', + type: 'POLICY_CHANGE', + nbAgentsActioned: 0, + nbAgentsAck: 0, + nbAgentsActionCreated: 0, + nbAgentsFailed: 0, + status: 'COMPLETE', + policyId: 'policy3', + revision: 2, + }, + { + actionId: 'action7', + nbAgentsActionCreated: 1, + nbAgentsAck: 0, + type: 'POLICY_REASSIGN', + nbAgentsActioned: 1, + status: 'FAILED', + expiration: '2099-09-16T10:00:00.000Z', + newPolicyId: 'policy1', + creationTime: '2022-09-15T10:30:00.000Z', + nbAgentsFailed: 1, hasRolloutPeriod: false, - latestErrors: [], + completionTime: '2022-09-15T11:00:00.000Z', + latestErrors: [ + { + agentId: 'agent1', + error: 'agent already assigned', + timestamp: '2022-09-15T11:00:00.000Z', + hostname: 'agent1', + }, + ], }, { - actionId: 'action3', - nbAgentsActionCreated: 2, - nbAgentsAck: 2, + actionId: 'action5', + nbAgentsActionCreated: 3, + nbAgentsAck: 0, + startTime: '2022-09-15T10:20:00.000Z', type: 'UPGRADE', - nbAgentsActioned: 2, - status: 'COMPLETE', + nbAgentsActioned: 3, + status: 'CANCELLED', expiration: '2099-09-16T10:00:00.000Z', - creationTime: '2022-09-15T10:00:00.000Z', + creationTime: '2022-09-15T10:20:00.000Z', nbAgentsFailed: 0, - completionTime: '2022-09-15T12:00:00.000Z', hasRolloutPeriod: false, + cancellationTime: '2022-09-15T11:00:00.000Z', latestErrors: [], }, { @@ -220,47 +330,38 @@ export default function (providerContext: FtrProviderContext) { nbAgentsActioned: 3, status: 'EXPIRED', expiration: '2022-09-14T10:00:00.000Z', - creationTime: '2022-09-15T10:00:00.000Z', + creationTime: '2022-09-15T10:10:00.000Z', nbAgentsFailed: 0, hasRolloutPeriod: false, latestErrors: [], }, { - actionId: 'action5', - nbAgentsActionCreated: 3, - nbAgentsAck: 0, - startTime: '2022-09-15T10:00:00.000Z', + actionId: 'action3', + nbAgentsActionCreated: 2, + nbAgentsAck: 2, type: 'UPGRADE', - nbAgentsActioned: 3, - status: 'CANCELLED', + nbAgentsActioned: 2, + status: 'COMPLETE', expiration: '2099-09-16T10:00:00.000Z', - creationTime: '2022-09-15T10:00:00.000Z', + creationTime: '2022-09-15T10:05:00.000Z', nbAgentsFailed: 0, - cancellationTime: '2022-09-15T11:00:00.000Z', hasRolloutPeriod: false, + completionTime: '2022-09-15T12:00:00.000Z', latestErrors: [], }, { - actionId: 'action7', - nbAgentsActionCreated: 1, + actionId: 'action2', + nbAgentsActionCreated: 5, nbAgentsAck: 0, - type: 'POLICY_REASSIGN', - nbAgentsActioned: 1, - status: 'FAILED', - expiration: '2099-09-16T10:00:00.000Z', - newPolicyId: 'policy1', + version: '8.5.0', + startTime: '2022-09-15T10:00:00.000Z', + type: 'UPGRADE', + nbAgentsActioned: 5, + status: 'IN_PROGRESS', creationTime: '2022-09-15T10:00:00.000Z', - nbAgentsFailed: 1, - completionTime: '2022-09-15T11:00:00.000Z', + nbAgentsFailed: 0, hasRolloutPeriod: false, - latestErrors: [ - { - agentId: 'agent1', - error: 'agent already assigned', - timestamp: '2022-09-15T11:00:00.000Z', - hostname: 'agent1', - }, - ], + latestErrors: [], }, ]); });