feat(slo): Add more data in SLO rollup index (#161392)

Resolves https://github.com/elastic/kibana/issues/161393

## Summary

This PR is the first of a series to implement the summary and search
improvement feature on SLO.
Every PR will be merged against the feature branch:
`slo/feature-branch`. And we'll update this feature branch with main as
often as possible to keep conflict as a minimum.

This PR changes the SLO rollup index mapping, adding more fields to it,
that we are going to use to summarize the SLO rollup data later.
It also includes the SLO summary index templates (mappings, settings,
template) and install them with the other templates.

Since this is a **breaking change**, any SLO running will be shown as
`NO_DATA`. You can remove the SLO through the API or the UI, or update
them (make sure you change something significant (time window, indicator
params, ...) in order to induce a revision bump) so the underlying
transform is recreated using the new index structure.
This commit is contained in:
Kevin Delemme 2023-07-07 10:55:48 -04:00
parent 477505a2dd
commit 39af36782e
18 changed files with 841 additions and 67 deletions

View file

@ -55,6 +55,10 @@ class Duration {
format(): string {
return `${this.value}${this.unit}`;
}
asSeconds(): number {
return moment.duration(this.value, toMomentUnitOfTime(this.unit)).asSeconds();
}
}
const toDurationUnit = (unit: string): DurationUnit => {

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { SLO_RESOURCES_VERSION } from '../constants';
export const getSLOMappingsTemplate = (name: string) => ({
name,
template: {
@ -23,6 +25,33 @@ export const getSLOMappingsTemplate = (name: string) => ({
revision: {
type: 'long',
},
instanceId: {
type: 'keyword',
ignore_above: 256,
},
objective: {
properties: {
target: {
type: 'double',
},
sliceDurationInSeconds: {
type: 'long',
},
},
},
budgetingMethod: {
type: 'keyword',
},
timeWindow: {
properties: {
duration: {
type: 'keyword',
},
type: {
type: 'keyword',
},
},
},
numerator: {
type: 'long',
},
@ -32,9 +61,6 @@ export const getSLOMappingsTemplate = (name: string) => ({
isGoodSlice: {
type: 'byte',
},
context: {
type: 'flattened',
},
},
},
},
@ -42,7 +68,7 @@ export const getSLOMappingsTemplate = (name: string) => ({
},
_meta: {
description: 'Mappings for SLO rollup data',
version: 1,
version: SLO_RESOURCES_VERSION,
managed: true,
managed_by: 'observability',
},

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { SLO_RESOURCES_VERSION } from '../constants';
export const getSLOSettingsTemplate = (name: string) => ({
name,
template: {
@ -14,7 +16,7 @@ export const getSLOSettingsTemplate = (name: string) => ({
},
_meta: {
description: 'Settings for SLO rollup data',
version: 1,
version: SLO_RESOURCES_VERSION,
managed: true,
managed_by: 'observability',
},

View file

@ -0,0 +1,73 @@
/*
* 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 { SLO_RESOURCES_VERSION } from '../constants';
export const getSLOSummaryMappingsTemplate = (name: string) => ({
name,
template: {
mappings: {
properties: {
slo: {
properties: {
id: {
type: 'keyword',
ignore_above: 256,
},
revision: {
type: 'long',
},
instanceId: {
type: 'keyword',
ignore_above: 256,
},
budgetingMethod: {
type: 'keyword',
},
timeWindow: {
properties: {
duration: {
type: 'keyword',
},
type: {
type: 'keyword',
},
},
},
},
},
sliValue: {
type: 'double',
},
goodEvents: {
type: 'long',
},
totalEvents: {
type: 'long',
},
errorBudgetInitial: {
type: 'double',
},
errorBudgetConsumed: {
type: 'double',
},
errorBudgetRemaining: {
type: 'double',
},
status: {
type: 'byte',
},
},
},
},
_meta: {
description: 'SLO summary mappings template',
version: SLO_RESOURCES_VERSION,
managed: true,
managed_by: 'observability',
},
});

View file

@ -0,0 +1,23 @@
/*
* 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 { SLO_RESOURCES_VERSION } from '../constants';
export const getSLOSummarySettingsTemplate = (name: string) => ({
name,
template: {
settings: {
hidden: true,
},
},
_meta: {
description: 'SLO summary settings template',
version: SLO_RESOURCES_VERSION,
managed: true,
managed_by: 'observability',
},
});

View file

@ -5,13 +5,25 @@
* 2.0.
*/
export const SLO_RESOURCES_VERSION = 2;
export const SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME = '.slo-observability.sli-mappings';
export const SLO_COMPONENT_TEMPLATE_SETTINGS_NAME = '.slo-observability.sli-settings';
export const SLO_INDEX_TEMPLATE_NAME = '.slo-observability.sli';
export const SLO_RESOURCES_VERSION = 1;
export const SLO_INGEST_PIPELINE_NAME = `${SLO_INDEX_TEMPLATE_NAME}.monthly`;
export const SLO_INDEX_TEMPLATE_PATTERN = `${SLO_INDEX_TEMPLATE_NAME}-*`;
export const SLO_DESTINATION_INDEX_NAME = `${SLO_INDEX_TEMPLATE_NAME}-v${SLO_RESOURCES_VERSION}`;
export const SLO_DESTINATION_INDEX_PATTERN = `${SLO_DESTINATION_INDEX_NAME}*`;
export const SLO_INGEST_PIPELINE_NAME = `${SLO_INDEX_TEMPLATE_NAME}.monthly`;
// slo-observability.sli-v<version>.(YYYY-MM-DD)
export const SLO_INGEST_PIPELINE_INDEX_NAME_PREFIX = `${SLO_DESTINATION_INDEX_NAME}.`;
export const SLO_SUMMARY_COMPONENT_TEMPLATE_MAPPINGS_NAME = '.slo-observability.summary-mappings';
export const SLO_SUMMARY_COMPONENT_TEMPLATE_SETTINGS_NAME = '.slo-observability.summary-settings';
export const SLO_SUMMARY_INDEX_TEMPLATE_NAME = '.slo-observability.summary';
export const SLO_SUMMARY_INDEX_TEMPLATE_PATTERN = `${SLO_SUMMARY_INDEX_TEMPLATE_NAME}-*`;
export const getSLOTransformId = (sloId: string, sloRevision: number) =>
`slo-${sloId}-${sloRevision}`;

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { SLO_RESOURCES_VERSION } from '../constants';
export const getSLOIndexTemplate = (name: string, indexPattern: string, composedOf: string[]) => ({
name,
index_patterns: [indexPattern],
@ -12,7 +14,7 @@ export const getSLOIndexTemplate = (name: string, indexPattern: string, composed
priority: 500,
_meta: {
description: 'Template for SLO rollup data',
version: 1,
version: SLO_RESOURCES_VERSION,
managed: true,
managed_by: 'observability',
},

View file

@ -0,0 +1,25 @@
/*
* 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 { SLO_RESOURCES_VERSION } from '../constants';
export const getSLOSummaryIndexTemplate = (
name: string,
indexPattern: string,
composedOf: string[]
) => ({
name,
index_patterns: [indexPattern],
composed_of: composedOf,
priority: 500,
_meta: {
description: 'SLO summary index template',
version: SLO_RESOURCES_VERSION,
managed: true,
managed_by: 'observability',
},
});

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { SLO_RESOURCES_VERSION } from '../constants';
export const getSLOPipelineTemplate = (id: string, indexNamePrefix: string) => ({
id,
description: 'Monthly date-time index naming for SLO data',
@ -19,7 +21,7 @@ export const getSLOPipelineTemplate = (id: string, indexNamePrefix: string) => (
],
_meta: {
description: 'SLO ingest pipeline',
version: 1,
version: SLO_RESOURCES_VERSION,
managed: true,
managed_by: 'observability',
},

View file

@ -12,6 +12,7 @@ import {
TransformSource,
TransformTimeSync,
} from '@elastic/elasticsearch/lib/api/types';
import { SLO_RESOURCES_VERSION } from '../constants';
export interface TransformSettings {
frequency: TransformPutTransformRequest['frequency'];
@ -47,7 +48,7 @@ export const getSLOTransformTemplate = (
aggregations,
},
_meta: {
version: 1,
version: SLO_RESOURCES_VERSION,
managed: true,
managed_by: 'observability',
},

View file

@ -16,7 +16,7 @@ Object {
exports[`SummaryClient fetchSummary with a rolling and occurrences composite SLO returns the summary 2`] = `
Array [
Object {
"index": ".slo-observability.sli-v1*",
"index": ".slo-observability.sli-v2*",
},
Object {
"aggs": Object {
@ -108,7 +108,7 @@ Object {
exports[`SummaryClient with rolling and timeslices SLO returns the summary 2`] = `
Array [
Object {
"index": ".slo-observability.sli-v1*",
"index": ".slo-observability.sli-v2*",
},
Object {
"aggs": Object {

View file

@ -5,28 +5,30 @@
* 2.0.
*/
import { IngestGetPipelineResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { elasticsearchServiceMock } from '@kbn/core/server/mocks';
import { loggerMock } from '@kbn/logging-mocks';
import {
SLO_INGEST_PIPELINE_NAME,
SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME,
SLO_COMPONENT_TEMPLATE_SETTINGS_NAME,
SLO_INDEX_TEMPLATE_NAME,
SLO_INGEST_PIPELINE_NAME,
SLO_RESOURCES_VERSION,
SLO_SUMMARY_COMPONENT_TEMPLATE_MAPPINGS_NAME,
SLO_SUMMARY_COMPONENT_TEMPLATE_SETTINGS_NAME,
SLO_SUMMARY_INDEX_TEMPLATE_NAME,
} from '../../assets/constants';
import { DefaultResourceInstaller } from './resource_installer';
describe('resourceInstaller', () => {
describe("when the common resources don't exist", () => {
describe('when the common resources are not installed yet', () => {
it('installs the common resources', async () => {
const mockClusterClient = elasticsearchServiceMock.createElasticsearchClient();
mockClusterClient.indices.existsIndexTemplate.mockResponseOnce(false);
mockClusterClient.indices.getIndexTemplate.mockResponseOnce({ index_templates: [] });
const installer = new DefaultResourceInstaller(mockClusterClient, loggerMock.create());
await installer.ensureCommonResourcesInstalled();
expect(mockClusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(2);
expect(mockClusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(4);
expect(mockClusterClient.cluster.putComponentTemplate).toHaveBeenNthCalledWith(
1,
expect.objectContaining({ name: SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME })
@ -35,23 +37,60 @@ describe('resourceInstaller', () => {
2,
expect.objectContaining({ name: SLO_COMPONENT_TEMPLATE_SETTINGS_NAME })
);
expect(mockClusterClient.indices.putIndexTemplate).toHaveBeenCalledWith(
expect(mockClusterClient.cluster.putComponentTemplate).toHaveBeenNthCalledWith(
3,
expect.objectContaining({ name: SLO_SUMMARY_COMPONENT_TEMPLATE_MAPPINGS_NAME })
);
expect(mockClusterClient.cluster.putComponentTemplate).toHaveBeenNthCalledWith(
4,
expect.objectContaining({ name: SLO_SUMMARY_COMPONENT_TEMPLATE_SETTINGS_NAME })
);
expect(mockClusterClient.indices.putIndexTemplate).toHaveBeenCalledTimes(2);
expect(mockClusterClient.indices.putIndexTemplate).toHaveBeenNthCalledWith(
1,
expect.objectContaining({ name: SLO_INDEX_TEMPLATE_NAME })
);
expect(mockClusterClient.indices.putIndexTemplate).toHaveBeenNthCalledWith(
2,
expect.objectContaining({ name: SLO_SUMMARY_INDEX_TEMPLATE_NAME })
);
expect(mockClusterClient.ingest.putPipeline).toHaveBeenCalledWith(
expect.objectContaining({ id: SLO_INGEST_PIPELINE_NAME })
);
});
});
describe('when the common resources exist', () => {
it('does not install the common resources', async () => {
describe('when the common resources are already installed', () => {
it('skips the installation', async () => {
const mockClusterClient = elasticsearchServiceMock.createElasticsearchClient();
mockClusterClient.indices.existsIndexTemplate.mockResponseOnce(true);
mockClusterClient.indices.getIndexTemplate.mockResponseOnce({
index_templates: [
{
name: SLO_INDEX_TEMPLATE_NAME,
index_template: {
index_patterns: [],
composed_of: [],
_meta: { version: SLO_RESOURCES_VERSION },
},
},
],
});
mockClusterClient.indices.getIndexTemplate.mockResponseOnce({
index_templates: [
{
name: SLO_SUMMARY_INDEX_TEMPLATE_NAME,
index_template: {
index_patterns: [],
composed_of: [],
_meta: { version: SLO_RESOURCES_VERSION },
},
},
],
});
mockClusterClient.ingest.getPipeline.mockResponseOnce({
// @ts-ignore _meta not typed properly
[SLO_INGEST_PIPELINE_NAME]: { _meta: { version: SLO_RESOURCES_VERSION } },
} as IngestGetPipelineResponse);
});
const installer = new DefaultResourceInstaller(mockClusterClient, loggerMock.create());
await installer.ensureCommonResourcesInstalled();

View file

@ -18,11 +18,20 @@ import {
SLO_COMPONENT_TEMPLATE_SETTINGS_NAME,
SLO_INDEX_TEMPLATE_NAME,
SLO_RESOURCES_VERSION,
SLO_INDEX_TEMPLATE_PATTERN,
SLO_INGEST_PIPELINE_INDEX_NAME_PREFIX,
SLO_SUMMARY_INDEX_TEMPLATE_NAME,
SLO_SUMMARY_COMPONENT_TEMPLATE_MAPPINGS_NAME,
SLO_SUMMARY_COMPONENT_TEMPLATE_SETTINGS_NAME,
SLO_SUMMARY_INDEX_TEMPLATE_PATTERN,
} from '../../assets/constants';
import { getSLOMappingsTemplate } from '../../assets/component_templates/slo_mappings_template';
import { getSLOSettingsTemplate } from '../../assets/component_templates/slo_settings_template';
import { getSLOIndexTemplate } from '../../assets/index_templates/slo_index_templates';
import { getSLOPipelineTemplate } from '../../assets/ingest_templates/slo_pipeline_template';
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';
export interface ResourceInstaller {
ensureCommonResourcesInstalled(): Promise<void>;
@ -35,9 +44,7 @@ export class DefaultResourceInstaller implements ResourceInstaller {
const alreadyInstalled = await this.areResourcesAlreadyInstalled();
if (alreadyInstalled) {
this.logger.debug(
`Skipping installation of resources shared for SLO since they already exist`
);
this.logger.debug('SLO resources already installed - skipping.');
return;
}
@ -49,37 +56,71 @@ export class DefaultResourceInstaller implements ResourceInstaller {
this.createOrUpdateComponentTemplate(
getSLOSettingsTemplate(SLO_COMPONENT_TEMPLATE_SETTINGS_NAME)
),
this.createOrUpdateComponentTemplate(
getSLOSummaryMappingsTemplate(SLO_SUMMARY_COMPONENT_TEMPLATE_MAPPINGS_NAME)
),
this.createOrUpdateComponentTemplate(
getSLOSummarySettingsTemplate(SLO_SUMMARY_COMPONENT_TEMPLATE_SETTINGS_NAME)
),
]);
await this.createOrUpdateIndexTemplate(
getSLOIndexTemplate(SLO_INDEX_TEMPLATE_NAME, `${SLO_INDEX_TEMPLATE_NAME}-*`, [
getSLOIndexTemplate(SLO_INDEX_TEMPLATE_NAME, SLO_INDEX_TEMPLATE_PATTERN, [
SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME,
SLO_COMPONENT_TEMPLATE_SETTINGS_NAME,
])
);
await this.createOrUpdateIngestPipelineTemplate(
getSLOPipelineTemplate(
SLO_INGEST_PIPELINE_NAME,
this.getPipelinePrefix(SLO_RESOURCES_VERSION)
await this.createOrUpdateIndexTemplate(
getSLOSummaryIndexTemplate(
SLO_SUMMARY_INDEX_TEMPLATE_NAME,
SLO_SUMMARY_INDEX_TEMPLATE_PATTERN,
[
SLO_SUMMARY_COMPONENT_TEMPLATE_MAPPINGS_NAME,
SLO_SUMMARY_COMPONENT_TEMPLATE_SETTINGS_NAME,
]
)
);
await this.createOrUpdateIngestPipelineTemplate(
getSLOPipelineTemplate(SLO_INGEST_PIPELINE_NAME, SLO_INGEST_PIPELINE_INDEX_NAME_PREFIX)
);
} catch (err) {
this.logger.error(`Error installing resources shared for SLO - ${err.message}`);
this.logger.error(`Error installing resources shared for SLO: ${err.message}`);
throw err;
}
}
private getPipelinePrefix(version: number): string {
// Following https://www.elastic.co/blog/an-introduction-to-the-elastic-data-stream-naming-scheme
// slo-observability.sli-<version>.<index-date>
return `${SLO_INDEX_TEMPLATE_NAME}-v${version}.`;
}
private async areResourcesAlreadyInstalled(): Promise<boolean> {
const indexTemplateExists = await this.esClient.indices.existsIndexTemplate({
name: SLO_INDEX_TEMPLATE_NAME,
});
let indexTemplateExists = false;
try {
const { index_templates: indexTemplates } = await this.esClient.indices.getIndexTemplate({
name: SLO_INDEX_TEMPLATE_NAME,
});
const sloIndexTemplate = indexTemplates.find(
(template) => template.name === SLO_INDEX_TEMPLATE_NAME
);
indexTemplateExists =
!!sloIndexTemplate &&
sloIndexTemplate.index_template._meta?.version === SLO_RESOURCES_VERSION;
} catch (err) {
return false;
}
let summaryIndexTemplateExists = false;
try {
const { index_templates: indexTemplates } = await this.esClient.indices.getIndexTemplate({
name: SLO_SUMMARY_INDEX_TEMPLATE_NAME,
});
const sloSummaryIndexTemplate = indexTemplates.find(
(template) => template.name === SLO_SUMMARY_INDEX_TEMPLATE_NAME
);
summaryIndexTemplateExists =
!!sloSummaryIndexTemplate &&
sloSummaryIndexTemplate.index_template._meta?.version === SLO_RESOURCES_VERSION;
} catch (err) {
return false;
}
let ingestPipelineExists = false;
try {
@ -92,7 +133,7 @@ export class DefaultResourceInstaller implements ResourceInstaller {
return false;
}
return indexTemplateExists && ingestPipelineExists;
return indexTemplateExists && summaryIndexTemplateExists && ingestPipelineExists;
}
private async createOrUpdateComponentTemplate(template: ClusterPutComponentTemplateRequest) {

View file

@ -144,11 +144,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",
@ -194,16 +194,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 {
@ -262,18 +292,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 {
@ -291,11 +357,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",
@ -332,16 +398,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 {
@ -400,18 +491,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 {

View file

@ -136,11 +136,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",
@ -179,16 +179,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 {
@ -243,18 +273,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 {
@ -272,11 +338,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",
@ -306,16 +372,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 {
@ -370,18 +461,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 {

View file

@ -105,11 +105,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",
@ -153,16 +153,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 {
@ -183,18 +213,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 {
@ -212,11 +278,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",
@ -251,16 +317,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 {
@ -281,18 +372,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 {

View file

@ -76,11 +76,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",
@ -162,16 +162,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 {
@ -192,18 +222,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 {
@ -221,11 +287,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",
@ -298,16 +364,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 {
@ -328,18 +419,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 {

View file

@ -7,7 +7,7 @@
import { MappingRuntimeFieldType } from '@elastic/elasticsearch/lib/api/types';
import { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { timeslicesBudgetingMethodSchema } from '@kbn/slo-schema';
import { ALL_VALUE, timeslicesBudgetingMethodSchema } from '@kbn/slo-schema';
import { TransformSettings } from '../../../assets/transform_templates/slo_transform_template';
import { SLO } from '../../../domain/models';
@ -29,6 +29,44 @@ export abstract class TransformGenerator {
source: `emit(${slo.revision})`,
},
},
'slo.instanceId': {
type: 'keyword' as MappingRuntimeFieldType,
script: {
source: `emit('${ALL_VALUE}')`,
},
},
'slo.objective.target': {
type: 'double' as MappingRuntimeFieldType,
script: {
source: `emit(${slo.objective.target})`,
},
},
...(slo.objective.timesliceWindow && {
'slo.objective.sliceDurationInSeconds': {
type: 'long' as MappingRuntimeFieldType,
script: {
source: `emit(${slo.objective.timesliceWindow!.asSeconds()})`,
},
},
}),
'slo.budgetingMethod': {
type: 'keyword' as MappingRuntimeFieldType,
script: {
source: `emit('${slo.budgetingMethod}')`,
},
},
'slo.timeWindow.duration': {
type: 'keyword' as MappingRuntimeFieldType,
script: {
source: `emit('${slo.timeWindow.duration.format()}')`,
},
},
'slo.timeWindow.type': {
type: 'keyword' as MappingRuntimeFieldType,
script: {
source: `emit('${slo.timeWindow.type}')`,
},
},
};
}
@ -43,16 +81,18 @@ export abstract class TransformGenerator {
}
return {
'slo.id': {
terms: {
field: 'slo.id',
'slo.id': { terms: { field: 'slo.id' } },
'slo.revision': { terms: { field: 'slo.revision' } },
'slo.instanceId': { terms: { field: 'slo.instanceId' } },
'slo.objective.target': { terms: { field: 'slo.objective.target' } },
...(slo.objective.timesliceWindow && {
'slo.objective.sliceDurationInSeconds': {
terms: { field: 'slo.objective.sliceDurationInSeconds' },
},
},
'slo.revision': {
terms: {
field: 'slo.revision',
},
},
}),
'slo.budgetingMethod': { terms: { field: 'slo.budgetingMethod' } },
'slo.timeWindow.duration': { terms: { field: 'slo.timeWindow.duration' } },
'slo.timeWindow.type': { terms: { field: 'slo.timeWindow.type' } },
// timestamp field defined in the destination index
'@timestamp': {
date_histogram: {