[Status UI] Use the new output format of API GET /api/status
(#107937)
This commit is contained in:
parent
6d2c1da2ba
commit
def97bd734
|
@ -8,7 +8,7 @@ pageLoadAssetSize:
|
||||||
charts: 95000
|
charts: 95000
|
||||||
cloud: 21076
|
cloud: 21076
|
||||||
console: 46091
|
console: 46091
|
||||||
core: 434325
|
core: 435325
|
||||||
crossClusterReplication: 65408
|
crossClusterReplication: 65408
|
||||||
dashboard: 374194
|
dashboard: 374194
|
||||||
dashboardEnhanced: 65646
|
dashboardEnhanced: 65646
|
||||||
|
|
|
@ -27,7 +27,7 @@ exports[`StatusTable renders when statuses is provided 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"id": "plugin:1",
|
"id": "plugin:1",
|
||||||
"state": Object {
|
"state": Object {
|
||||||
"id": "green",
|
"id": "available",
|
||||||
"message": "Ready",
|
"message": "Ready",
|
||||||
"title": "green",
|
"title": "green",
|
||||||
"uiColor": "secondary",
|
"uiColor": "secondary",
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { ServerStatus } from './server_status';
|
||||||
import { FormattedStatus } from '../lib';
|
import { FormattedStatus } from '../lib';
|
||||||
|
|
||||||
const getStatus = (parts: Partial<FormattedStatus['state']> = {}): FormattedStatus['state'] => ({
|
const getStatus = (parts: Partial<FormattedStatus['state']> = {}): FormattedStatus['state'] => ({
|
||||||
id: 'green',
|
id: 'available',
|
||||||
title: 'Green',
|
title: 'Green',
|
||||||
uiColor: 'secondary',
|
uiColor: 'secondary',
|
||||||
message: '',
|
message: '',
|
||||||
|
@ -29,7 +29,7 @@ describe('ServerStatus', () => {
|
||||||
|
|
||||||
it('renders correctly for red state', () => {
|
it('renders correctly for red state', () => {
|
||||||
const status = getStatus({
|
const status = getStatus({
|
||||||
id: 'red',
|
id: 'unavailable',
|
||||||
title: 'Red',
|
title: 'Red',
|
||||||
});
|
});
|
||||||
const component = mount(<ServerStatus serverState={status} name="My Computer" />);
|
const component = mount(<ServerStatus serverState={status} name="My Computer" />);
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { shallow } from 'enzyme';
|
||||||
import { StatusTable } from './status_table';
|
import { StatusTable } from './status_table';
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
id: 'green',
|
id: 'available' as const,
|
||||||
uiColor: 'secondary',
|
uiColor: 'secondary',
|
||||||
message: 'Ready',
|
message: 'Ready',
|
||||||
title: 'green',
|
title: 'green',
|
||||||
|
|
|
@ -17,36 +17,37 @@ const mockedResponse: StatusResponse = {
|
||||||
version: {
|
version: {
|
||||||
number: '8.0.0',
|
number: '8.0.0',
|
||||||
build_hash: '9007199254740991',
|
build_hash: '9007199254740991',
|
||||||
build_number: '12',
|
build_number: 12,
|
||||||
build_snapshot: 'XXXXXXXX',
|
build_snapshot: false,
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
overall: {
|
overall: {
|
||||||
id: 'overall',
|
level: 'degraded',
|
||||||
state: 'yellow',
|
summary: 'yellow',
|
||||||
title: 'Yellow',
|
|
||||||
message: 'yellow',
|
|
||||||
uiColor: 'secondary',
|
|
||||||
},
|
},
|
||||||
statuses: [
|
core: {
|
||||||
{
|
elasticsearch: {
|
||||||
id: 'plugin:1',
|
level: 'available',
|
||||||
state: 'green',
|
summary: 'Elasticsearch is available',
|
||||||
title: 'Green',
|
|
||||||
message: 'Ready',
|
|
||||||
uiColor: 'secondary',
|
|
||||||
},
|
},
|
||||||
{
|
savedObjects: {
|
||||||
id: 'plugin:2',
|
level: 'available',
|
||||||
state: 'yellow',
|
summary: 'SavedObjects service has completed migrations and is available',
|
||||||
title: 'Yellow',
|
|
||||||
message: 'Something is weird',
|
|
||||||
uiColor: 'warning',
|
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
|
plugins: {
|
||||||
|
'1': {
|
||||||
|
level: 'available',
|
||||||
|
summary: 'Ready',
|
||||||
|
},
|
||||||
|
'2': {
|
||||||
|
level: 'degraded',
|
||||||
|
summary: 'Something is weird',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
metrics: {
|
metrics: {
|
||||||
collected_at: new Date('2020-01-01 01:00:00'),
|
last_updated: '2020-01-01 01:00:00',
|
||||||
collection_interval_in_millis: 1000,
|
collection_interval_in_millis: 1000,
|
||||||
os: {
|
os: {
|
||||||
platform: 'darwin' as const,
|
platform: 'darwin' as const,
|
||||||
|
@ -80,6 +81,7 @@ const mockedResponse: StatusResponse = {
|
||||||
disconnects: 1,
|
disconnects: 1,
|
||||||
total: 400,
|
total: 400,
|
||||||
statusCodes: {},
|
statusCodes: {},
|
||||||
|
status_codes: {},
|
||||||
},
|
},
|
||||||
concurrent_connections: 1,
|
concurrent_connections: 1,
|
||||||
},
|
},
|
||||||
|
@ -148,13 +150,36 @@ describe('response processing', () => {
|
||||||
test('includes the plugin statuses', async () => {
|
test('includes the plugin statuses', async () => {
|
||||||
const data = await loadStatus({ http, notifications });
|
const data = await loadStatus({ http, notifications });
|
||||||
expect(data.statuses).toEqual([
|
expect(data.statuses).toEqual([
|
||||||
|
{
|
||||||
|
id: 'core:elasticsearch',
|
||||||
|
state: {
|
||||||
|
id: 'available',
|
||||||
|
title: 'Green',
|
||||||
|
message: 'Elasticsearch is available',
|
||||||
|
uiColor: 'secondary',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'core:savedObjects',
|
||||||
|
state: {
|
||||||
|
id: 'available',
|
||||||
|
title: 'Green',
|
||||||
|
message: 'SavedObjects service has completed migrations and is available',
|
||||||
|
uiColor: 'secondary',
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'plugin:1',
|
id: 'plugin:1',
|
||||||
state: { id: 'green', title: 'Green', message: 'Ready', uiColor: 'secondary' },
|
state: { id: 'available', title: 'Green', message: 'Ready', uiColor: 'secondary' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'plugin:2',
|
id: 'plugin:2',
|
||||||
state: { id: 'yellow', title: 'Yellow', message: 'Something is weird', uiColor: 'warning' },
|
state: {
|
||||||
|
id: 'degraded',
|
||||||
|
title: 'Yellow',
|
||||||
|
message: 'Something is weird',
|
||||||
|
uiColor: 'warning',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
@ -162,10 +187,10 @@ describe('response processing', () => {
|
||||||
test('includes the serverState', async () => {
|
test('includes the serverState', async () => {
|
||||||
const data = await loadStatus({ http, notifications });
|
const data = await loadStatus({ http, notifications });
|
||||||
expect(data.serverState).toEqual({
|
expect(data.serverState).toEqual({
|
||||||
id: 'yellow',
|
id: 'degraded',
|
||||||
title: 'Yellow',
|
title: 'Yellow',
|
||||||
message: 'yellow',
|
message: 'yellow',
|
||||||
uiColor: 'secondary',
|
uiColor: 'warning',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import type { UnwrapPromise } from '@kbn/utility-types';
|
import type { UnwrapPromise } from '@kbn/utility-types';
|
||||||
import type { ServerStatus, StatusResponse } from '../../../../types/status';
|
import type { StatusResponse, ServiceStatus, ServiceStatusLevel } from '../../../../types/status';
|
||||||
import type { HttpSetup } from '../../../http';
|
import type { HttpSetup } from '../../../http';
|
||||||
import type { NotificationsSetup } from '../../../notifications';
|
import type { NotificationsSetup } from '../../../notifications';
|
||||||
import type { DataType } from '../lib';
|
import type { DataType } from '../lib';
|
||||||
|
@ -22,13 +22,18 @@ export interface Metric {
|
||||||
export interface FormattedStatus {
|
export interface FormattedStatus {
|
||||||
id: string;
|
id: string;
|
||||||
state: {
|
state: {
|
||||||
id: string;
|
id: ServiceStatusLevel;
|
||||||
title: string;
|
title: string;
|
||||||
message: string;
|
message: string;
|
||||||
uiColor: string;
|
uiColor: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface StatusUIAttributes {
|
||||||
|
title: string;
|
||||||
|
uiColor: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an object of any keys that should be included for metrics.
|
* Returns an object of any keys that should be included for metrics.
|
||||||
*/
|
*/
|
||||||
|
@ -86,18 +91,47 @@ function formatMetrics({ metrics }: StatusResponse): Metric[] {
|
||||||
/**
|
/**
|
||||||
* Reformat the backend data to make the frontend views simpler.
|
* Reformat the backend data to make the frontend views simpler.
|
||||||
*/
|
*/
|
||||||
function formatStatus(status: ServerStatus): FormattedStatus {
|
function formatStatus(id: string, status: ServiceStatus): FormattedStatus {
|
||||||
|
const { title, uiColor } = STATUS_LEVEL_UI_ATTRS[status.level];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: status.id,
|
id,
|
||||||
state: {
|
state: {
|
||||||
id: status.state,
|
id: status.level,
|
||||||
title: status.title,
|
message: status.summary,
|
||||||
message: status.message,
|
title,
|
||||||
uiColor: status.uiColor,
|
uiColor,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const STATUS_LEVEL_UI_ATTRS: Record<ServiceStatusLevel, StatusUIAttributes> = {
|
||||||
|
critical: {
|
||||||
|
title: i18n.translate('core.status.redTitle', {
|
||||||
|
defaultMessage: 'Red',
|
||||||
|
}),
|
||||||
|
uiColor: 'danger',
|
||||||
|
},
|
||||||
|
unavailable: {
|
||||||
|
title: i18n.translate('core.status.redTitle', {
|
||||||
|
defaultMessage: 'Red',
|
||||||
|
}),
|
||||||
|
uiColor: 'danger',
|
||||||
|
},
|
||||||
|
degraded: {
|
||||||
|
title: i18n.translate('core.status.yellowTitle', {
|
||||||
|
defaultMessage: 'Yellow',
|
||||||
|
}),
|
||||||
|
uiColor: 'warning',
|
||||||
|
},
|
||||||
|
available: {
|
||||||
|
title: i18n.translate('core.status.greenTitle', {
|
||||||
|
defaultMessage: 'Green',
|
||||||
|
}),
|
||||||
|
uiColor: 'secondary',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the status from the server API and format it for display.
|
* Get the status from the server API and format it for display.
|
||||||
*/
|
*/
|
||||||
|
@ -111,7 +145,7 @@ export async function loadStatus({
|
||||||
let response: StatusResponse;
|
let response: StatusResponse;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
response = await http.get('/api/status');
|
response = await http.get('/api/status', { query: { v8format: true } });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// API returns a 503 response if not all services are available.
|
// API returns a 503 response if not all services are available.
|
||||||
// In this case, we want to treat this as a successful API call, so that we can
|
// In this case, we want to treat this as a successful API call, so that we can
|
||||||
|
@ -144,8 +178,15 @@ export async function loadStatus({
|
||||||
return {
|
return {
|
||||||
name: response.name,
|
name: response.name,
|
||||||
version: response.version,
|
version: response.version,
|
||||||
statuses: response.status.statuses.map(formatStatus),
|
statuses: [
|
||||||
serverState: formatStatus(response.status.overall).state,
|
...Object.entries(response.status.core).map(([serviceName, status]) =>
|
||||||
|
formatStatus(`core:${serviceName}`, status)
|
||||||
|
),
|
||||||
|
...Object.entries(response.status.plugins).map(([pluginName, status]) =>
|
||||||
|
formatStatus(`plugin:${pluginName}`, status)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
serverState: formatStatus('overall', response.status.overall).state,
|
||||||
metrics: formatMetrics(response),
|
metrics: formatMetrics(response),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { ServiceStatus, CoreStatus, ServiceStatusLevels } from '../types';
|
||||||
import { PluginName } from '../../plugins';
|
import { PluginName } from '../../plugins';
|
||||||
import { calculateLegacyStatus, LegacyStatusInfo } from '../legacy_status';
|
import { calculateLegacyStatus, LegacyStatusInfo } from '../legacy_status';
|
||||||
import { PackageInfo } from '../../config';
|
import { PackageInfo } from '../../config';
|
||||||
|
import { StatusResponse } from '../../../types/status';
|
||||||
|
|
||||||
const SNAPSHOT_POSTFIX = /-SNAPSHOT$/;
|
const SNAPSHOT_POSTFIX = /-SNAPSHOT$/;
|
||||||
|
|
||||||
|
@ -41,55 +42,9 @@ interface StatusInfo {
|
||||||
plugins: Record<string, ServiceStatus>;
|
plugins: Record<string, ServiceStatus>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface StatusHttpBody {
|
// The moment we remove support for the LegacyStatusInfo, we can use the StatusResponse straight away.
|
||||||
name: string;
|
interface StatusHttpBody extends Omit<StatusResponse, 'status'> {
|
||||||
uuid: string;
|
|
||||||
version: {
|
|
||||||
number: string;
|
|
||||||
build_hash: string;
|
|
||||||
build_number: number;
|
|
||||||
build_snapshot: boolean;
|
|
||||||
};
|
|
||||||
status: StatusInfo | LegacyStatusInfo;
|
status: StatusInfo | LegacyStatusInfo;
|
||||||
metrics: {
|
|
||||||
/** ISO-8601 date string w/o timezone */
|
|
||||||
last_updated: string;
|
|
||||||
collection_interval_in_millis: number;
|
|
||||||
process: {
|
|
||||||
memory: {
|
|
||||||
heap: {
|
|
||||||
total_in_bytes: number;
|
|
||||||
used_in_bytes: number;
|
|
||||||
size_limit: number;
|
|
||||||
};
|
|
||||||
resident_set_size_in_bytes: number;
|
|
||||||
};
|
|
||||||
event_loop_delay: number;
|
|
||||||
pid: number;
|
|
||||||
uptime_in_millis: number;
|
|
||||||
};
|
|
||||||
os: {
|
|
||||||
load: Record<string, number>;
|
|
||||||
memory: {
|
|
||||||
total_in_bytes: number;
|
|
||||||
used_in_bytes: number;
|
|
||||||
free_in_bytes: number;
|
|
||||||
};
|
|
||||||
uptime_in_millis: number;
|
|
||||||
platform: string;
|
|
||||||
platformRelease: string;
|
|
||||||
};
|
|
||||||
response_times: {
|
|
||||||
max_in_millis: number;
|
|
||||||
};
|
|
||||||
requests: {
|
|
||||||
total: number;
|
|
||||||
disconnects: number;
|
|
||||||
statusCodes: Record<number, number>;
|
|
||||||
status_codes: Record<number, number>;
|
|
||||||
};
|
|
||||||
concurrent_connections: number;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const registerStatusRoute = ({ router, config, metrics, status }: Deps) => {
|
export const registerStatusRoute = ({ router, config, metrics, status }: Deps) => {
|
||||||
|
|
|
@ -6,36 +6,58 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { OpsMetrics } from '../server/metrics';
|
import type {
|
||||||
|
CoreStatus as CoreStatusFromServer,
|
||||||
|
ServiceStatus as ServiceStatusFromServer,
|
||||||
|
ServiceStatusLevel as ServiceStatusLevelFromServer,
|
||||||
|
OpsMetrics,
|
||||||
|
} from '../server';
|
||||||
|
|
||||||
export interface ServerStatus {
|
/**
|
||||||
id: string;
|
* We need this type to convert the object `ServiceStatusLevel` to a union of the possible strings.
|
||||||
title: string;
|
* This is because of the "stringification" that occurs when serving HTTP requests.
|
||||||
state: string;
|
*/
|
||||||
message: string;
|
export type ServiceStatusLevel = ReturnType<ServiceStatusLevelFromServer['toString']>;
|
||||||
uiColor: string;
|
|
||||||
icon?: string;
|
export interface ServiceStatus extends Omit<ServiceStatusFromServer, 'level'> {
|
||||||
since?: string;
|
level: ServiceStatusLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ServerMetrics = OpsMetrics & {
|
/**
|
||||||
|
* Copy all the services listed in CoreStatus with their specific ServiceStatus declarations
|
||||||
|
* but overwriting the `level` to its stringified version.
|
||||||
|
*/
|
||||||
|
export type CoreStatus = {
|
||||||
|
[ServiceName in keyof CoreStatusFromServer]: Omit<CoreStatusFromServer[ServiceName], 'level'> & {
|
||||||
|
level: ServiceStatusLevel;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ServerMetrics = Omit<OpsMetrics, 'collected_at'> & {
|
||||||
|
last_updated: string;
|
||||||
collection_interval_in_millis: number;
|
collection_interval_in_millis: number;
|
||||||
|
requests: {
|
||||||
|
status_codes: Record<number, number>;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ServerVersion {
|
export interface ServerVersion {
|
||||||
number: string;
|
number: string;
|
||||||
build_hash: string;
|
build_hash: string;
|
||||||
build_number: string;
|
build_number: number;
|
||||||
build_snapshot: string;
|
build_snapshot: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatusInfo {
|
||||||
|
overall: ServiceStatus;
|
||||||
|
core: CoreStatus;
|
||||||
|
plugins: Record<string, ServiceStatus>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StatusResponse {
|
export interface StatusResponse {
|
||||||
name: string;
|
name: string;
|
||||||
uuid: string;
|
uuid: string;
|
||||||
version: ServerVersion;
|
version: ServerVersion;
|
||||||
status: {
|
status: StatusInfo;
|
||||||
overall: ServerStatus;
|
|
||||||
statuses: ServerStatus[];
|
|
||||||
};
|
|
||||||
metrics: ServerMetrics;
|
metrics: ServerMetrics;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue