Skip to content

Commit

Permalink
[Fleet] Change agent status order offline before updating (#140621)
Browse files Browse the repository at this point in the history
  • Loading branch information
nchaulet authored Sep 15, 2022
1 parent c08465d commit 21c36c5
Show file tree
Hide file tree
Showing 6 changed files with 402 additions and 366 deletions.
82 changes: 62 additions & 20 deletions x-pack/plugins/fleet/common/services/agent_status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ export function getAgentStatus(agent: Agent | FleetServerAgent): AgentStatus {
if (!agent.active) {
return 'inactive';
}
if (agent.unenrollment_started_at && !agent.unenrolled_at) {
return 'unenrolling';
}

if (!agent.last_checkin) {
return 'enrolling';
}
Expand All @@ -27,6 +25,14 @@ export function getAgentStatus(agent: Agent | FleetServerAgent): AgentStatus {
const msSinceLastCheckIn = new Date().getTime() - msLastCheckIn;
const intervalsSinceLastCheckIn = Math.floor(msSinceLastCheckIn / AGENT_POLLING_THRESHOLD_MS);

if (intervalsSinceLastCheckIn >= offlineTimeoutIntervalCount) {
return 'offline';
}

if (agent.unenrollment_started_at && !agent.unenrolled_at) {
return 'unenrolling';
}

if (agent.last_checkin_status === 'error') {
return 'error';
}
Expand All @@ -44,49 +50,85 @@ export function getAgentStatus(agent: Agent | FleetServerAgent): AgentStatus {
if (!policyRevision || (agent.upgrade_started_at && agent.upgrade_status !== 'completed')) {
return 'updating';
}
if (intervalsSinceLastCheckIn >= offlineTimeoutIntervalCount) {
return 'offline';
}

return 'online';
}

export function buildKueryForEnrollingAgents(path: string = '') {
export function getPreviousAgentStatusForOfflineAgents(
agent: Agent | FleetServerAgent
): AgentStatus | undefined {
if (agent.unenrollment_started_at && !agent.unenrolled_at) {
return 'unenrolling';
}

if (agent.last_checkin_status === 'error') {
return 'error';
}
if (agent.last_checkin_status === 'degraded') {
return 'degraded';
}

const policyRevision =
'policy_revision' in agent
? agent.policy_revision
: 'policy_revision_idx' in agent
? agent.policy_revision_idx
: undefined;

if (!policyRevision || (agent.upgrade_started_at && agent.upgrade_status !== 'completed')) {
return 'updating';
}
}

export function buildKueryForEnrollingAgents(path: string = ''): string {
return `not (${path}last_checkin:*)`;
}

export function buildKueryForUnenrollingAgents(path: string = '') {
export function buildKueryForUnenrollingAgents(path: string = ''): string {
return `${path}unenrollment_started_at:*`;
}

export function buildKueryForOnlineAgents(path: string = '') {
return `not (${buildKueryForOfflineAgents(path)}) AND not (${buildKueryForErrorAgents(
export function buildKueryForOnlineAgents(path: string = ''): string {
return `${path}last_checkin:* ${addExclusiveKueryFilter(
[buildKueryForOfflineAgents, buildKueryForUpdatingAgents, buildKueryForErrorAgents],
path
)}) AND not (${buildKueryForUpdatingAgents(path)})`;
)}`;
}

export function buildKueryForErrorAgents(path: string = '') {
return `(${path}last_checkin_status:error or ${path}last_checkin_status:degraded) AND not (${buildKueryForUpdatingAgents(
export function buildKueryForErrorAgents(path: string = ''): string {
return `(${path}last_checkin_status:error or ${path}last_checkin_status:degraded) ${addExclusiveKueryFilter(
[buildKueryForOfflineAgents, buildKueryForUnenrollingAgents],
path
)})`;
)}`;
}

export function buildKueryForOfflineAgents(path: string = '') {
export function buildKueryForOfflineAgents(path: string = ''): string {
return `${path}last_checkin < now-${
(offlineTimeoutIntervalCount * AGENT_POLLING_THRESHOLD_MS) / 1000
}s AND not (${buildKueryForErrorAgents(path)}) AND not ( ${buildKueryForUpdatingAgents(path)} )`;
}s`;
}

export function buildKueryForUpgradingAgents(path: string = '') {
export function buildKueryForUpgradingAgents(path: string = ''): string {
return `(${path}upgrade_started_at:*) and not (${path}upgrade_status:completed)`;
}

export function buildKueryForUpdatingAgents(path: string = '') {
return `(${buildKueryForUpgradingAgents(path)}) or (${buildKueryForEnrollingAgents(
export function buildKueryForUpdatingAgents(path: string = ''): string {
return `((${buildKueryForUpgradingAgents(path)}) or (${buildKueryForEnrollingAgents(
path
)}) or (${buildKueryForUnenrollingAgents(
path
)}) or (not ${path}policy_revision_idx:*)) ${addExclusiveKueryFilter(
[buildKueryForOfflineAgents, buildKueryForErrorAgents],
path
)}) or (${buildKueryForUnenrollingAgents(path)}) or (not ${path}policy_revision_idx:*)`;
)}`;
}

export function buildKueryForInactiveAgents(path: string = '') {
return `${path}active:false`;
}

function addExclusiveKueryFilter(kueryBuilders: Array<(path?: string) => string>, path?: string) {
return ` AND not (${kueryBuilders
.map((kueryBuilder) => `(${kueryBuilder(path)})`)
.join(' or ')})`;
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const AgentDetailsOverviewSection: React.FunctionComponent<{
title: i18n.translate('xpack.fleet.agentDetails.statusLabel', {
defaultMessage: 'Status',
}),
description: <AgentHealth agent={agent} />,
description: <AgentHealth agent={agent} showOfflinePreviousStatus={true} />,
},
{
title: i18n.translate('xpack.fleet.agentDetails.lastActivityLabel', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@
* 2.0.
*/

import React from 'react';
import React, { useMemo } from 'react';
import { FormattedMessage, FormattedRelative } from '@kbn/i18n-react';
import { EuiBadge, EuiToolTip } from '@elastic/eui';

import { euiLightVars as euiVars } from '@kbn/ui-theme';

import { getPreviousAgentStatusForOfflineAgents } from '../../../../../../common/services/agent_status';

import type { Agent } from '../../../types';

interface Props {
agent: Agent;
showOfflinePreviousStatus?: boolean;
}

const Status = {
Expand Down Expand Up @@ -48,8 +51,8 @@ const Status = {
),
};

function getStatusComponent(agent: Agent): React.ReactElement {
switch (agent.status) {
function getStatusComponent(status: Agent['status']): React.ReactElement {
switch (status) {
case 'warning':
case 'error':
case 'degraded':
Expand All @@ -67,10 +70,21 @@ function getStatusComponent(agent: Agent): React.ReactElement {
}
}

export const AgentHealth: React.FunctionComponent<Props> = ({ agent }) => {
export const AgentHealth: React.FunctionComponent<Props> = ({
agent,
showOfflinePreviousStatus,
}) => {
const { last_checkin: lastCheckIn } = agent;
const msLastCheckIn = new Date(lastCheckIn || 0).getTime();

const previousToOfflineStatus = useMemo(() => {
if (!showOfflinePreviousStatus || agent.status !== 'offline') {
return;
}

return getPreviousAgentStatusForOfflineAgents(agent);
}, [showOfflinePreviousStatus, agent]);

return (
<EuiToolTip
position="top"
Expand All @@ -93,7 +107,10 @@ export const AgentHealth: React.FunctionComponent<Props> = ({ agent }) => {
)
}
>
{getStatusComponent(agent)}
<>
{getStatusComponent(agent.status)}
{previousToOfflineStatus ? getStatusComponent(previousToOfflineStatus) : null}
</>
</EuiToolTip>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -266,13 +266,51 @@ describe('test endpoint routes', () => {
},
{
bool: {
should: [
filter: [
{
bool: {
filter: [
should: [
{
bool: {
should: [{ exists: { field: 'united.agent.upgrade_started_at' } }],
filter: [
{
bool: {
should: [
{ exists: { field: 'united.agent.upgrade_started_at' } },
],
minimum_should_match: 1,
},
},
{
bool: {
must_not: {
bool: {
should: [
{ match: { 'united.agent.upgrade_status': 'completed' } },
],
minimum_should_match: 1,
},
},
},
},
],
},
},
{
bool: {
must_not: {
bool: {
should: [{ exists: { field: 'united.agent.last_checkin' } }],
minimum_should_match: 1,
},
},
},
},
{
bool: {
should: [
{ exists: { field: 'united.agent.unenrollment_started_at' } },
],
minimum_should_match: 1,
},
},
Expand All @@ -281,48 +319,112 @@ describe('test endpoint routes', () => {
must_not: {
bool: {
should: [
{
match: {
'united.agent.upgrade_status': 'completed',
},
},
{ exists: { field: 'united.agent.policy_revision_idx' } },
],
minimum_should_match: 1,
},
},
},
},
],
},
},
{
bool: {
must_not: {
bool: {
should: [{ exists: { field: 'united.agent.last_checkin' } }],
minimum_should_match: 1,
},
},
},
},
{
bool: {
should: [{ exists: { field: 'united.agent.unenrollment_started_at' } }],
minimum_should_match: 1,
},
},
{
bool: {
must_not: {
bool: {
should: [{ exists: { field: 'united.agent.policy_revision_idx' } }],
should: [
{
bool: {
should: [
{ range: { 'united.agent.last_checkin': { lt: 'now-300s' } } },
],
minimum_should_match: 1,
},
},
{
bool: {
filter: [
{
bool: {
should: [
{
bool: {
should: [
{
match: {
'united.agent.last_checkin_status': 'error',
},
},
],
minimum_should_match: 1,
},
},
{
bool: {
should: [
{
match: {
'united.agent.last_checkin_status': 'degraded',
},
},
],
minimum_should_match: 1,
},
},
],
minimum_should_match: 1,
},
},
{
bool: {
must_not: {
bool: {
should: [
{
bool: {
should: [
{
range: {
'united.agent.last_checkin': {
lt: 'now-300s',
},
},
},
],
minimum_should_match: 1,
},
},
{
bool: {
should: [
{
exists: {
field:
'united.agent.unenrollment_started_at',
},
},
],
minimum_should_match: 1,
},
},
],
minimum_should_match: 1,
},
},
},
},
],
},
},
],
minimum_should_match: 1,
},
},
},
},
],
minimum_should_match: 1,
},
},
{
Expand Down
Loading

0 comments on commit 21c36c5

Please sign in to comment.