mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
feat(slo): Add summary transform for rolling slo (#161698)
This commit is contained in:
parent
39af36782e
commit
b1cfbbd673
23 changed files with 2099 additions and 82 deletions
|
@ -37,8 +37,8 @@ export function SloTimeWindowBadge({ slo }: Props) {
|
|||
const unitMoment = toMomentUnitOfTime(unit);
|
||||
const now = moment.utc();
|
||||
|
||||
const periodStart = now.clone().startOf(unitMoment!);
|
||||
const periodEnd = now.clone().endOf(unitMoment!);
|
||||
const periodStart = now.clone().startOf(unitMoment!).add(1, 'day');
|
||||
const periodEnd = now.clone().endOf(unitMoment!).add(1, 'day');
|
||||
|
||||
const totalDurationInDays = periodEnd.diff(periodStart, 'days') + 1;
|
||||
const elapsedDurationInDays = now.diff(periodStart, 'days') + 1;
|
||||
|
|
|
@ -25,5 +25,9 @@ export const SLO_SUMMARY_COMPONENT_TEMPLATE_SETTINGS_NAME = '.slo-observability.
|
|||
export const SLO_SUMMARY_INDEX_TEMPLATE_NAME = '.slo-observability.summary';
|
||||
export const SLO_SUMMARY_INDEX_TEMPLATE_PATTERN = `${SLO_SUMMARY_INDEX_TEMPLATE_NAME}-*`;
|
||||
|
||||
export const SLO_SUMMARY_TRANSFORM_NAME_PREFIX = 'slo-summary-';
|
||||
export const SLO_SUMMARY_DESTINATION_INDEX_NAME = `${SLO_SUMMARY_INDEX_TEMPLATE_NAME}-v${SLO_RESOURCES_VERSION}`;
|
||||
export const SLO_SUMMARY_DESTINATION_INDEX_PATTERN = `${SLO_SUMMARY_DESTINATION_INDEX_NAME}*`;
|
||||
|
||||
export const getSLOTransformId = (sloId: string, sloRevision: number) =>
|
||||
`slo-${sloId}-${sloRevision}`;
|
||||
|
|
|
@ -17,8 +17,8 @@ describe('toDateRange', () => {
|
|||
it('computes the date range for weekly calendar', () => {
|
||||
const timeWindow = aCalendarTimeWindow(oneWeek());
|
||||
expect(toDateRange(timeWindow, NOW)).toEqual({
|
||||
from: new Date('2022-08-07T00:00:00.000Z'),
|
||||
to: new Date('2022-08-13T23:59:59.999Z'),
|
||||
from: new Date('2022-08-08T00:00:00.000Z'),
|
||||
to: new Date('2022-08-14T23:59:59.999Z'),
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -15,6 +15,14 @@ import type { TimeWindow } from '../models/time_window';
|
|||
export const toDateRange = (timeWindow: TimeWindow, currentDate: Date = new Date()): DateRange => {
|
||||
if (calendarAlignedTimeWindowSchema.is(timeWindow)) {
|
||||
const unit = toMomentUnitOfTime(timeWindow.duration.unit);
|
||||
if (unit === 'weeks') {
|
||||
// moment startOf(week) returns sunday, but we want to stay consistent with es "now/w" date math which returns monday.
|
||||
const from = moment.utc(currentDate).startOf(unit).add(1, 'day');
|
||||
const to = moment.utc(currentDate).endOf(unit).add(1, 'day');
|
||||
|
||||
return { from: from.toDate(), to: to.toDate() };
|
||||
}
|
||||
|
||||
const from = moment.utc(currentDate).startOf(unit);
|
||||
const to = moment.utc(currentDate).endOf(unit);
|
||||
|
||||
|
|
|
@ -5,47 +5,47 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
PluginInitializerContext,
|
||||
Plugin,
|
||||
CoreSetup,
|
||||
DEFAULT_APP_CATEGORIES,
|
||||
Logger,
|
||||
} from '@kbn/core/server';
|
||||
import { hiddenTypes as filesSavedObjectTypes } from '@kbn/files-plugin/server/saved_objects';
|
||||
import { PluginSetupContract, PluginStartContract } from '@kbn/alerting-plugin/server';
|
||||
import { RuleRegistryPluginSetupContract } from '@kbn/rule-registry-plugin/server';
|
||||
import { PluginSetupContract as FeaturesSetup } from '@kbn/features-plugin/server';
|
||||
import {
|
||||
createUICapabilities as createCasesUICapabilities,
|
||||
getApiTags as getCasesApiTags,
|
||||
} from '@kbn/cases-plugin/common';
|
||||
import { SharePluginSetup } from '@kbn/share-plugin/server';
|
||||
import { SpacesPluginSetup } from '@kbn/spaces-plugin/server';
|
||||
import type { GuidedOnboardingPluginSetup } from '@kbn/guided-onboarding-plugin/server';
|
||||
import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server';
|
||||
import { CloudSetup } from '@kbn/cloud-plugin/server';
|
||||
import {
|
||||
kubernetesGuideId,
|
||||
kubernetesGuideConfig,
|
||||
} from '../common/guided_onboarding/kubernetes_guide_config';
|
||||
CoreSetup,
|
||||
DEFAULT_APP_CATEGORIES,
|
||||
Logger,
|
||||
Plugin,
|
||||
PluginInitializerContext,
|
||||
} from '@kbn/core/server';
|
||||
import { PluginSetupContract as FeaturesSetup } from '@kbn/features-plugin/server';
|
||||
import { hiddenTypes as filesSavedObjectTypes } from '@kbn/files-plugin/server/saved_objects';
|
||||
import type { GuidedOnboardingPluginSetup } from '@kbn/guided-onboarding-plugin/server';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { RuleRegistryPluginSetupContract } from '@kbn/rule-registry-plugin/server';
|
||||
import { SharePluginSetup } from '@kbn/share-plugin/server';
|
||||
import { SpacesPluginSetup } from '@kbn/spaces-plugin/server';
|
||||
import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server';
|
||||
import { ObservabilityConfig } from '.';
|
||||
import { casesFeatureId, observabilityFeatureId, sloFeatureId } from '../common';
|
||||
import { SLO_BURN_RATE_RULE_TYPE_ID } from '../common/constants';
|
||||
import {
|
||||
kubernetesGuideConfig,
|
||||
kubernetesGuideId,
|
||||
} from '../common/guided_onboarding/kubernetes_guide_config';
|
||||
import { AlertsLocatorDefinition } from '../common/locators/alerts';
|
||||
import {
|
||||
AnnotationsAPI,
|
||||
bootstrapAnnotations,
|
||||
ScopedAnnotationsClientFactory,
|
||||
AnnotationsAPI,
|
||||
} from './lib/annotations/bootstrap_annotations';
|
||||
import { uiSettings } from './ui_settings';
|
||||
import { registerRoutes } from './routes/register_routes';
|
||||
import { getObservabilityServerRouteRepository } from './routes/get_global_observability_server_route_repository';
|
||||
import { compositeSlo, slo, SO_COMPOSITE_SLO_TYPE, SO_SLO_TYPE } from './saved_objects';
|
||||
import { AlertsLocatorDefinition } from '../common/locators/alerts';
|
||||
import { casesFeatureId, observabilityFeatureId, sloFeatureId } from '../common';
|
||||
import { registerRuleTypes } from './lib/rules/register_rule_types';
|
||||
import { SLO_BURN_RATE_RULE_TYPE_ID } from '../common/constants';
|
||||
import { registerSloUsageCollector } from './lib/collectors/register';
|
||||
import { registerRuleTypes } from './lib/rules/register_rule_types';
|
||||
import { getObservabilityServerRouteRepository } from './routes/get_global_observability_server_route_repository';
|
||||
import { registerRoutes } from './routes/register_routes';
|
||||
import { compositeSlo, slo, SO_COMPOSITE_SLO_TYPE, SO_SLO_TYPE } from './saved_objects';
|
||||
import { threshold } from './saved_objects/threshold';
|
||||
import { uiSettings } from './ui_settings';
|
||||
|
||||
export type ObservabilityPluginSetup = ReturnType<ObservabilityPlugin['setup']>;
|
||||
|
||||
|
|
|
@ -5,19 +5,20 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { forbidden, failedDependency } from '@hapi/boom';
|
||||
import { failedDependency, forbidden } from '@hapi/boom';
|
||||
import {
|
||||
createSLOParamsSchema,
|
||||
deleteSLOParamsSchema,
|
||||
fetchHistoricalSummaryParamsSchema,
|
||||
findSLOParamsSchema,
|
||||
getSLOBurnRatesParamsSchema,
|
||||
getPreviewDataParamsSchema,
|
||||
getSLOBurnRatesParamsSchema,
|
||||
getSLODiagnosisParamsSchema,
|
||||
getSLOParamsSchema,
|
||||
manageSLOParamsSchema,
|
||||
updateSLOParamsSchema,
|
||||
} from '@kbn/slo-schema';
|
||||
import type { IndicatorTypes } from '../../domain/models';
|
||||
import {
|
||||
CreateSLO,
|
||||
DefaultResourceInstaller,
|
||||
|
@ -29,6 +30,13 @@ import {
|
|||
KibanaSavedObjectsSLORepository,
|
||||
UpdateSLO,
|
||||
} from '../../services/slo';
|
||||
import { FetchHistoricalSummary } from '../../services/slo/fetch_historical_summary';
|
||||
import { getBurnRates } from '../../services/slo/get_burn_rates';
|
||||
import { getGlobalDiagnosis, getSloDiagnosis } from '../../services/slo/get_diagnosis';
|
||||
import { GetPreviewData } from '../../services/slo/get_preview_data';
|
||||
import { DefaultHistoricalSummaryClient } from '../../services/slo/historical_summary_client';
|
||||
import { ManageSLO } from '../../services/slo/manage_slo';
|
||||
import { DefaultSummaryTransformInstaller } from '../../services/slo/summary_transform/summary_transform_installer';
|
||||
import {
|
||||
ApmTransactionDurationTransformGenerator,
|
||||
ApmTransactionErrorRateTransformGenerator,
|
||||
|
@ -37,15 +45,8 @@ import {
|
|||
MetricCustomTransformGenerator,
|
||||
TransformGenerator,
|
||||
} from '../../services/slo/transform_generators';
|
||||
import { createObservabilityServerRoute } from '../create_observability_server_route';
|
||||
import { DefaultHistoricalSummaryClient } from '../../services/slo/historical_summary_client';
|
||||
import { FetchHistoricalSummary } from '../../services/slo/fetch_historical_summary';
|
||||
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';
|
||||
import { getBurnRates } from '../../services/slo/get_burn_rates';
|
||||
import { GetPreviewData } from '../../services/slo/get_preview_data';
|
||||
import { createObservabilityServerRoute } from '../create_observability_server_route';
|
||||
|
||||
const transformGenerators: Record<IndicatorTypes, TransformGenerator> = {
|
||||
'sli.apm.transactionDuration': new ApmTransactionDurationTransformGenerator(),
|
||||
|
@ -74,12 +75,22 @@ const createSLORoute = createObservabilityServerRoute({
|
|||
}
|
||||
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
const esInternalClient = (await context.core).elasticsearch.client.asInternalUser;
|
||||
const soClient = (await context.core).savedObjects.client;
|
||||
|
||||
const resourceInstaller = new DefaultResourceInstaller(esClient, logger);
|
||||
const sloResourceInstaller = new DefaultResourceInstaller(esInternalClient, logger);
|
||||
const sloSummaryInstaller = new DefaultSummaryTransformInstaller(esInternalClient, logger);
|
||||
try {
|
||||
await sloResourceInstaller.ensureCommonResourcesInstalled();
|
||||
await sloSummaryInstaller.installAndStart();
|
||||
} catch (error) {
|
||||
logger.error('Failed to install SLO common resources and summary transforms', { error });
|
||||
throw error;
|
||||
}
|
||||
|
||||
const repository = new KibanaSavedObjectsSLORepository(soClient);
|
||||
const transformManager = new DefaultTransformManager(transformGenerators, esClient, logger);
|
||||
const createSLO = new CreateSLO(resourceInstaller, repository, transformManager);
|
||||
const createSLO = new CreateSLO(repository, transformManager);
|
||||
|
||||
const response = await createSLO.execute(params.body);
|
||||
|
||||
|
|
|
@ -8,26 +8,19 @@
|
|||
import { CreateSLO } from './create_slo';
|
||||
import { fiveMinute, oneMinute } from './fixtures/duration';
|
||||
import { createAPMTransactionErrorRateIndicator, createSLOParams } from './fixtures/slo';
|
||||
import {
|
||||
createResourceInstallerMock,
|
||||
createSLORepositoryMock,
|
||||
createTransformManagerMock,
|
||||
} from './mocks';
|
||||
import { ResourceInstaller } from './resource_installer';
|
||||
import { createSLORepositoryMock, createTransformManagerMock } from './mocks';
|
||||
import { SLORepository } from './slo_repository';
|
||||
import { TransformManager } from './transform_manager';
|
||||
|
||||
describe('CreateSLO', () => {
|
||||
let mockResourceInstaller: jest.Mocked<ResourceInstaller>;
|
||||
let mockRepository: jest.Mocked<SLORepository>;
|
||||
let mockTransformManager: jest.Mocked<TransformManager>;
|
||||
let createSLO: CreateSLO;
|
||||
|
||||
beforeEach(() => {
|
||||
mockResourceInstaller = createResourceInstallerMock();
|
||||
mockRepository = createSLORepositoryMock();
|
||||
mockTransformManager = createTransformManagerMock();
|
||||
createSLO = new CreateSLO(mockResourceInstaller, mockRepository, mockTransformManager);
|
||||
createSLO = new CreateSLO(mockRepository, mockTransformManager);
|
||||
});
|
||||
|
||||
describe('happy path', () => {
|
||||
|
@ -37,7 +30,6 @@ describe('CreateSLO', () => {
|
|||
|
||||
const response = await createSLO.execute(sloParams);
|
||||
|
||||
expect(mockResourceInstaller.ensureCommonResourcesInstalled).toHaveBeenCalled();
|
||||
expect(mockRepository.save).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
...sloParams,
|
||||
|
@ -73,7 +65,6 @@ describe('CreateSLO', () => {
|
|||
|
||||
await createSLO.execute(sloParams);
|
||||
|
||||
expect(mockResourceInstaller.ensureCommonResourcesInstalled).toHaveBeenCalled();
|
||||
expect(mockRepository.save).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
...sloParams,
|
||||
|
|
|
@ -5,30 +5,21 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { v1 as uuidv1 } from 'uuid';
|
||||
|
||||
import { CreateSLOParams, CreateSLOResponse } from '@kbn/slo-schema';
|
||||
|
||||
import { v1 as uuidv1 } from 'uuid';
|
||||
import { Duration, DurationUnit, SLO } from '../../domain/models';
|
||||
import { ResourceInstaller } from './resource_installer';
|
||||
import { validateSLO } from '../../domain/services';
|
||||
import { SLORepository } from './slo_repository';
|
||||
import { TransformManager } from './transform_manager';
|
||||
import { validateSLO } from '../../domain/services';
|
||||
|
||||
export class CreateSLO {
|
||||
constructor(
|
||||
private resourceInstaller: ResourceInstaller,
|
||||
private repository: SLORepository,
|
||||
private transformManager: TransformManager
|
||||
) {}
|
||||
constructor(private repository: SLORepository, private transformManager: TransformManager) {}
|
||||
|
||||
public async execute(params: CreateSLOParams): Promise<CreateSLOResponse> {
|
||||
const slo = this.toSLO(params);
|
||||
validateSLO(slo);
|
||||
|
||||
await this.resourceInstaller.ensureCommonResourcesInstalled();
|
||||
await this.repository.save(slo, { throwOnConflict: true });
|
||||
|
||||
let sloTransformId;
|
||||
try {
|
||||
sloTransformId = await this.transformManager.install(slo);
|
||||
|
|
|
@ -24,6 +24,8 @@ import {
|
|||
SLO_SUMMARY_COMPONENT_TEMPLATE_MAPPINGS_NAME,
|
||||
SLO_SUMMARY_COMPONENT_TEMPLATE_SETTINGS_NAME,
|
||||
SLO_SUMMARY_INDEX_TEMPLATE_PATTERN,
|
||||
SLO_DESTINATION_INDEX_NAME,
|
||||
SLO_SUMMARY_DESTINATION_INDEX_NAME,
|
||||
} from '../../assets/constants';
|
||||
import { getSLOMappingsTemplate } from '../../assets/component_templates/slo_mappings_template';
|
||||
import { getSLOSettingsTemplate } from '../../assets/component_templates/slo_settings_template';
|
||||
|
@ -32,6 +34,7 @@ import { getSLOPipelineTemplate } from '../../assets/ingest_templates/slo_pipeli
|
|||
import { getSLOSummaryMappingsTemplate } from '../../assets/component_templates/slo_summary_mappings_template';
|
||||
import { getSLOSummarySettingsTemplate } from '../../assets/component_templates/slo_summary_settings_template';
|
||||
import { getSLOSummaryIndexTemplate } from '../../assets/index_templates/slo_summary_index_templates';
|
||||
import { retryTransientEsErrors } from '../../utils/retry';
|
||||
|
||||
export interface ResourceInstaller {
|
||||
ensureCommonResourcesInstalled(): Promise<void>;
|
||||
|
@ -44,11 +47,12 @@ export class DefaultResourceInstaller implements ResourceInstaller {
|
|||
const alreadyInstalled = await this.areResourcesAlreadyInstalled();
|
||||
|
||||
if (alreadyInstalled) {
|
||||
this.logger.debug('SLO resources already installed - skipping.');
|
||||
this.logger.info('SLO resources already installed - skipping');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.logger.info('Installing SLO shared resources');
|
||||
await Promise.all([
|
||||
this.createOrUpdateComponentTemplate(
|
||||
getSLOMappingsTemplate(SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME)
|
||||
|
@ -82,6 +86,11 @@ export class DefaultResourceInstaller implements ResourceInstaller {
|
|||
)
|
||||
);
|
||||
|
||||
await this.execute(() => this.esClient.indices.create({ index: SLO_DESTINATION_INDEX_NAME }));
|
||||
await this.execute(() =>
|
||||
this.esClient.indices.create({ index: SLO_SUMMARY_DESTINATION_INDEX_NAME })
|
||||
);
|
||||
|
||||
await this.createOrUpdateIngestPipelineTemplate(
|
||||
getSLOPipelineTemplate(SLO_INGEST_PIPELINE_NAME, SLO_INGEST_PIPELINE_INDEX_NAME_PREFIX)
|
||||
);
|
||||
|
@ -94,9 +103,12 @@ export class DefaultResourceInstaller implements ResourceInstaller {
|
|||
private async areResourcesAlreadyInstalled(): Promise<boolean> {
|
||||
let indexTemplateExists = false;
|
||||
try {
|
||||
const { index_templates: indexTemplates } = await this.esClient.indices.getIndexTemplate({
|
||||
name: SLO_INDEX_TEMPLATE_NAME,
|
||||
});
|
||||
const { index_templates: indexTemplates } = await this.execute(() =>
|
||||
this.esClient.indices.getIndexTemplate({
|
||||
name: SLO_INDEX_TEMPLATE_NAME,
|
||||
})
|
||||
);
|
||||
|
||||
const sloIndexTemplate = indexTemplates.find(
|
||||
(template) => template.name === SLO_INDEX_TEMPLATE_NAME
|
||||
);
|
||||
|
@ -109,9 +121,11 @@ export class DefaultResourceInstaller implements ResourceInstaller {
|
|||
|
||||
let summaryIndexTemplateExists = false;
|
||||
try {
|
||||
const { index_templates: indexTemplates } = await this.esClient.indices.getIndexTemplate({
|
||||
name: SLO_SUMMARY_INDEX_TEMPLATE_NAME,
|
||||
});
|
||||
const { index_templates: indexTemplates } = await this.execute(() =>
|
||||
this.esClient.indices.getIndexTemplate({
|
||||
name: SLO_SUMMARY_INDEX_TEMPLATE_NAME,
|
||||
})
|
||||
);
|
||||
const sloSummaryIndexTemplate = indexTemplates.find(
|
||||
(template) => template.name === SLO_SUMMARY_INDEX_TEMPLATE_NAME
|
||||
);
|
||||
|
@ -124,7 +138,9 @@ export class DefaultResourceInstaller implements ResourceInstaller {
|
|||
|
||||
let ingestPipelineExists = false;
|
||||
try {
|
||||
const pipeline = await this.esClient.ingest.getPipeline({ id: SLO_INGEST_PIPELINE_NAME });
|
||||
const pipeline = await this.execute(() =>
|
||||
this.esClient.ingest.getPipeline({ id: SLO_INGEST_PIPELINE_NAME })
|
||||
);
|
||||
|
||||
ingestPipelineExists =
|
||||
// @ts-ignore _meta is not defined on the type
|
||||
|
@ -138,16 +154,20 @@ export class DefaultResourceInstaller implements ResourceInstaller {
|
|||
|
||||
private async createOrUpdateComponentTemplate(template: ClusterPutComponentTemplateRequest) {
|
||||
this.logger.debug(`Installing SLO component template ${template.name}`);
|
||||
return this.esClient.cluster.putComponentTemplate(template);
|
||||
return this.execute(() => this.esClient.cluster.putComponentTemplate(template));
|
||||
}
|
||||
|
||||
private async createOrUpdateIndexTemplate(template: IndicesPutIndexTemplateRequest) {
|
||||
this.logger.debug(`Installing SLO index template ${template.name}`);
|
||||
return this.esClient.indices.putIndexTemplate(template);
|
||||
return this.execute(() => this.esClient.indices.putIndexTemplate(template));
|
||||
}
|
||||
|
||||
private async createOrUpdateIngestPipelineTemplate(template: IngestPutPipelineRequest) {
|
||||
this.logger.debug(`Installing SLO ingest pipeline template ${template.id}`);
|
||||
await this.esClient.ingest.putPipeline(template);
|
||||
return this.execute(() => this.esClient.ingest.putPipeline(template));
|
||||
}
|
||||
|
||||
private async execute<T>(esCall: () => Promise<T>): Promise<T> {
|
||||
return await retryTransientEsErrors(esCall, { logger: this.logger });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* 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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types';
|
||||
import type { ElasticsearchClient, Logger } from '@kbn/core/server';
|
||||
import {
|
||||
SLO_RESOURCES_VERSION,
|
||||
SLO_SUMMARY_TRANSFORM_NAME_PREFIX,
|
||||
} from '../../../assets/constants';
|
||||
import { retryTransientEsErrors } from '../../../utils/retry';
|
||||
import { ALL_TRANSFORM_TEMPLATES } from './templates';
|
||||
|
||||
export interface SummaryTransformInstaller {
|
||||
installAndStart(): Promise<void>;
|
||||
}
|
||||
|
||||
export class DefaultSummaryTransformInstaller implements SummaryTransformInstaller {
|
||||
constructor(private esClient: ElasticsearchClient, private logger: Logger) {}
|
||||
|
||||
public async installAndStart(): Promise<void> {
|
||||
const allTransformIds = ALL_TRANSFORM_TEMPLATES.map((transform) => transform.transform_id);
|
||||
const summaryTransforms = await this.execute(() =>
|
||||
this.esClient.transform.getTransform(
|
||||
{ transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}*`, allow_no_match: true },
|
||||
{ ignore: [404] }
|
||||
)
|
||||
);
|
||||
const alreadyInstalled =
|
||||
summaryTransforms.count === allTransformIds.length &&
|
||||
summaryTransforms.transforms.every(
|
||||
(transform) => transform._meta?.version === SLO_RESOURCES_VERSION
|
||||
) &&
|
||||
summaryTransforms.transforms.every((transform) => allTransformIds.includes(transform.id));
|
||||
|
||||
if (alreadyInstalled) {
|
||||
this.logger.info(`SLO summary transforms already installed - skipping`);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const transformTemplate of ALL_TRANSFORM_TEMPLATES) {
|
||||
const transformId = transformTemplate.transform_id;
|
||||
const transform = summaryTransforms.transforms.find((t) => t.id === transformId);
|
||||
|
||||
const transformAlreadyInstalled =
|
||||
!!transform && transform._meta?.version === SLO_RESOURCES_VERSION;
|
||||
const previousTransformAlreadyInstalled =
|
||||
!!transform && transform._meta?.version !== SLO_RESOURCES_VERSION;
|
||||
|
||||
if (transformAlreadyInstalled) {
|
||||
this.logger.info(`SLO summary transform: ${transformId} already installed - skipping`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (previousTransformAlreadyInstalled) {
|
||||
await this.deletePreviousTransformVersion(transformId);
|
||||
}
|
||||
|
||||
await this.installTransform(transformId, transformTemplate);
|
||||
await this.startTransform(transformId);
|
||||
}
|
||||
|
||||
this.logger.info(`All SLO summary transforms installed and started`);
|
||||
}
|
||||
|
||||
private async installTransform(
|
||||
transformId: string,
|
||||
transformTemplate: TransformPutTransformRequest
|
||||
) {
|
||||
this.logger.info(`Installing SLO summary transform: ${transformId}`);
|
||||
await this.execute(() =>
|
||||
this.esClient.transform.putTransform(transformTemplate, { ignore: [409] })
|
||||
);
|
||||
}
|
||||
|
||||
private async deletePreviousTransformVersion(transformId: string) {
|
||||
this.logger.info(`Deleting previous SLO summary transform: ${transformId}`);
|
||||
await this.execute(() =>
|
||||
this.esClient.transform.stopTransform(
|
||||
{ transform_id: transformId, allow_no_match: true, force: true },
|
||||
{ ignore: [409, 404] }
|
||||
)
|
||||
);
|
||||
await this.execute(() =>
|
||||
this.esClient.transform.deleteTransform(
|
||||
{ transform_id: transformId, force: true },
|
||||
{ ignore: [409, 404] }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private async startTransform(transformId: string) {
|
||||
this.logger.info(`Starting SLO summary transform: ${transformId} - noop if already running`);
|
||||
await this.execute(() =>
|
||||
this.esClient.transform.startTransform({ transform_id: transformId }, { ignore: [409] })
|
||||
);
|
||||
}
|
||||
|
||||
private async execute<T>(esCall: () => Promise<T>): Promise<T> {
|
||||
return await retryTransientEsErrors(esCall, { logger: this.logger });
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 { SUMMARY_OCCURRENCES_7D_ROLLING } from './summary_occurrences_7d_rolling';
|
||||
import { SUMMARY_OCCURRENCES_30D_ROLLING } from './summary_occurrences_30d_rolling';
|
||||
import { SUMMARY_OCCURRENCES_90D_ROLLING } from './summary_occurrences_90d_rolling';
|
||||
import { SUMMARY_TIMESLICES_7D_ROLLING } from './summary_timeslices_7d_rolling';
|
||||
import { SUMMARY_TIMESLICES_30D_ROLLING } from './summary_timeslices_30d_rolling';
|
||||
import { SUMMARY_TIMESLICES_90D_ROLLING } from './summary_timeslices_90d_rolling';
|
||||
import { SUMMARY_OCCURRENCES_WEEKLY_ALIGNED } from './summary_occurrences_weekly_aligned';
|
||||
import { SUMMARY_OCCURRENCES_MONTHLY_ALIGNED } from './summary_occurrences_monthly_aligned';
|
||||
import { SUMMARY_TIMESLICES_WEEKLY_ALIGNED } from './summary_timeslices_weekly_aligned';
|
||||
import { SUMMARY_TIMESLICES_MONTHLY_ALIGNED } from './summary_timeslices_monthly_aligned';
|
||||
|
||||
export const ALL_TRANSFORM_TEMPLATES = [
|
||||
SUMMARY_OCCURRENCES_7D_ROLLING,
|
||||
SUMMARY_OCCURRENCES_30D_ROLLING,
|
||||
SUMMARY_OCCURRENCES_90D_ROLLING,
|
||||
SUMMARY_OCCURRENCES_WEEKLY_ALIGNED,
|
||||
SUMMARY_OCCURRENCES_MONTHLY_ALIGNED,
|
||||
SUMMARY_TIMESLICES_7D_ROLLING,
|
||||
SUMMARY_TIMESLICES_30D_ROLLING,
|
||||
SUMMARY_TIMESLICES_90D_ROLLING,
|
||||
SUMMARY_TIMESLICES_WEEKLY_ALIGNED,
|
||||
SUMMARY_TIMESLICES_MONTHLY_ALIGNED,
|
||||
];
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* 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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types';
|
||||
import {
|
||||
SLO_DESTINATION_INDEX_PATTERN,
|
||||
SLO_RESOURCES_VERSION,
|
||||
SLO_SUMMARY_DESTINATION_INDEX_NAME,
|
||||
SLO_SUMMARY_TRANSFORM_NAME_PREFIX,
|
||||
} from '../../../../assets/constants';
|
||||
|
||||
export const SUMMARY_OCCURRENCES_30D_ROLLING: TransformPutTransformRequest = {
|
||||
transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}occurrences-30d-rolling`,
|
||||
dest: {
|
||||
index: SLO_SUMMARY_DESTINATION_INDEX_NAME,
|
||||
},
|
||||
source: {
|
||||
index: SLO_DESTINATION_INDEX_PATTERN,
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: 'now-30d/m',
|
||||
lte: 'now/m',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.budgetingMethod': 'occurrences',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.timeWindow.type': 'rolling',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.timeWindow.duration': '30d',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
pivot: {
|
||||
group_by: {
|
||||
'slo.id': {
|
||||
terms: {
|
||||
field: 'slo.id',
|
||||
},
|
||||
},
|
||||
'slo.revision': {
|
||||
terms: {
|
||||
field: 'slo.revision',
|
||||
},
|
||||
},
|
||||
'slo.instanceId': {
|
||||
terms: {
|
||||
field: 'slo.instanceId',
|
||||
},
|
||||
},
|
||||
'slo.budgetingMethod': {
|
||||
terms: {
|
||||
field: 'slo.budgetingMethod',
|
||||
},
|
||||
},
|
||||
'slo.timeWindow.duration': {
|
||||
terms: {
|
||||
field: 'slo.timeWindow.duration',
|
||||
},
|
||||
},
|
||||
'slo.timeWindow.type': {
|
||||
terms: {
|
||||
field: 'slo.timeWindow.type',
|
||||
},
|
||||
},
|
||||
},
|
||||
aggregations: {
|
||||
goodEvents: {
|
||||
sum: {
|
||||
field: 'slo.numerator',
|
||||
},
|
||||
},
|
||||
totalEvents: {
|
||||
sum: {
|
||||
field: 'slo.denominator',
|
||||
},
|
||||
},
|
||||
_objectiveTarget: {
|
||||
max: {
|
||||
field: 'slo.objective.target',
|
||||
},
|
||||
},
|
||||
sliValue: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
goodEvents: 'goodEvents',
|
||||
totalEvents: 'totalEvents',
|
||||
},
|
||||
script:
|
||||
'if (params.totalEvents == 0) { return -1 } else { return params.goodEvents / params.totalEvents }',
|
||||
},
|
||||
},
|
||||
errorBudgetInitial: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
objectiveTarget: '_objectiveTarget',
|
||||
},
|
||||
script: '1 - params.objectiveTarget',
|
||||
},
|
||||
},
|
||||
errorBudgetConsumed: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
sliValue: 'sliValue',
|
||||
errorBudgetInitial: 'errorBudgetInitial',
|
||||
},
|
||||
script:
|
||||
'if (params.sliValue == -1) { return 0 } else { return (1 - params.sliValue) / params.errorBudgetInitial }',
|
||||
},
|
||||
},
|
||||
errorBudgetRemaining: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
errorBudgetConsummed: 'errorBudgetConsumed',
|
||||
},
|
||||
script: '1 - params.errorBudgetConsummed',
|
||||
},
|
||||
},
|
||||
status: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
sliValue: 'sliValue',
|
||||
objectiveTarget: '_objectiveTarget',
|
||||
errorBudgetRemaining: 'errorBudgetRemaining',
|
||||
},
|
||||
script: {
|
||||
source:
|
||||
'if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objectiveTarget) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Summarize every SLO with occurrences budgeting method and a 30 days rolling time window',
|
||||
frequency: '1m',
|
||||
sync: {
|
||||
time: {
|
||||
field: '@timestamp',
|
||||
delay: '60s',
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
deduce_mappings: false,
|
||||
},
|
||||
_meta: {
|
||||
version: SLO_RESOURCES_VERSION,
|
||||
managed: true,
|
||||
managed_by: 'observability',
|
||||
},
|
||||
};
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* 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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types';
|
||||
import {
|
||||
SLO_DESTINATION_INDEX_PATTERN,
|
||||
SLO_RESOURCES_VERSION,
|
||||
SLO_SUMMARY_DESTINATION_INDEX_NAME,
|
||||
SLO_SUMMARY_TRANSFORM_NAME_PREFIX,
|
||||
} from '../../../../assets/constants';
|
||||
|
||||
export const SUMMARY_OCCURRENCES_7D_ROLLING: TransformPutTransformRequest = {
|
||||
transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}occurrences-7d-rolling`,
|
||||
dest: {
|
||||
index: SLO_SUMMARY_DESTINATION_INDEX_NAME,
|
||||
},
|
||||
source: {
|
||||
index: SLO_DESTINATION_INDEX_PATTERN,
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: 'now-7d/m',
|
||||
lte: 'now/m',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.budgetingMethod': 'occurrences',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.timeWindow.type': 'rolling',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.timeWindow.duration': '7d',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
pivot: {
|
||||
group_by: {
|
||||
'slo.id': {
|
||||
terms: {
|
||||
field: 'slo.id',
|
||||
},
|
||||
},
|
||||
'slo.revision': {
|
||||
terms: {
|
||||
field: 'slo.revision',
|
||||
},
|
||||
},
|
||||
'slo.instanceId': {
|
||||
terms: {
|
||||
field: 'slo.instanceId',
|
||||
},
|
||||
},
|
||||
'slo.budgetingMethod': {
|
||||
terms: {
|
||||
field: 'slo.budgetingMethod',
|
||||
},
|
||||
},
|
||||
'slo.timeWindow.duration': {
|
||||
terms: {
|
||||
field: 'slo.timeWindow.duration',
|
||||
},
|
||||
},
|
||||
'slo.timeWindow.type': {
|
||||
terms: {
|
||||
field: 'slo.timeWindow.type',
|
||||
},
|
||||
},
|
||||
},
|
||||
aggregations: {
|
||||
goodEvents: {
|
||||
sum: {
|
||||
field: 'slo.numerator',
|
||||
},
|
||||
},
|
||||
totalEvents: {
|
||||
sum: {
|
||||
field: 'slo.denominator',
|
||||
},
|
||||
},
|
||||
_objectiveTarget: {
|
||||
max: {
|
||||
field: 'slo.objective.target',
|
||||
},
|
||||
},
|
||||
sliValue: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
goodEvents: 'goodEvents',
|
||||
totalEvents: 'totalEvents',
|
||||
},
|
||||
script:
|
||||
'if (params.totalEvents == 0) { return -1 } else { return params.goodEvents / params.totalEvents }',
|
||||
},
|
||||
},
|
||||
errorBudgetInitial: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
objectiveTarget: '_objectiveTarget',
|
||||
},
|
||||
script: '1 - params.objectiveTarget',
|
||||
},
|
||||
},
|
||||
errorBudgetConsumed: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
sliValue: 'sliValue',
|
||||
errorBudgetInitial: 'errorBudgetInitial',
|
||||
},
|
||||
script:
|
||||
'if (params.sliValue == -1) { return 0 } else { return (1 - params.sliValue) / params.errorBudgetInitial }',
|
||||
},
|
||||
},
|
||||
errorBudgetRemaining: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
errorBudgetConsummed: 'errorBudgetConsumed',
|
||||
},
|
||||
script: '1 - params.errorBudgetConsummed',
|
||||
},
|
||||
},
|
||||
status: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
sliValue: 'sliValue',
|
||||
objectiveTarget: '_objectiveTarget',
|
||||
errorBudgetRemaining: 'errorBudgetRemaining',
|
||||
},
|
||||
script: {
|
||||
source:
|
||||
'if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objectiveTarget) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Summarize every SLO with occurrences budgeting method and a 7 days rolling time window',
|
||||
frequency: '1m',
|
||||
sync: {
|
||||
time: {
|
||||
field: '@timestamp',
|
||||
delay: '60s',
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
deduce_mappings: false,
|
||||
},
|
||||
_meta: {
|
||||
version: SLO_RESOURCES_VERSION,
|
||||
managed: true,
|
||||
managed_by: 'observability',
|
||||
},
|
||||
};
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* 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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types';
|
||||
import {
|
||||
SLO_DESTINATION_INDEX_PATTERN,
|
||||
SLO_RESOURCES_VERSION,
|
||||
SLO_SUMMARY_DESTINATION_INDEX_NAME,
|
||||
SLO_SUMMARY_TRANSFORM_NAME_PREFIX,
|
||||
} from '../../../../assets/constants';
|
||||
|
||||
export const SUMMARY_OCCURRENCES_90D_ROLLING: TransformPutTransformRequest = {
|
||||
transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}occurrences-90d-rolling`,
|
||||
dest: {
|
||||
index: SLO_SUMMARY_DESTINATION_INDEX_NAME,
|
||||
},
|
||||
source: {
|
||||
index: SLO_DESTINATION_INDEX_PATTERN,
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: 'now-90d/m',
|
||||
lte: 'now/m',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.budgetingMethod': 'occurrences',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.timeWindow.type': 'rolling',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.timeWindow.duration': '90d',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
pivot: {
|
||||
group_by: {
|
||||
'slo.id': {
|
||||
terms: {
|
||||
field: 'slo.id',
|
||||
},
|
||||
},
|
||||
'slo.revision': {
|
||||
terms: {
|
||||
field: 'slo.revision',
|
||||
},
|
||||
},
|
||||
'slo.instanceId': {
|
||||
terms: {
|
||||
field: 'slo.instanceId',
|
||||
},
|
||||
},
|
||||
'slo.budgetingMethod': {
|
||||
terms: {
|
||||
field: 'slo.budgetingMethod',
|
||||
},
|
||||
},
|
||||
'slo.timeWindow.duration': {
|
||||
terms: {
|
||||
field: 'slo.timeWindow.duration',
|
||||
},
|
||||
},
|
||||
'slo.timeWindow.type': {
|
||||
terms: {
|
||||
field: 'slo.timeWindow.type',
|
||||
},
|
||||
},
|
||||
},
|
||||
aggregations: {
|
||||
goodEvents: {
|
||||
sum: {
|
||||
field: 'slo.numerator',
|
||||
},
|
||||
},
|
||||
totalEvents: {
|
||||
sum: {
|
||||
field: 'slo.denominator',
|
||||
},
|
||||
},
|
||||
_objectiveTarget: {
|
||||
max: {
|
||||
field: 'slo.objective.target',
|
||||
},
|
||||
},
|
||||
sliValue: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
goodEvents: 'goodEvents',
|
||||
totalEvents: 'totalEvents',
|
||||
},
|
||||
script:
|
||||
'if (params.totalEvents == 0) { return -1 } else { return params.goodEvents / params.totalEvents }',
|
||||
},
|
||||
},
|
||||
errorBudgetInitial: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
objectiveTarget: '_objectiveTarget',
|
||||
},
|
||||
script: '1 - params.objectiveTarget',
|
||||
},
|
||||
},
|
||||
errorBudgetConsumed: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
sliValue: 'sliValue',
|
||||
errorBudgetInitial: 'errorBudgetInitial',
|
||||
},
|
||||
script:
|
||||
'if (params.sliValue == -1) { return 0 } else { return (1 - params.sliValue) / params.errorBudgetInitial }',
|
||||
},
|
||||
},
|
||||
errorBudgetRemaining: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
errorBudgetConsummed: 'errorBudgetConsumed',
|
||||
},
|
||||
script: '1 - params.errorBudgetConsummed',
|
||||
},
|
||||
},
|
||||
status: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
sliValue: 'sliValue',
|
||||
objectiveTarget: '_objectiveTarget',
|
||||
errorBudgetRemaining: 'errorBudgetRemaining',
|
||||
},
|
||||
script: {
|
||||
source:
|
||||
'if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objectiveTarget) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Summarize every SLO with occurrences budgeting method and a 90 days rolling time window',
|
||||
frequency: '1m',
|
||||
sync: {
|
||||
time: {
|
||||
field: '@timestamp',
|
||||
delay: '60s',
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
deduce_mappings: false,
|
||||
},
|
||||
_meta: {
|
||||
version: SLO_RESOURCES_VERSION,
|
||||
managed: true,
|
||||
managed_by: 'observability',
|
||||
},
|
||||
};
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* 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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types';
|
||||
import {
|
||||
SLO_DESTINATION_INDEX_PATTERN,
|
||||
SLO_RESOURCES_VERSION,
|
||||
SLO_SUMMARY_DESTINATION_INDEX_NAME,
|
||||
SLO_SUMMARY_TRANSFORM_NAME_PREFIX,
|
||||
} from '../../../../assets/constants';
|
||||
|
||||
export const SUMMARY_OCCURRENCES_MONTHLY_ALIGNED: TransformPutTransformRequest = {
|
||||
transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}occurrences-monthly-aligned`,
|
||||
dest: {
|
||||
index: SLO_SUMMARY_DESTINATION_INDEX_NAME,
|
||||
},
|
||||
source: {
|
||||
index: SLO_DESTINATION_INDEX_PATTERN,
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: 'now/M',
|
||||
lte: 'now/m',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.budgetingMethod': 'occurrences',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.timeWindow.type': 'calendarAligned',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.timeWindow.duration': '1M',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
pivot: {
|
||||
group_by: {
|
||||
'slo.id': {
|
||||
terms: {
|
||||
field: 'slo.id',
|
||||
},
|
||||
},
|
||||
'slo.revision': {
|
||||
terms: {
|
||||
field: 'slo.revision',
|
||||
},
|
||||
},
|
||||
'slo.instanceId': {
|
||||
terms: {
|
||||
field: 'slo.instanceId',
|
||||
},
|
||||
},
|
||||
'slo.budgetingMethod': {
|
||||
terms: {
|
||||
field: 'slo.budgetingMethod',
|
||||
},
|
||||
},
|
||||
'slo.timeWindow.duration': {
|
||||
terms: {
|
||||
field: 'slo.timeWindow.duration',
|
||||
},
|
||||
},
|
||||
'slo.timeWindow.type': {
|
||||
terms: {
|
||||
field: 'slo.timeWindow.type',
|
||||
},
|
||||
},
|
||||
},
|
||||
aggregations: {
|
||||
_objectiveTarget: {
|
||||
max: {
|
||||
field: 'slo.objective.target',
|
||||
},
|
||||
},
|
||||
goodEvents: {
|
||||
sum: {
|
||||
field: 'slo.numerator',
|
||||
},
|
||||
},
|
||||
totalEvents: {
|
||||
sum: {
|
||||
field: 'slo.denominator',
|
||||
},
|
||||
},
|
||||
sliValue: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
goodEvents: 'goodEvents',
|
||||
totalEvents: 'totalEvents',
|
||||
},
|
||||
script:
|
||||
'if (params.totalEvents == 0) { return -1 } else { return params.goodEvents / params.totalEvents }',
|
||||
},
|
||||
},
|
||||
errorBudgetInitial: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
objective: '_objectiveTarget',
|
||||
},
|
||||
script: '1 - params.objective',
|
||||
},
|
||||
},
|
||||
errorBudgetConsumed: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
sliValue: 'sliValue',
|
||||
errorBudgetInitial: 'errorBudgetInitial',
|
||||
},
|
||||
script:
|
||||
'if (params.sliValue == -1) { return 0 } else { return (1 - params.sliValue) / params.errorBudgetInitial }',
|
||||
},
|
||||
},
|
||||
errorBudgetRemaining: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
errorBudgetConsumed: 'errorBudgetConsumed',
|
||||
},
|
||||
script: '1 - params.errorBudgetConsumed',
|
||||
},
|
||||
},
|
||||
status: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
sliValue: 'sliValue',
|
||||
objective: '_objectiveTarget',
|
||||
errorBudgetRemaining: 'errorBudgetRemaining',
|
||||
},
|
||||
script:
|
||||
'if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objective) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Summarize every SLO with occurrences budgeting method and a monthly calendar aligned time window',
|
||||
frequency: '1m',
|
||||
sync: {
|
||||
time: {
|
||||
field: '@timestamp',
|
||||
delay: '60s',
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
deduce_mappings: false,
|
||||
},
|
||||
_meta: {
|
||||
version: SLO_RESOURCES_VERSION,
|
||||
managed: true,
|
||||
managed_by: 'observability',
|
||||
},
|
||||
};
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* 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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types';
|
||||
import {
|
||||
SLO_DESTINATION_INDEX_PATTERN,
|
||||
SLO_RESOURCES_VERSION,
|
||||
SLO_SUMMARY_DESTINATION_INDEX_NAME,
|
||||
SLO_SUMMARY_TRANSFORM_NAME_PREFIX,
|
||||
} from '../../../../assets/constants';
|
||||
|
||||
export const SUMMARY_OCCURRENCES_WEEKLY_ALIGNED: TransformPutTransformRequest = {
|
||||
transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}occurrences-weekly-aligned`,
|
||||
dest: {
|
||||
index: SLO_SUMMARY_DESTINATION_INDEX_NAME,
|
||||
},
|
||||
source: {
|
||||
index: SLO_DESTINATION_INDEX_PATTERN,
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: 'now/w',
|
||||
lte: 'now/m',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.budgetingMethod': 'occurrences',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.timeWindow.type': 'calendarAligned',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.timeWindow.duration': '1w',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
pivot: {
|
||||
group_by: {
|
||||
'slo.id': {
|
||||
terms: {
|
||||
field: 'slo.id',
|
||||
},
|
||||
},
|
||||
'slo.revision': {
|
||||
terms: {
|
||||
field: 'slo.revision',
|
||||
},
|
||||
},
|
||||
'slo.instanceId': {
|
||||
terms: {
|
||||
field: 'slo.instanceId',
|
||||
},
|
||||
},
|
||||
'slo.budgetingMethod': {
|
||||
terms: {
|
||||
field: 'slo.budgetingMethod',
|
||||
},
|
||||
},
|
||||
'slo.timeWindow.duration': {
|
||||
terms: {
|
||||
field: 'slo.timeWindow.duration',
|
||||
},
|
||||
},
|
||||
'slo.timeWindow.type': {
|
||||
terms: {
|
||||
field: 'slo.timeWindow.type',
|
||||
},
|
||||
},
|
||||
},
|
||||
aggregations: {
|
||||
_objectiveTarget: {
|
||||
max: {
|
||||
field: 'slo.objective.target',
|
||||
},
|
||||
},
|
||||
goodEvents: {
|
||||
sum: {
|
||||
field: 'slo.numerator',
|
||||
},
|
||||
},
|
||||
totalEvents: {
|
||||
sum: {
|
||||
field: 'slo.denominator',
|
||||
},
|
||||
},
|
||||
sliValue: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
goodEvents: 'goodEvents',
|
||||
totalEvents: 'totalEvents',
|
||||
},
|
||||
script:
|
||||
'if (params.totalEvents == 0) { return -1 } else { return params.goodEvents / params.totalEvents }',
|
||||
},
|
||||
},
|
||||
errorBudgetInitial: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
objective: '_objectiveTarget',
|
||||
},
|
||||
script: '1 - params.objective',
|
||||
},
|
||||
},
|
||||
errorBudgetConsumed: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
sliValue: 'sliValue',
|
||||
errorBudgetInitial: 'errorBudgetInitial',
|
||||
},
|
||||
script:
|
||||
'if (params.sliValue == -1) { return 0 } else { return (1 - params.sliValue) / params.errorBudgetInitial }',
|
||||
},
|
||||
},
|
||||
errorBudgetRemaining: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
errorBudgetConsumed: 'errorBudgetConsumed',
|
||||
},
|
||||
script: '1 - params.errorBudgetConsumed',
|
||||
},
|
||||
},
|
||||
status: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
sliValue: 'sliValue',
|
||||
objective: '_objectiveTarget',
|
||||
errorBudgetRemaining: 'errorBudgetRemaining',
|
||||
},
|
||||
script:
|
||||
'if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objective) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Summarize every SLO with occurrences budgeting method and a weekly calendar aligned time window',
|
||||
frequency: '1m',
|
||||
sync: {
|
||||
time: {
|
||||
field: '@timestamp',
|
||||
delay: '60s',
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
deduce_mappings: false,
|
||||
},
|
||||
_meta: {
|
||||
version: SLO_RESOURCES_VERSION,
|
||||
managed: true,
|
||||
managed_by: 'observability',
|
||||
},
|
||||
};
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* 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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types';
|
||||
import {
|
||||
SLO_DESTINATION_INDEX_PATTERN,
|
||||
SLO_RESOURCES_VERSION,
|
||||
SLO_SUMMARY_DESTINATION_INDEX_NAME,
|
||||
SLO_SUMMARY_TRANSFORM_NAME_PREFIX,
|
||||
} from '../../../../assets/constants';
|
||||
|
||||
export const SUMMARY_TIMESLICES_30D_ROLLING: TransformPutTransformRequest = {
|
||||
transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}timeslices-30d-rolling`,
|
||||
dest: {
|
||||
index: SLO_SUMMARY_DESTINATION_INDEX_NAME,
|
||||
},
|
||||
source: {
|
||||
index: SLO_DESTINATION_INDEX_PATTERN,
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: 'now-30d/m',
|
||||
lte: 'now/m',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.budgetingMethod': 'timeslices',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.timeWindow.type': 'rolling',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.timeWindow.duration': '30d',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
pivot: {
|
||||
group_by: {
|
||||
'slo.id': {
|
||||
terms: {
|
||||
field: 'slo.id',
|
||||
},
|
||||
},
|
||||
'slo.revision': {
|
||||
terms: {
|
||||
field: 'slo.revision',
|
||||
},
|
||||
},
|
||||
'slo.instanceId': {
|
||||
terms: {
|
||||
field: 'slo.instanceId',
|
||||
},
|
||||
},
|
||||
'slo.budgetingMethod': {
|
||||
terms: {
|
||||
field: 'slo.budgetingMethod',
|
||||
},
|
||||
},
|
||||
'slo.timeWindow.duration': {
|
||||
terms: {
|
||||
field: 'slo.timeWindow.duration',
|
||||
},
|
||||
},
|
||||
'slo.timeWindow.type': {
|
||||
terms: {
|
||||
field: 'slo.timeWindow.type',
|
||||
},
|
||||
},
|
||||
},
|
||||
aggregations: {
|
||||
goodEvents: {
|
||||
sum: {
|
||||
field: 'slo.isGoodSlice',
|
||||
},
|
||||
},
|
||||
totalEvents: {
|
||||
value_count: {
|
||||
field: 'slo.isGoodSlice',
|
||||
},
|
||||
},
|
||||
_objectiveTarget: {
|
||||
max: {
|
||||
field: 'slo.objective.target',
|
||||
},
|
||||
},
|
||||
sliValue: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
goodEvents: 'goodEvents',
|
||||
totalEvents: 'totalEvents',
|
||||
},
|
||||
script:
|
||||
'if (params.totalEvents == 0) { return -1 } else { return params.goodEvents / params.totalEvents }',
|
||||
},
|
||||
},
|
||||
errorBudgetInitial: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
objectiveTarget: '_objectiveTarget',
|
||||
},
|
||||
script: '1 - params.objectiveTarget',
|
||||
},
|
||||
},
|
||||
errorBudgetConsumed: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
sliValue: 'sliValue',
|
||||
errorBudgetInitial: 'errorBudgetInitial',
|
||||
},
|
||||
script:
|
||||
'if (params.sliValue == -1) { return 0 } else { return (1 - params.sliValue) / params.errorBudgetInitial }',
|
||||
},
|
||||
},
|
||||
errorBudgetRemaining: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
errorBudgetConsummed: 'errorBudgetConsumed',
|
||||
},
|
||||
script: '1 - params.errorBudgetConsummed',
|
||||
},
|
||||
},
|
||||
status: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
sliValue: 'sliValue',
|
||||
objectiveTarget: '_objectiveTarget',
|
||||
errorBudgetRemaining: 'errorBudgetRemaining',
|
||||
},
|
||||
script: {
|
||||
source:
|
||||
'if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objectiveTarget) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Summarize every SLO with timeslices budgeting method and a 30 days rolling time window',
|
||||
frequency: '1m',
|
||||
sync: {
|
||||
time: {
|
||||
field: '@timestamp',
|
||||
delay: '60s',
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
deduce_mappings: false,
|
||||
},
|
||||
_meta: {
|
||||
version: SLO_RESOURCES_VERSION,
|
||||
managed: true,
|
||||
managed_by: 'observability',
|
||||
},
|
||||
};
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* 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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types';
|
||||
import {
|
||||
SLO_DESTINATION_INDEX_PATTERN,
|
||||
SLO_RESOURCES_VERSION,
|
||||
SLO_SUMMARY_DESTINATION_INDEX_NAME,
|
||||
SLO_SUMMARY_TRANSFORM_NAME_PREFIX,
|
||||
} from '../../../../assets/constants';
|
||||
|
||||
export const SUMMARY_TIMESLICES_7D_ROLLING: TransformPutTransformRequest = {
|
||||
transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}timeslices-7d-rolling`,
|
||||
dest: {
|
||||
index: SLO_SUMMARY_DESTINATION_INDEX_NAME,
|
||||
},
|
||||
source: {
|
||||
index: SLO_DESTINATION_INDEX_PATTERN,
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: 'now-7d/m',
|
||||
lte: 'now/m',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.budgetingMethod': 'timeslices',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.timeWindow.type': 'rolling',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.timeWindow.duration': '7d',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
pivot: {
|
||||
group_by: {
|
||||
'slo.id': {
|
||||
terms: {
|
||||
field: 'slo.id',
|
||||
},
|
||||
},
|
||||
'slo.revision': {
|
||||
terms: {
|
||||
field: 'slo.revision',
|
||||
},
|
||||
},
|
||||
'slo.instanceId': {
|
||||
terms: {
|
||||
field: 'slo.instanceId',
|
||||
},
|
||||
},
|
||||
'slo.budgetingMethod': {
|
||||
terms: {
|
||||
field: 'slo.budgetingMethod',
|
||||
},
|
||||
},
|
||||
'slo.timeWindow.duration': {
|
||||
terms: {
|
||||
field: 'slo.timeWindow.duration',
|
||||
},
|
||||
},
|
||||
'slo.timeWindow.type': {
|
||||
terms: {
|
||||
field: 'slo.timeWindow.type',
|
||||
},
|
||||
},
|
||||
},
|
||||
aggregations: {
|
||||
goodEvents: {
|
||||
sum: {
|
||||
field: 'slo.isGoodSlice',
|
||||
},
|
||||
},
|
||||
totalEvents: {
|
||||
value_count: {
|
||||
field: 'slo.isGoodSlice',
|
||||
},
|
||||
},
|
||||
_objectiveTarget: {
|
||||
max: {
|
||||
field: 'slo.objective.target',
|
||||
},
|
||||
},
|
||||
sliValue: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
goodEvents: 'goodEvents',
|
||||
totalEvents: 'totalEvents',
|
||||
},
|
||||
script:
|
||||
'if (params.totalEvents == 0) { return -1 } else { return params.goodEvents / params.totalEvents }',
|
||||
},
|
||||
},
|
||||
errorBudgetInitial: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
objectiveTarget: '_objectiveTarget',
|
||||
},
|
||||
script: '1 - params.objectiveTarget',
|
||||
},
|
||||
},
|
||||
errorBudgetConsumed: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
sliValue: 'sliValue',
|
||||
errorBudgetInitial: 'errorBudgetInitial',
|
||||
},
|
||||
script:
|
||||
'if (params.sliValue == -1) { return 0 } else { return (1 - params.sliValue) / params.errorBudgetInitial }',
|
||||
},
|
||||
},
|
||||
errorBudgetRemaining: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
errorBudgetConsummed: 'errorBudgetConsumed',
|
||||
},
|
||||
script: '1 - params.errorBudgetConsummed',
|
||||
},
|
||||
},
|
||||
status: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
sliValue: 'sliValue',
|
||||
objectiveTarget: '_objectiveTarget',
|
||||
errorBudgetRemaining: 'errorBudgetRemaining',
|
||||
},
|
||||
script: {
|
||||
source:
|
||||
'if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objectiveTarget) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Summarize every SLO with timeslices budgeting method and a 7 days rolling time window',
|
||||
frequency: '1m',
|
||||
sync: {
|
||||
time: {
|
||||
field: '@timestamp',
|
||||
delay: '60s',
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
deduce_mappings: false,
|
||||
},
|
||||
_meta: {
|
||||
version: SLO_RESOURCES_VERSION,
|
||||
managed: true,
|
||||
managed_by: 'observability',
|
||||
},
|
||||
};
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* 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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types';
|
||||
import {
|
||||
SLO_DESTINATION_INDEX_PATTERN,
|
||||
SLO_RESOURCES_VERSION,
|
||||
SLO_SUMMARY_DESTINATION_INDEX_NAME,
|
||||
SLO_SUMMARY_TRANSFORM_NAME_PREFIX,
|
||||
} from '../../../../assets/constants';
|
||||
|
||||
export const SUMMARY_TIMESLICES_90D_ROLLING: TransformPutTransformRequest = {
|
||||
transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}timeslices-90d-rolling`,
|
||||
dest: {
|
||||
index: SLO_SUMMARY_DESTINATION_INDEX_NAME,
|
||||
},
|
||||
source: {
|
||||
index: SLO_DESTINATION_INDEX_PATTERN,
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: 'now-90d/m',
|
||||
lte: 'now/m',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.budgetingMethod': 'timeslices',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.timeWindow.type': 'rolling',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.timeWindow.duration': '90d',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
pivot: {
|
||||
group_by: {
|
||||
'slo.id': {
|
||||
terms: {
|
||||
field: 'slo.id',
|
||||
},
|
||||
},
|
||||
'slo.revision': {
|
||||
terms: {
|
||||
field: 'slo.revision',
|
||||
},
|
||||
},
|
||||
'slo.instanceId': {
|
||||
terms: {
|
||||
field: 'slo.instanceId',
|
||||
},
|
||||
},
|
||||
'slo.budgetingMethod': {
|
||||
terms: {
|
||||
field: 'slo.budgetingMethod',
|
||||
},
|
||||
},
|
||||
'slo.timeWindow.duration': {
|
||||
terms: {
|
||||
field: 'slo.timeWindow.duration',
|
||||
},
|
||||
},
|
||||
'slo.timeWindow.type': {
|
||||
terms: {
|
||||
field: 'slo.timeWindow.type',
|
||||
},
|
||||
},
|
||||
},
|
||||
aggregations: {
|
||||
goodEvents: {
|
||||
sum: {
|
||||
field: 'slo.isGoodSlice',
|
||||
},
|
||||
},
|
||||
totalEvents: {
|
||||
value_count: {
|
||||
field: 'slo.isGoodSlice',
|
||||
},
|
||||
},
|
||||
_objectiveTarget: {
|
||||
max: {
|
||||
field: 'slo.objective.target',
|
||||
},
|
||||
},
|
||||
sliValue: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
goodEvents: 'goodEvents',
|
||||
totalEvents: 'totalEvents',
|
||||
},
|
||||
script:
|
||||
'if (params.totalEvents == 0) { return -1 } else { return params.goodEvents / params.totalEvents }',
|
||||
},
|
||||
},
|
||||
errorBudgetInitial: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
objectiveTarget: '_objectiveTarget',
|
||||
},
|
||||
script: '1 - params.objectiveTarget',
|
||||
},
|
||||
},
|
||||
errorBudgetConsumed: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
sliValue: 'sliValue',
|
||||
errorBudgetInitial: 'errorBudgetInitial',
|
||||
},
|
||||
script:
|
||||
'if (params.sliValue == -1) { return 0 } else { return (1 - params.sliValue) / params.errorBudgetInitial }',
|
||||
},
|
||||
},
|
||||
errorBudgetRemaining: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
errorBudgetConsummed: 'errorBudgetConsumed',
|
||||
},
|
||||
script: '1 - params.errorBudgetConsummed',
|
||||
},
|
||||
},
|
||||
status: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
sliValue: 'sliValue',
|
||||
objectiveTarget: '_objectiveTarget',
|
||||
errorBudgetRemaining: 'errorBudgetRemaining',
|
||||
},
|
||||
script: {
|
||||
source:
|
||||
'if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objectiveTarget) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Summarize every SLO with timeslices budgeting method and a 90 days rolling time window',
|
||||
frequency: '1m',
|
||||
sync: {
|
||||
time: {
|
||||
field: '@timestamp',
|
||||
delay: '60s',
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
deduce_mappings: false,
|
||||
},
|
||||
_meta: {
|
||||
version: SLO_RESOURCES_VERSION,
|
||||
managed: true,
|
||||
managed_by: 'observability',
|
||||
},
|
||||
};
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* 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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types';
|
||||
import {
|
||||
SLO_DESTINATION_INDEX_PATTERN,
|
||||
SLO_RESOURCES_VERSION,
|
||||
SLO_SUMMARY_DESTINATION_INDEX_NAME,
|
||||
SLO_SUMMARY_TRANSFORM_NAME_PREFIX,
|
||||
} from '../../../../assets/constants';
|
||||
|
||||
export const SUMMARY_TIMESLICES_MONTHLY_ALIGNED: TransformPutTransformRequest = {
|
||||
transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}timeslices-monthly-aligned`,
|
||||
dest: {
|
||||
index: SLO_SUMMARY_DESTINATION_INDEX_NAME,
|
||||
},
|
||||
source: {
|
||||
index: SLO_DESTINATION_INDEX_PATTERN,
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: 'now/M',
|
||||
lte: 'now/m',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.budgetingMethod': 'timeslices',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.timeWindow.type': 'calendarAligned',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.timeWindow.duration': '1M',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
pivot: {
|
||||
group_by: {
|
||||
'slo.id': {
|
||||
terms: {
|
||||
field: 'slo.id',
|
||||
},
|
||||
},
|
||||
'slo.revision': {
|
||||
terms: {
|
||||
field: 'slo.revision',
|
||||
},
|
||||
},
|
||||
'slo.instanceId': {
|
||||
terms: {
|
||||
field: 'slo.instanceId',
|
||||
},
|
||||
},
|
||||
'slo.budgetingMethod': {
|
||||
terms: {
|
||||
field: 'slo.budgetingMethod',
|
||||
},
|
||||
},
|
||||
'slo.timeWindow.duration': {
|
||||
terms: {
|
||||
field: 'slo.timeWindow.duration',
|
||||
},
|
||||
},
|
||||
'slo.timeWindow.type': {
|
||||
terms: {
|
||||
field: 'slo.timeWindow.type',
|
||||
},
|
||||
},
|
||||
},
|
||||
aggregations: {
|
||||
_sliceDurationInSeconds: {
|
||||
max: {
|
||||
field: 'slo.objective.sliceDurationInSeconds',
|
||||
},
|
||||
},
|
||||
_totalSlicesInPeriod: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
sliceDurationInSeconds: '_sliceDurationInSeconds',
|
||||
},
|
||||
script: {
|
||||
source: `
|
||||
Date d = new Date();
|
||||
Instant instant = Instant.ofEpochMilli(d.getTime());
|
||||
LocalDateTime now = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
|
||||
LocalDateTime startOfMonth = now
|
||||
.withDayOfMonth(1)
|
||||
.withHour(0)
|
||||
.withMinute(0)
|
||||
.withSecond(0);
|
||||
LocalDateTime startOfNextMonth = startOfMonth.plusMonths(1);
|
||||
double sliceDurationInMinutes = params.sliceDurationInSeconds / 60;
|
||||
|
||||
return Math.ceil(Duration.between(startOfMonth, startOfNextMonth).toMinutes() / sliceDurationInMinutes);
|
||||
`,
|
||||
},
|
||||
},
|
||||
},
|
||||
_objectiveTarget: {
|
||||
max: {
|
||||
field: 'slo.objective.target',
|
||||
},
|
||||
},
|
||||
goodEvents: {
|
||||
sum: {
|
||||
field: 'slo.isGoodSlice',
|
||||
},
|
||||
},
|
||||
totalEvents: {
|
||||
value_count: {
|
||||
field: 'slo.isGoodSlice',
|
||||
},
|
||||
},
|
||||
sliValue: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
goodEvents: 'goodEvents',
|
||||
totalEvents: 'totalEvents',
|
||||
},
|
||||
script:
|
||||
'if (params.totalEvents == 0) { return -1 } else { return params.goodEvents / params.totalEvents }',
|
||||
},
|
||||
},
|
||||
errorBudgetInitial: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
objective: '_objectiveTarget',
|
||||
},
|
||||
script: '1 - params.objective',
|
||||
},
|
||||
},
|
||||
errorBudgetConsumed: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
goodEvents: 'goodEvents',
|
||||
totalEvents: 'totalEvents',
|
||||
totalSlicesInPeriod: '_totalSlicesInPeriod',
|
||||
errorBudgetInitial: 'errorBudgetInitial',
|
||||
},
|
||||
script:
|
||||
'if (params.totalEvents == 0) { return 0 } else { return (params.totalEvents - params.goodEvents) / (params.totalSlicesInPeriod * params.errorBudgetInitial) }',
|
||||
},
|
||||
},
|
||||
errorBudgetRemaining: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
errorBudgetConsumed: 'errorBudgetConsumed',
|
||||
},
|
||||
script: '1 - params.errorBudgetConsumed',
|
||||
},
|
||||
},
|
||||
status: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
sliValue: 'sliValue',
|
||||
objective: '_objectiveTarget',
|
||||
errorBudgetRemaining: 'errorBudgetRemaining',
|
||||
},
|
||||
script:
|
||||
'if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objective) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Summarize every SLO with timeslices budgeting method and a monthly calendar aligned time window',
|
||||
frequency: '1m',
|
||||
sync: {
|
||||
time: {
|
||||
field: '@timestamp',
|
||||
delay: '60s',
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
deduce_mappings: false,
|
||||
},
|
||||
_meta: {
|
||||
version: SLO_RESOURCES_VERSION,
|
||||
managed: true,
|
||||
managed_by: 'observability',
|
||||
},
|
||||
};
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* 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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types';
|
||||
import {
|
||||
SLO_DESTINATION_INDEX_PATTERN,
|
||||
SLO_RESOURCES_VERSION,
|
||||
SLO_SUMMARY_DESTINATION_INDEX_NAME,
|
||||
SLO_SUMMARY_TRANSFORM_NAME_PREFIX,
|
||||
} from '../../../../assets/constants';
|
||||
|
||||
export const SUMMARY_TIMESLICES_WEEKLY_ALIGNED: TransformPutTransformRequest = {
|
||||
transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}timeslices-weekly-aligned`,
|
||||
dest: {
|
||||
index: SLO_SUMMARY_DESTINATION_INDEX_NAME,
|
||||
},
|
||||
source: {
|
||||
index: SLO_DESTINATION_INDEX_PATTERN,
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: 'now/w',
|
||||
lte: 'now/m',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.budgetingMethod': 'timeslices',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.timeWindow.type': 'calendarAligned',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
'slo.timeWindow.duration': '1w',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
pivot: {
|
||||
group_by: {
|
||||
'slo.id': {
|
||||
terms: {
|
||||
field: 'slo.id',
|
||||
},
|
||||
},
|
||||
'slo.revision': {
|
||||
terms: {
|
||||
field: 'slo.revision',
|
||||
},
|
||||
},
|
||||
'slo.instanceId': {
|
||||
terms: {
|
||||
field: 'slo.instanceId',
|
||||
},
|
||||
},
|
||||
'slo.budgetingMethod': {
|
||||
terms: {
|
||||
field: 'slo.budgetingMethod',
|
||||
},
|
||||
},
|
||||
'slo.timeWindow.duration': {
|
||||
terms: {
|
||||
field: 'slo.timeWindow.duration',
|
||||
},
|
||||
},
|
||||
'slo.timeWindow.type': {
|
||||
terms: {
|
||||
field: 'slo.timeWindow.type',
|
||||
},
|
||||
},
|
||||
},
|
||||
aggregations: {
|
||||
_sliceDurationInSeconds: {
|
||||
max: {
|
||||
field: 'slo.objective.sliceDurationInSeconds',
|
||||
},
|
||||
},
|
||||
_totalSlicesInPeriod: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
sliceDurationInSeconds: '_sliceDurationInSeconds',
|
||||
},
|
||||
script: 'Math.ceil(7 * 24 * 60 * 60 / params.sliceDurationInSeconds)',
|
||||
},
|
||||
},
|
||||
_objectiveTarget: {
|
||||
max: {
|
||||
field: 'slo.objective.target',
|
||||
},
|
||||
},
|
||||
goodEvents: {
|
||||
sum: {
|
||||
field: 'slo.isGoodSlice',
|
||||
},
|
||||
},
|
||||
totalEvents: {
|
||||
value_count: {
|
||||
field: 'slo.isGoodSlice',
|
||||
},
|
||||
},
|
||||
sliValue: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
goodEvents: 'goodEvents',
|
||||
totalEvents: 'totalEvents',
|
||||
},
|
||||
script:
|
||||
'if (params.totalEvents == 0) { return -1 } else { return params.goodEvents / params.totalEvents }',
|
||||
},
|
||||
},
|
||||
errorBudgetInitial: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
objective: '_objectiveTarget',
|
||||
},
|
||||
script: '1 - params.objective',
|
||||
},
|
||||
},
|
||||
errorBudgetConsumed: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
goodEvents: 'goodEvents',
|
||||
totalEvents: 'totalEvents',
|
||||
totalSlicesInPeriod: '_totalSlicesInPeriod',
|
||||
errorBudgetInitial: 'errorBudgetInitial',
|
||||
},
|
||||
script:
|
||||
'if (params.totalEvents == 0) { return 0 } else { return (params.totalEvents - params.goodEvents) / (params.totalSlicesInPeriod * params.errorBudgetInitial) }',
|
||||
},
|
||||
},
|
||||
errorBudgetRemaining: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
errorBudgetConsumed: 'errorBudgetConsumed',
|
||||
},
|
||||
script: '1 - params.errorBudgetConsumed',
|
||||
},
|
||||
},
|
||||
status: {
|
||||
bucket_script: {
|
||||
buckets_path: {
|
||||
sliValue: 'sliValue',
|
||||
objective: '_objectiveTarget',
|
||||
errorBudgetRemaining: 'errorBudgetRemaining',
|
||||
},
|
||||
script:
|
||||
'if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objective) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Summarize every SLO with timeslices budgeting method and a weekly calendar aligned time window',
|
||||
frequency: '1m',
|
||||
sync: {
|
||||
time: {
|
||||
field: '@timestamp',
|
||||
delay: '60s',
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
deduce_mappings: false,
|
||||
},
|
||||
_meta: {
|
||||
version: SLO_RESOURCES_VERSION,
|
||||
managed: true,
|
||||
managed_by: 'observability',
|
||||
},
|
||||
};
|
|
@ -64,11 +64,11 @@ Object {
|
|||
"_meta": Object {
|
||||
"managed": true,
|
||||
"managed_by": "observability",
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
},
|
||||
"description": "Rolled-up SLI data for SLO: irrelevant",
|
||||
"dest": Object {
|
||||
"index": ".slo-observability.sli-v1",
|
||||
"index": ".slo-observability.sli-v2",
|
||||
"pipeline": ".slo-observability.sli.monthly",
|
||||
},
|
||||
"frequency": "1m",
|
||||
|
@ -138,16 +138,46 @@ Object {
|
|||
"fixed_interval": "2m",
|
||||
},
|
||||
},
|
||||
"slo.budgetingMethod": Object {
|
||||
"terms": Object {
|
||||
"field": "slo.budgetingMethod",
|
||||
},
|
||||
},
|
||||
"slo.id": Object {
|
||||
"terms": Object {
|
||||
"field": "slo.id",
|
||||
},
|
||||
},
|
||||
"slo.instanceId": Object {
|
||||
"terms": Object {
|
||||
"field": "slo.instanceId",
|
||||
},
|
||||
},
|
||||
"slo.objective.sliceDurationInSeconds": Object {
|
||||
"terms": Object {
|
||||
"field": "slo.objective.sliceDurationInSeconds",
|
||||
},
|
||||
},
|
||||
"slo.objective.target": Object {
|
||||
"terms": Object {
|
||||
"field": "slo.objective.target",
|
||||
},
|
||||
},
|
||||
"slo.revision": Object {
|
||||
"terms": Object {
|
||||
"field": "slo.revision",
|
||||
},
|
||||
},
|
||||
"slo.timeWindow.duration": Object {
|
||||
"terms": Object {
|
||||
"field": "slo.timeWindow.duration",
|
||||
},
|
||||
},
|
||||
"slo.timeWindow.type": Object {
|
||||
"terms": Object {
|
||||
"field": "slo.timeWindow.type",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"settings": Object {
|
||||
|
@ -168,18 +198,54 @@ Object {
|
|||
},
|
||||
},
|
||||
"runtime_mappings": Object {
|
||||
"slo.budgetingMethod": Object {
|
||||
"script": Object {
|
||||
"source": "emit('timeslices')",
|
||||
},
|
||||
"type": "keyword",
|
||||
},
|
||||
"slo.id": Object {
|
||||
"script": Object {
|
||||
"source": Any<String>,
|
||||
},
|
||||
"type": "keyword",
|
||||
},
|
||||
"slo.instanceId": Object {
|
||||
"script": Object {
|
||||
"source": "emit('*')",
|
||||
},
|
||||
"type": "keyword",
|
||||
},
|
||||
"slo.objective.sliceDurationInSeconds": Object {
|
||||
"script": Object {
|
||||
"source": "emit(120)",
|
||||
},
|
||||
"type": "long",
|
||||
},
|
||||
"slo.objective.target": Object {
|
||||
"script": Object {
|
||||
"source": "emit(0.98)",
|
||||
},
|
||||
"type": "double",
|
||||
},
|
||||
"slo.revision": Object {
|
||||
"script": Object {
|
||||
"source": "emit(1)",
|
||||
},
|
||||
"type": "long",
|
||||
},
|
||||
"slo.timeWindow.duration": Object {
|
||||
"script": Object {
|
||||
"source": "emit('7d')",
|
||||
},
|
||||
"type": "keyword",
|
||||
},
|
||||
"slo.timeWindow.type": Object {
|
||||
"script": Object {
|
||||
"source": "emit('rolling')",
|
||||
},
|
||||
"type": "keyword",
|
||||
},
|
||||
},
|
||||
},
|
||||
"sync": Object {
|
||||
|
@ -197,11 +263,11 @@ Object {
|
|||
"_meta": Object {
|
||||
"managed": true,
|
||||
"managed_by": "observability",
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
},
|
||||
"description": "Rolled-up SLI data for SLO: irrelevant",
|
||||
"dest": Object {
|
||||
"index": ".slo-observability.sli-v1",
|
||||
"index": ".slo-observability.sli-v2",
|
||||
"pipeline": ".slo-observability.sli.monthly",
|
||||
},
|
||||
"frequency": "1m",
|
||||
|
@ -262,16 +328,41 @@ Object {
|
|||
"fixed_interval": "1m",
|
||||
},
|
||||
},
|
||||
"slo.budgetingMethod": Object {
|
||||
"terms": Object {
|
||||
"field": "slo.budgetingMethod",
|
||||
},
|
||||
},
|
||||
"slo.id": Object {
|
||||
"terms": Object {
|
||||
"field": "slo.id",
|
||||
},
|
||||
},
|
||||
"slo.instanceId": Object {
|
||||
"terms": Object {
|
||||
"field": "slo.instanceId",
|
||||
},
|
||||
},
|
||||
"slo.objective.target": Object {
|
||||
"terms": Object {
|
||||
"field": "slo.objective.target",
|
||||
},
|
||||
},
|
||||
"slo.revision": Object {
|
||||
"terms": Object {
|
||||
"field": "slo.revision",
|
||||
},
|
||||
},
|
||||
"slo.timeWindow.duration": Object {
|
||||
"terms": Object {
|
||||
"field": "slo.timeWindow.duration",
|
||||
},
|
||||
},
|
||||
"slo.timeWindow.type": Object {
|
||||
"terms": Object {
|
||||
"field": "slo.timeWindow.type",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"settings": Object {
|
||||
|
@ -292,18 +383,48 @@ Object {
|
|||
},
|
||||
},
|
||||
"runtime_mappings": Object {
|
||||
"slo.budgetingMethod": Object {
|
||||
"script": Object {
|
||||
"source": "emit('occurrences')",
|
||||
},
|
||||
"type": "keyword",
|
||||
},
|
||||
"slo.id": Object {
|
||||
"script": Object {
|
||||
"source": Any<String>,
|
||||
},
|
||||
"type": "keyword",
|
||||
},
|
||||
"slo.instanceId": Object {
|
||||
"script": Object {
|
||||
"source": "emit('*')",
|
||||
},
|
||||
"type": "keyword",
|
||||
},
|
||||
"slo.objective.target": Object {
|
||||
"script": Object {
|
||||
"source": "emit(0.999)",
|
||||
},
|
||||
"type": "double",
|
||||
},
|
||||
"slo.revision": Object {
|
||||
"script": Object {
|
||||
"source": "emit(1)",
|
||||
},
|
||||
"type": "long",
|
||||
},
|
||||
"slo.timeWindow.duration": Object {
|
||||
"script": Object {
|
||||
"source": "emit('7d')",
|
||||
},
|
||||
"type": "keyword",
|
||||
},
|
||||
"slo.timeWindow.type": Object {
|
||||
"script": Object {
|
||||
"source": "emit('rolling')",
|
||||
},
|
||||
"type": "keyword",
|
||||
},
|
||||
},
|
||||
},
|
||||
"sync": Object {
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
import { MappingRuntimeFieldType } from '@elastic/elasticsearch/lib/api/types';
|
||||
import { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import { ALL_VALUE, timeslicesBudgetingMethodSchema } from '@kbn/slo-schema';
|
||||
|
||||
import { TransformSettings } from '../../../assets/transform_templates/slo_transform_template';
|
||||
import { SLO } from '../../../domain/models';
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue