From 2ff007b2668bac58d12273440fc657d5876d227b Mon Sep 17 00:00:00 2001 From: Takuya Kosugiyama Date: Mon, 22 Aug 2022 17:11:54 +0900 Subject: [PATCH] Cloud Monitoring: Support SLO burn rate (#53710) --- .../google-cloud-monitoring/_index.md | 1 + pkg/tsdb/cloudmonitoring/cloudmonitoring.go | 8 ++- .../cloudmonitoring/cloudmonitoring_test.go | 20 +++++++ pkg/tsdb/cloudmonitoring/types.go | 1 + .../__mocks__/cloudMonitoringQuery.ts | 1 + .../Experimental/LookbackPeriodSelect.tsx | 47 ++++++++++++++++ .../Experimental/SLOQueryEditor.tsx | 12 ++++- .../components/SLO/LookbackPeriodSelect.tsx | 53 +++++++++++++++++++ .../components/SLO/SLOQueryEditor.tsx | 14 ++++- .../datasource/cloud-monitoring/constants.ts | 18 +++++++ .../datasource/cloud-monitoring/datasource.ts | 11 +++- .../datasource/cloud-monitoring/types.ts | 1 + 12 files changed, 182 insertions(+), 5 deletions(-) create mode 100644 public/app/plugins/datasource/cloud-monitoring/components/Experimental/LookbackPeriodSelect.tsx create mode 100644 public/app/plugins/datasource/cloud-monitoring/components/SLO/LookbackPeriodSelect.tsx diff --git a/docs/sources/datasources/google-cloud-monitoring/_index.md b/docs/sources/datasources/google-cloud-monitoring/_index.md index bbe74d431bb..d0bf56bbc64 100644 --- a/docs/sources/datasources/google-cloud-monitoring/_index.md +++ b/docs/sources/datasources/google-cloud-monitoring/_index.md @@ -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 diff --git a/pkg/tsdb/cloudmonitoring/cloudmonitoring.go b/pkg/tsdb/cloudmonitoring/cloudmonitoring.go index d65c1abdcbf..65f622737d9 100644 --- a/pkg/tsdb/cloudmonitoring/cloudmonitoring.go +++ b/pkg/tsdb/cloudmonitoring/cloudmonitoring.go @@ -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) { diff --git a/pkg/tsdb/cloudmonitoring/cloudmonitoring_test.go b/pkg/tsdb/cloudmonitoring/cloudmonitoring_test.go index 728611c541e..94b982f86a2 100644 --- a/pkg/tsdb/cloudmonitoring/cloudmonitoring_test.go +++ b/pkg/tsdb/cloudmonitoring/cloudmonitoring_test.go @@ -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) }) }) diff --git a/pkg/tsdb/cloudmonitoring/types.go b/pkg/tsdb/cloudmonitoring/types.go index 4d5d4fdbd65..e766a6920d2 100644 --- a/pkg/tsdb/cloudmonitoring/types.go +++ b/pkg/tsdb/cloudmonitoring/types.go @@ -68,6 +68,7 @@ type ( SelectorName string ServiceId string SloId string + LookbackPeriod string } grafanaQuery struct { diff --git a/public/app/plugins/datasource/cloud-monitoring/__mocks__/cloudMonitoringQuery.ts b/public/app/plugins/datasource/cloud-monitoring/__mocks__/cloudMonitoringQuery.ts index f41e0d8c8bb..191b64495aa 100644 --- a/public/app/plugins/datasource/cloud-monitoring/__mocks__/cloudMonitoringQuery.ts +++ b/public/app/plugins/datasource/cloud-monitoring/__mocks__/cloudMonitoringQuery.ts @@ -31,6 +31,7 @@ export const createMockSLOQuery: (overrides?: Partial) => SLOQuery = ( serviceName: '', sloId: '', sloName: '', + lookbackPeriod: '', ...overrides, }; }; diff --git a/public/app/plugins/datasource/cloud-monitoring/components/Experimental/LookbackPeriodSelect.tsx b/public/app/plugins/datasource/cloud-monitoring/components/Experimental/LookbackPeriodSelect.tsx new file mode 100644 index 00000000000..8a31e9125f9 --- /dev/null +++ b/public/app/plugins/datasource/cloud-monitoring/components/Experimental/LookbackPeriodSelect.tsx @@ -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>; + current?: string; +} + +export const LookbackPeriodSelect: React.FC = ({ 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 ( + + s.value === current)} + options={[ + { + label: 'Template Variables', + options: templateVariableOptions, + }, + { + label: 'Predefined periods', + expanded: true, + options: visibleOptions, + }, + ]} + onChange={({ value }) => onChange(value!)} + /> + + ); +}; diff --git a/public/app/plugins/datasource/cloud-monitoring/components/SLO/SLOQueryEditor.tsx b/public/app/plugins/datasource/cloud-monitoring/components/SLO/SLOQueryEditor.tsx index a61376d06d8..ea94d1fb2cf 100644 --- a/public/app/plugins/datasource/cloud-monitoring/components/SLO/SLOQueryEditor.tsx +++ b/public/app/plugins/datasource/cloud-monitoring/components/SLO/SLOQueryEditor.tsx @@ -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} > + {query.selectorName === SLO_BURN_RATE_SELECTOR_NAME && ( + onChange({ ...query, lookbackPeriod: lookbackPeriod })} + current={query.lookbackPeriod} + templateVariableOptions={variableOptionGroup.options} + /> + )} +