mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
feat(slo): global and slo specific diagnosis (#153118)
This commit is contained in:
parent
0343c634b2
commit
c226c07c03
4 changed files with 141 additions and 0 deletions
|
@ -118,6 +118,10 @@ const findSLOResponseSchema = t.type({
|
|||
const fetchHistoricalSummaryParamsSchema = t.type({ body: t.type({ sloIds: t.array(t.string) }) });
|
||||
const fetchHistoricalSummaryResponseSchema = t.record(t.string, t.array(historicalSummarySchema));
|
||||
|
||||
const getSLODiagnosisParamsSchema = t.type({
|
||||
path: t.type({ id: t.string }),
|
||||
});
|
||||
|
||||
type SLOResponse = t.OutputOf<typeof sloResponseSchema>;
|
||||
type SLOWithSummaryResponse = t.OutputOf<typeof sloWithSummaryResponseSchema>;
|
||||
|
||||
|
@ -147,6 +151,7 @@ export {
|
|||
deleteSLOParamsSchema,
|
||||
findSLOParamsSchema,
|
||||
findSLOResponseSchema,
|
||||
getSLODiagnosisParamsSchema,
|
||||
getSLOParamsSchema,
|
||||
getSLOResponseSchema,
|
||||
fetchHistoricalSummaryParamsSchema,
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
deleteSLOParamsSchema,
|
||||
fetchHistoricalSummaryParamsSchema,
|
||||
findSLOParamsSchema,
|
||||
getSLODiagnosisParamsSchema,
|
||||
getSLOParamsSchema,
|
||||
manageSLOParamsSchema,
|
||||
updateSLOParamsSchema,
|
||||
|
@ -38,6 +39,7 @@ import { FetchHistoricalSummary } from '../../services/slo/fetch_historical_summ
|
|||
import type { IndicatorTypes } from '../../domain/models';
|
||||
import type { ObservabilityRequestHandlerContext } from '../../types';
|
||||
import { ManageSLO } from '../../services/slo/manage_slo';
|
||||
import { getGlobalDiagnosis, getSloDiagnosis } from '../../services/slo/get_diagnosis';
|
||||
|
||||
const transformGenerators: Record<IndicatorTypes, TransformGenerator> = {
|
||||
'sli.apm.transactionDuration': new ApmTransactionDurationTransformGenerator(),
|
||||
|
@ -238,6 +240,34 @@ const fetchHistoricalSummary = createObservabilityServerRoute({
|
|||
},
|
||||
});
|
||||
|
||||
const getDiagnosisRoute = createObservabilityServerRoute({
|
||||
endpoint: 'GET /internal/observability/slos/_diagnosis',
|
||||
options: {
|
||||
tags: [],
|
||||
},
|
||||
params: undefined,
|
||||
handler: async ({ context }) => {
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
const licensing = await context.licensing;
|
||||
|
||||
return getGlobalDiagnosis(esClient, licensing);
|
||||
},
|
||||
});
|
||||
|
||||
const getSloDiagnosisRoute = createObservabilityServerRoute({
|
||||
endpoint: 'GET /internal/observability/slos/{id}/_diagnosis',
|
||||
options: {
|
||||
tags: [],
|
||||
},
|
||||
params: getSLODiagnosisParamsSchema,
|
||||
handler: async ({ context, params }) => {
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
const soClient = (await context.core).savedObjects.client;
|
||||
|
||||
return getSloDiagnosis(params.path.id, { esClient, soClient });
|
||||
},
|
||||
});
|
||||
|
||||
export const slosRouteRepository = {
|
||||
...createSLORoute,
|
||||
...deleteSLORoute,
|
||||
|
@ -247,4 +277,6 @@ export const slosRouteRepository = {
|
|||
...findSLORoute,
|
||||
...getSLORoute,
|
||||
...updateSLORoute,
|
||||
...getDiagnosisRoute,
|
||||
...getSloDiagnosisRoute,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
|
||||
import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
|
||||
import { LicensingApiRequestHandlerContext } from '@kbn/licensing-plugin/server';
|
||||
|
||||
import {
|
||||
getSLOTransformId,
|
||||
SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME,
|
||||
SLO_COMPONENT_TEMPLATE_SETTINGS_NAME,
|
||||
SLO_INDEX_TEMPLATE_NAME,
|
||||
SLO_INGEST_PIPELINE_NAME,
|
||||
} from '../../assets/constants';
|
||||
import { StoredSLO } from '../../domain/models';
|
||||
import { SO_SLO_TYPE } from '../../saved_objects';
|
||||
|
||||
const OK = 'OK';
|
||||
const NOT_OK = 'NOT_OK';
|
||||
|
||||
export async function getGlobalDiagnosis(
|
||||
esClient: ElasticsearchClient,
|
||||
licensing: LicensingApiRequestHandlerContext
|
||||
) {
|
||||
const licenseInfo = licensing.license.toJSON();
|
||||
const userPrivileges = await esClient.security.getUserPrivileges();
|
||||
const sloResources = await getSloResourcesDiagnosis(esClient);
|
||||
|
||||
return {
|
||||
licenseAndFeatures: licenseInfo,
|
||||
userPrivileges,
|
||||
sloResources,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getSloDiagnosis(
|
||||
sloId: string,
|
||||
services: { esClient: ElasticsearchClient; soClient: SavedObjectsClientContract }
|
||||
) {
|
||||
const { esClient, soClient } = services;
|
||||
|
||||
const sloResources = await getSloResourcesDiagnosis(esClient);
|
||||
|
||||
let sloSavedObject;
|
||||
try {
|
||||
sloSavedObject = await soClient.get<StoredSLO>(SO_SLO_TYPE, sloId);
|
||||
} catch (err) {
|
||||
// noop
|
||||
}
|
||||
|
||||
const sloTransformStats = await esClient.transform.getTransformStats({
|
||||
transform_id: getSLOTransformId(sloId, sloSavedObject?.attributes.revision ?? 1),
|
||||
});
|
||||
|
||||
let dataSample;
|
||||
if (sloSavedObject?.attributes.indicator.params.index) {
|
||||
const slo = sloSavedObject.attributes;
|
||||
dataSample = await esClient.search({
|
||||
index: slo.indicator.params.index,
|
||||
sort: { [slo.settings.timestampField]: 'desc' },
|
||||
size: 5,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
sloResources,
|
||||
sloSavedObject: sloSavedObject ?? NOT_OK,
|
||||
sloTransformStats,
|
||||
dataSample: dataSample ?? NOT_OK,
|
||||
};
|
||||
}
|
||||
|
||||
async function getSloResourcesDiagnosis(esClient: ElasticsearchClient) {
|
||||
const indexTemplateExists = await esClient.indices.existsIndexTemplate({
|
||||
name: SLO_INDEX_TEMPLATE_NAME,
|
||||
});
|
||||
|
||||
const mappingsTemplateExists = await esClient.cluster.existsComponentTemplate({
|
||||
name: SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME,
|
||||
});
|
||||
|
||||
const settingsTemplateExists = await esClient.cluster.existsComponentTemplate({
|
||||
name: SLO_COMPONENT_TEMPLATE_SETTINGS_NAME,
|
||||
});
|
||||
|
||||
let ingestPipelineExists = true;
|
||||
try {
|
||||
await esClient.ingest.getPipeline({ id: SLO_INGEST_PIPELINE_NAME });
|
||||
} catch (err) {
|
||||
ingestPipelineExists = false;
|
||||
}
|
||||
|
||||
return {
|
||||
[SLO_INDEX_TEMPLATE_NAME]: indexTemplateExists ? OK : NOT_OK,
|
||||
[SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME]: mappingsTemplateExists ? OK : NOT_OK,
|
||||
[SLO_COMPONENT_TEMPLATE_SETTINGS_NAME]: settingsTemplateExists ? OK : NOT_OK,
|
||||
[SLO_INGEST_PIPELINE_NAME]: ingestPipelineExists ? OK : NOT_OK,
|
||||
};
|
||||
}
|
|
@ -73,6 +73,7 @@
|
|||
"@kbn/alerts-as-data-utils",
|
||||
"@kbn/core-application-browser",
|
||||
"@kbn/files-plugin",
|
||||
"@kbn/core-elasticsearch-server",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue