Skip to content

Commit

Permalink
[SLO] Add synthetics availability SLI (elastic#177842)
Browse files Browse the repository at this point in the history
## Summary

Relates to
elastic/actionable-observability#122
Relates to elastic/observability-dev#2931

Adds the Synthetics SLI

Also adds a concept of a [`meta` key
](https://github.com/elastic/kibana/pull/177842/files#diff-9540eeb8237fdcf36ceac67af9cc67e192c7b67ff1c9a944db3635f9e953a9a1R139)
to the `SLOWithSummaryResponse` object, to hold arbitrary fields related
to a specific indicator. In the case of synthetics, I'm using this key
to store `config_id` (the monitor's SO id) and `observer.name` (the
monitors location id). These keys are critical for linking back to
Synthetics, but not available elsewhere.

## Release note
Adds the ability to create an SLI based on the availability of your
synthetics monitors. Select one or multiple monitors, or groups of
monitors via projects or tags. Individual SLOs will automatically be
generated for each monitor and location combination, based on your
specified filters.

<img width="862" alt="Screenshot 2024-03-04 at 8 40 37 AM"
src="https://github.com/elastic/kibana/assets/11356435/53c84fcb-6209-43f4-bac4-b10032795d02">
<img width="724" alt="Screenshot 2024-03-04 at 8 41 47 AM"
src="https://github.com/elastic/kibana/assets/11356435/89a9ef2c-82c6-470a-ac03-e19bd24e52c9">
<img width="1435" alt="Screenshot 2024-03-04 at 8 41 59 AM"
src="https://github.com/elastic/kibana/assets/11356435/44e9843f-053b-447c-9298-6ec4c66496d8">

### Testing
1. On main, create a few different SLIs
2. Check out this branch
3. Create synthetics monitors. You can do so by creating an oblt
cluster, using the `kibana.yml` provided, and then navigating to the
Synthetics app. Be sure to create monitors with tags.
4. Navigate to the SLO page. Create a test Synthetics SLI. Ensure that
the tags you created appear in the `tags` suggestions, and the monitors
you created appear in the `monitor name` suggestions. When filtering by
a specific tag, you should only see monitors with that tag. On the
opposite side, when filtering by a monitor name(s) you should only see
tags related to those monitors.
5. Navigate to the SLO overview. Ensure the SLO is computed correctly
6. Navigate to the SLO details page. Ensure all of the visualizations
are populated correctly. Ensure the groupings value appear correct.
Under the `Synthetics Monitor` key in the Overview table, click the name
and ensure that you can navigate to the Synthetics app successfully.


### Checklist

Delete any items that are not applicable to this PR.

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [ ] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [ ] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)

---------

Co-authored-by: shahzad31 <shahzad31comp@gmail.com>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
3 people authored Mar 8, 2024
1 parent 5b63660 commit 8a594a4
Show file tree
Hide file tree
Showing 48 changed files with 2,194 additions and 182 deletions.
9 changes: 8 additions & 1 deletion x-pack/packages/kbn-slo-schema/src/rest_specs/slo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
allOrAnyString,
apmTransactionDurationIndicatorSchema,
apmTransactionErrorRateIndicatorSchema,
syntheticsAvailabilityIndicatorSchema,
budgetingMethodSchema,
dateType,
durationType,
Expand All @@ -21,6 +22,7 @@ import {
indicatorTypesSchema,
kqlCustomIndicatorSchema,
metricCustomIndicatorSchema,
metaSchema,
timesliceMetricIndicatorSchema,
objectiveSchema,
optionalSettingsSchema,
Expand Down Expand Up @@ -151,7 +153,10 @@ const sloResponseSchema = t.intersection([

const sloWithSummaryResponseSchema = t.intersection([
sloResponseSchema,
t.type({ summary: summarySchema, groupings: groupingsSchema }),
t.intersection([
t.type({ summary: summarySchema, groupings: groupingsSchema }),
t.partial({ meta: metaSchema }),
]),
]);

const sloGroupWithSummaryResponseSchema = t.type({
Expand Down Expand Up @@ -335,6 +340,7 @@ type Indicator = t.OutputOf<typeof indicatorSchema>;
type Objective = t.OutputOf<typeof objectiveSchema>;
type APMTransactionErrorRateIndicator = t.OutputOf<typeof apmTransactionErrorRateIndicatorSchema>;
type APMTransactionDurationIndicator = t.OutputOf<typeof apmTransactionDurationIndicatorSchema>;
type SyntheticsAvailabilityIndicator = t.OutputOf<typeof syntheticsAvailabilityIndicatorSchema>;
type MetricCustomIndicator = t.OutputOf<typeof metricCustomIndicatorSchema>;
type TimesliceMetricIndicator = t.OutputOf<typeof timesliceMetricIndicatorSchema>;
type TimesliceMetricBasicMetricWithField = t.OutputOf<typeof timesliceMetricBasicMetricWithField>;
Expand Down Expand Up @@ -406,6 +412,7 @@ export type {
UpdateSLOResponse,
APMTransactionDurationIndicator,
APMTransactionErrorRateIndicator,
SyntheticsAvailabilityIndicator,
GetSLOBurnRatesResponse,
GetSLOInstancesResponse,
IndicatorType,
Expand Down
9 changes: 9 additions & 0 deletions x-pack/packages/kbn-slo-schema/src/schema/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ const summarySchema = t.type({

const groupingsSchema = t.record(t.string, t.union([t.string, t.number]));

const metaSchema = t.partial({
synthetics: t.type({
monitorId: t.string,
locationId: t.string,
configId: t.string,
}),
});

const groupSummarySchema = t.type({
total: t.number,
worst: t.type({
Expand Down Expand Up @@ -132,6 +140,7 @@ export {
previewDataSchema,
statusSchema,
summarySchema,
metaSchema,
groupSummarySchema,
kqlWithFiltersSchema,
querySchema,
Expand Down
24 changes: 24 additions & 0 deletions x-pack/packages/kbn-slo-schema/src/schema/indicators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,26 @@ const histogramIndicatorSchema = t.type({
]),
});

const syntheticsParamSchema = t.type({
value: allOrAnyString,
label: allOrAnyString,
});
const syntheticsAvailabilityIndicatorTypeSchema = t.literal('sli.synthetics.availability');
const syntheticsAvailabilityIndicatorSchema = t.type({
type: syntheticsAvailabilityIndicatorTypeSchema,
params: t.intersection([
t.type({
monitorIds: t.array(syntheticsParamSchema),
index: t.string,
}),
t.partial({
tags: t.array(syntheticsParamSchema),
projects: t.array(syntheticsParamSchema),
filter: querySchema,
}),
]),
});

const indicatorDataSchema = t.type({
dateRange: dateRangeSchema,
good: t.number,
Expand All @@ -231,6 +251,7 @@ const indicatorDataSchema = t.type({
const indicatorTypesSchema = t.union([
apmTransactionDurationIndicatorTypeSchema,
apmTransactionErrorRateIndicatorTypeSchema,
syntheticsAvailabilityIndicatorTypeSchema,
kqlCustomIndicatorTypeSchema,
metricCustomIndicatorTypeSchema,
timesliceMetricIndicatorTypeSchema,
Expand Down Expand Up @@ -259,6 +280,7 @@ const indicatorTypesArraySchema = new t.Type<string[], string, unknown>(
const indicatorSchema = t.union([
apmTransactionDurationIndicatorSchema,
apmTransactionErrorRateIndicatorSchema,
syntheticsAvailabilityIndicatorSchema,
kqlCustomIndicatorSchema,
metricCustomIndicatorSchema,
timesliceMetricIndicatorSchema,
Expand All @@ -270,6 +292,8 @@ export {
apmTransactionDurationIndicatorTypeSchema,
apmTransactionErrorRateIndicatorSchema,
apmTransactionErrorRateIndicatorTypeSchema,
syntheticsAvailabilityIndicatorSchema,
syntheticsAvailabilityIndicatorTypeSchema,
kqlCustomIndicatorSchema,
kqlCustomIndicatorTypeSchema,
metricCustomIndicatorSchema,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,5 @@ export const getSLOSummaryTransformId = (sloId: string, sloRevision: number) =>

export const getSLOSummaryPipelineId = (sloId: string, sloRevision: number) =>
`.slo-observability.summary.pipeline-${sloId}-${sloRevision}`;

export const SYNTHETICS_INDEX_PATTERN = 'synthetics-*';
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ const baseSlo: Omit<SLOWithSummaryResponse, 'id'> = {
createdAt: now,
updatedAt: now,
version: 2,
meta: {},
};

export const sloList: FindSLOResponse = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* 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 { useQuery } from '@tanstack/react-query';

import { useKibana } from '../../utils/kibana_react';

export interface Suggestion {
label: string;
value: string;
count: number;
}

export interface UseFetchSyntheticsSuggestions {
suggestions: Suggestion[];
isLoading: boolean;
isSuccess: boolean;
isError: boolean;
}

export interface Params {
fieldName: string;
filters?: {
locations?: string[];
monitorIds?: string[];
tags?: string[];
projects?: string[];
};
search: string;
}

type ApiResponse = Record<string, Suggestion[]>;

export function useFetchSyntheticsSuggestions({
filters,
fieldName,
search,
}: Params): UseFetchSyntheticsSuggestions {
const { http } = useKibana().services;
const { locations, monitorIds, tags, projects } = filters || {};

const { isInitialLoading, isLoading, isError, isSuccess, isRefetching, data } = useQuery({
queryKey: ['fetchSyntheticsSuggestions', locations, monitorIds, tags, projects, search],
queryFn: async ({ signal }) => {
try {
const suggestions = await http.get<ApiResponse>('/internal/synthetics/suggestions', {
query: {
locations: locations || [],
monitorQueryIds: monitorIds || [],
tags: tags || [],
projects: projects || [],
query: search,
},
signal,
});

return suggestions;
} catch (error) {
// ignore error
}
},
refetchOnWindowFocus: false,
keepPreviousData: true,
});

return {
suggestions: isInitialLoading ? [] : data?.[fieldName] ?? [],
isLoading: isInitialLoading || isLoading || isRefetching,
isSuccess,
isError,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe('SloEditLocator', () => {
it('should return correct url when slo is provided', async () => {
const location = await locator.getLocation(buildSlo({ id: 'foo' }));
expect(location.path).toEqual(
"/slos/edit/foo?_a=(budgetingMethod:occurrences,createdAt:'2022-12-29T10:11:12.000Z',description:'some%20description%20useful',enabled:!t,groupBy:'*',groupings:(),id:foo,indicator:(params:(filter:'baz:%20foo%20and%20bar%20%3E%202',good:'http_status:%202xx',index:some-index,timestampField:custom_timestamp,total:'a%20query'),type:sli.kql.custom),instanceId:'*',name:'super%20important%20level%20service',objective:(target:0.98),revision:1,settings:(frequency:'1m',syncDelay:'1m'),summary:(errorBudget:(consumed:0.064,initial:0.02,isEstimated:!f,remaining:0.936),sliValue:0.99872,status:HEALTHY),tags:!(k8s,production,critical),timeWindow:(duration:'30d',type:rolling),updatedAt:'2022-12-29T10:11:12.000Z',version:2)"
"/slos/edit/foo?_a=(budgetingMethod:occurrences,createdAt:'2022-12-29T10:11:12.000Z',description:'some%20description%20useful',enabled:!t,groupBy:'*',groupings:(),id:foo,indicator:(params:(filter:'baz:%20foo%20and%20bar%20%3E%202',good:'http_status:%202xx',index:some-index,timestampField:custom_timestamp,total:'a%20query'),type:sli.kql.custom),instanceId:'*',meta:(),name:'super%20important%20level%20service',objective:(target:0.98),revision:1,settings:(frequency:'1m',syncDelay:'1m'),summary:(errorBudget:(consumed:0.064,initial:0.02,isEstimated:!f,remaining:0.936),sliValue:0.99872,status:HEALTHY),tags:!(k8s,production,critical),timeWindow:(duration:'30d',type:rolling),updatedAt:'2022-12-29T10:11:12.000Z',version:2)"
);
});
});
Loading

0 comments on commit 8a594a4

Please sign in to comment.