Cloud Monitoring: Support SLO burn rate (#53710)

This commit is contained in:
Takuya Kosugiyama 2022-08-22 17:11:54 +09:00 committed by GitHub
parent d5cc1bfff2
commit 2ff007b266
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 182 additions and 5 deletions

View File

@ -208,6 +208,7 @@ The friendly names for the time series selectors are shown in Grafana. Here is t
| SLI Value | select_slo_health |
| SLO Compliance | select_slo_compliance |
| SLO Error Budget Remaining | select_slo_budget_fraction |
| SLO Burn Rate | select_slo_burn_rate |
#### Alias patterns for SLO queries

View File

@ -429,7 +429,13 @@ func buildFilterString(metricType string, filterParts []string) string {
}
func buildSLOFilterExpression(q sloQuery) string {
return fmt.Sprintf(`%s("projects/%s/services/%s/serviceLevelObjectives/%s")`, q.SelectorName, q.ProjectName, q.ServiceId, q.SloId)
sloName := fmt.Sprintf("projects/%s/services/%s/serviceLevelObjectives/%s", q.ProjectName, q.ServiceId, q.SloId)
if q.SelectorName == "select_slo_burn_rate" {
return fmt.Sprintf(`%s("%s", "%s")`, q.SelectorName, sloName, q.LookbackPeriod)
} else {
return fmt.Sprintf(`%s("%s")`, q.SelectorName, sloName)
}
}
func setMetricAggParams(params *url.Values, query *metricQuery, durationSeconds int, intervalMs int64) {

View File

@ -589,6 +589,26 @@ func TestCloudMonitoring(t *testing.T) {
dl := qqueries[0].buildDeepLink()
assert.Empty(t, dl)
req.Queries[0].JSON = json.RawMessage(`{
"queryType": "slo",
"sloQuery": {
"projectName": "test-proj",
"alignmentPeriod": "stackdriver-auto",
"perSeriesAligner": "ALIGN_NEXT_OLDER",
"aliasBy": "",
"selectorName": "select_slo_burn_rate",
"serviceId": "test-service",
"sloId": "test-slo",
"lookbackPeriod": "1h"
},
"metricQuery": {}
}`)
qes, err = service.buildQueryExecutors(req)
require.NoError(t, err)
qqqueries := getCloudMonitoringQueriesFromInterface(t, qes)
assert.Equal(t, `aggregation.alignmentPeriod=%2B60s&aggregation.perSeriesAligner=ALIGN_NEXT_OLDER&filter=select_slo_burn_rate%28%22projects%2Ftest-proj%2Fservices%2Ftest-service%2FserviceLevelObjectives%2Ftest-slo%22%2C+%221h%22%29&interval.endTime=2018-03-15T13%3A34%3A00Z&interval.startTime=2018-03-15T13%3A00%3A00Z`, qqqueries[0].Target)
})
})

View File

@ -68,6 +68,7 @@ type (
SelectorName string
ServiceId string
SloId string
LookbackPeriod string
}
grafanaQuery struct {

View File

@ -31,6 +31,7 @@ export const createMockSLOQuery: (overrides?: Partial<SLOQuery>) => SLOQuery = (
serviceName: '',
sloId: '',
sloName: '',
lookbackPeriod: '',
...overrides,
};
};

View File

@ -0,0 +1,47 @@
import React from 'react';
import { SelectableValue } from '@grafana/data';
import { EditorField, Select } from '@grafana/ui';
import { LOOKBACK_PERIODS } from '../../constants';
export interface Props {
refId: string;
onChange: (lookbackPeriod: string) => void;
templateVariableOptions: Array<SelectableValue<string>>;
current?: string;
}
export const LookbackPeriodSelect: React.FC<Props> = ({ refId, current, templateVariableOptions, onChange }) => {
const options = LOOKBACK_PERIODS.map((lp) => ({
...lp,
label: lp.text,
}));
if (current && !options.find((op) => op.value === current)) {
options.push({ label: current, text: current, value: current, hidden: false });
}
const visibleOptions = options.filter((lp) => !lp.hidden);
return (
<EditorField label="Lookback period" htmlFor={`${refId}-lookback-period`}>
<Select
inputId={`${refId}-lookback-period`}
width="auto"
allowCustomValue
value={[...options, ...templateVariableOptions].find((s) => s.value === current)}
options={[
{
label: 'Template Variables',
options: templateVariableOptions,
},
{
label: 'Predefined periods',
expanded: true,
options: visibleOptions,
},
]}
onChange={({ value }) => onChange(value!)}
/>
</EditorField>
);
};

View File

@ -3,12 +3,13 @@ import React, { useMemo } from 'react';
import { SelectableValue } from '@grafana/data';
import { EditorField, EditorFieldGroup, EditorRow } from '@grafana/ui';
import { ALIGNMENT_PERIODS } from '../../constants';
import { ALIGNMENT_PERIODS, SLO_BURN_RATE_SELECTOR_NAME } from '../../constants';
import CloudMonitoringDatasource from '../../datasource';
import { alignmentPeriodLabel } from '../../functions';
import { AlignmentTypes, CustomMetaData, SLOQuery } from '../../types';
import { AliasBy } from './AliasBy';
import { LookbackPeriodSelect } from './LookbackPeriodSelect';
import { PeriodSelect } from './PeriodSelect';
import { Project } from './Project';
import { SLO } from './SLO';
@ -35,6 +36,7 @@ export const defaultQuery: (dataSource: CloudMonitoringDatasource) => SLOQuery =
serviceName: '',
sloId: '',
sloName: '',
lookbackPeriod: '',
});
export function SLOQueryEditor({
@ -77,6 +79,14 @@ export function SLOQueryEditor({
query={query}
onChange={onChange}
/>
{query.selectorName === SLO_BURN_RATE_SELECTOR_NAME && (
<LookbackPeriodSelect
refId={refId}
onChange={(lookbackPeriod) => onChange({ ...query, lookbackPeriod: lookbackPeriod })}
current={query.lookbackPeriod}
templateVariableOptions={variableOptionGroup.options}
/>
)}
<EditorFieldGroup>
<EditorField label="Alignment period" tooltip={alignmentLabel}>

View File

@ -0,0 +1,53 @@
import React, { FunctionComponent } from 'react';
import { SelectableValue } from '@grafana/data';
import { Select } from '@grafana/ui';
import { QueryEditorRow } from '..';
import { SELECT_WIDTH, LOOKBACK_PERIODS } from '../../constants';
export interface Props {
refId: string;
onChange: (lookbackPeriod: string) => void;
templateVariableOptions: Array<SelectableValue<string>>;
current?: string;
}
export const LookbackPeriodSelect: FunctionComponent<Props> = ({
refId,
current,
templateVariableOptions,
onChange,
}) => {
const options = LOOKBACK_PERIODS.map((lp) => ({
...lp,
label: lp.text,
}));
if (current && !options.find((op) => op.value === current)) {
options.push({ label: current, text: current, value: current, hidden: false });
}
const visibleOptions = options.filter((lp) => !lp.hidden);
return (
<QueryEditorRow label="Lookback period" htmlFor={`${refId}-lookback-period`}>
<Select
inputId={`${refId}-lookback-period`}
width={SELECT_WIDTH}
allowCustomValue
value={[...options, ...templateVariableOptions].find((s) => s.value === current)}
options={[
{
label: 'Template Variables',
options: templateVariableOptions,
},
{
label: 'Predefined periods',
expanded: true,
options: visibleOptions,
},
]}
onChange={({ value }) => onChange(value!)}
/>
</QueryEditorRow>
);
};

View File

@ -3,10 +3,12 @@ import React from 'react';
import { SelectableValue } from '@grafana/data';
import { AliasBy, PeriodSelect, AlignmentPeriodLabel, Project, QueryEditorRow } from '..';
import { ALIGNMENT_PERIODS, SELECT_WIDTH } from '../../constants';
import { ALIGNMENT_PERIODS, SELECT_WIDTH, SLO_BURN_RATE_SELECTOR_NAME } from '../../constants';
import CloudMonitoringDatasource from '../../datasource';
import { AlignmentTypes, CustomMetaData, SLOQuery } from '../../types';
import { LookbackPeriodSelect } from './LookbackPeriodSelect';
import { Selector, Service, SLO } from '.';
export interface Props {
@ -29,6 +31,7 @@ export const defaultQuery: (dataSource: CloudMonitoringDatasource) => SLOQuery =
serviceName: '',
sloId: '',
sloName: '',
lookbackPeriod: '',
});
export function SLOQueryEditor({
@ -70,6 +73,15 @@ export function SLOQueryEditor({
onChange={onChange}
></Selector>
{query.selectorName === SLO_BURN_RATE_SELECTOR_NAME && (
<LookbackPeriodSelect
refId={refId}
onChange={(lookbackPeriod) => onChange({ ...query, lookbackPeriod: lookbackPeriod })}
current={query.lookbackPeriod}
templateVariableOptions={variableOptionGroup.options}
/>
)}
<QueryEditorRow label="Alignment period" htmlFor={`${refId}-alignment-period`}>
<PeriodSelect
inputId={`${refId}-alignment-period`}

View File

@ -278,6 +278,21 @@ export const GRAPH_PERIODS: periodOption[] = [
{ text: '1w', value: '1w' },
];
// Usable units: ns, us, ms, s, m, h
// ref. https://cloud.google.com/stackdriver/docs/solutions/slo-monitoring/api/timeseries-selectors#tss-names-args
export const LOOKBACK_PERIODS: periodOption[] = [
{ text: '1m', value: '1m' },
{ text: '2m', value: '2m' },
{ text: '5m', value: '5m' },
{ text: '10m', value: '10m' },
{ text: '30m', value: '30m' },
{ text: '1h', value: '1h' },
{ text: '3h', value: '3h' },
{ text: '6h', value: '6h' },
{ text: '24h', value: '24h' },
{ text: '72h', value: '72h' },
];
export const SYSTEM_LABELS = [
'metadata.system_labels.cloud_account',
'metadata.system_labels.name',
@ -291,10 +306,13 @@ export const SYSTEM_LABELS = [
'metadata.system_labels.container_image',
];
export const SLO_BURN_RATE_SELECTOR_NAME = 'select_slo_burn_rate';
export const SELECTORS = [
{ label: 'SLI Value', value: 'select_slo_health' },
{ label: 'SLO Compliance', value: 'select_slo_compliance' },
{ label: 'SLO Error Budget Remaining', value: 'select_slo_budget_fraction' },
{ label: 'SLO Burn Rate', value: SLO_BURN_RATE_SELECTOR_NAME },
];
export const QUERY_TYPES = [

View File

@ -14,6 +14,7 @@ import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv';
import { CloudMonitoringAnnotationSupport } from './annotationSupport';
import { SLO_BURN_RATE_SELECTOR_NAME } from './constants';
import {
CloudMonitoringOptions,
CloudMonitoringQuery,
@ -224,8 +225,14 @@ export default class CloudMonitoringDatasource extends DataSourceWithBackend<
}
if (query.queryType && query.queryType === QueryType.SLO && query.sloQuery) {
const { selectorName, serviceId, sloId, projectName } = query.sloQuery;
return !!selectorName && !!serviceId && !!sloId && !!projectName;
const { selectorName, serviceId, sloId, projectName, lookbackPeriod } = query.sloQuery;
return (
!!selectorName &&
!!serviceId &&
!!sloId &&
!!projectName &&
(selectorName !== SLO_BURN_RATE_SELECTOR_NAME || !!lookbackPeriod)
);
}
if (query.queryType && query.queryType === QueryType.METRICS && query.metricQuery.editorMode === EditorMode.MQL) {

View File

@ -143,6 +143,7 @@ export interface SLOQuery extends BaseQuery {
sloId: string;
sloName: string;
goal?: number;
lookbackPeriod?: string;
}
export interface CloudMonitoringQuery extends DataQuery {