From f2f87c199c36e916939f9b02e6c11e54c65a3031 Mon Sep 17 00:00:00 2001 From: Julien Levesy Date: Sun, 7 Apr 2024 11:54:18 +0200 Subject: [PATCH 1/4] feat(cloudmonitoring): implement builders --- target/cloudmonitoring/cloudmonitoring.go | 81 +++++++++++++++ target/cloudmonitoring/mql.go | 47 +++++++++ target/cloudmonitoring/mql_test.go | 55 +++++++++++ target/cloudmonitoring/promql.go | 46 +++++++++ target/cloudmonitoring/promql_test.go | 61 ++++++++++++ target/cloudmonitoring/slo.go | 68 +++++++++++++ target/cloudmonitoring/slo_test.go | 63 ++++++++++++ target/cloudmonitoring/timeseries.go | 112 +++++++++++++++++++++ target/cloudmonitoring/timeseries_test.go | 114 ++++++++++++++++++++++ 9 files changed, 647 insertions(+) create mode 100644 target/cloudmonitoring/cloudmonitoring.go create mode 100644 target/cloudmonitoring/mql.go create mode 100644 target/cloudmonitoring/mql_test.go create mode 100644 target/cloudmonitoring/promql.go create mode 100644 target/cloudmonitoring/promql_test.go create mode 100644 target/cloudmonitoring/slo.go create mode 100644 target/cloudmonitoring/slo_test.go create mode 100644 target/cloudmonitoring/timeseries.go create mode 100644 target/cloudmonitoring/timeseries_test.go diff --git a/target/cloudmonitoring/cloudmonitoring.go b/target/cloudmonitoring/cloudmonitoring.go new file mode 100644 index 00000000..1af97fb1 --- /dev/null +++ b/target/cloudmonitoring/cloudmonitoring.go @@ -0,0 +1,81 @@ +package cloudmonitoring + +import "github.com/K-Phoen/sdk" + +// AutoAlignmentPeriod lets Google Clound Monitoring decide what it the ideal alignment period. +const AutoAlignmentPeriod = "cloud-monitoring-auto" + +// PreprocessorMethod defines the available pre-processing options. +type PreprocessorMethod string + +const ( + PreprocessNone PreprocessorMethod = "none" + PreprocessRate PreprocessorMethod = "rate" + PreprocessDelta PreprocessorMethod = "delta" +) + +// Aligner specifies the operation that will be applied to the data points in +// each alignment period in a time series. +// See https://cloud.google.com/monitoring/api/ref_v3/rest/v3/projects.alertPolicies#Aligner +type Aligner string + +const ( + AlignNone Aligner = "ALIGN_NONE" + AlignDelta Aligner = "ALIGN_DELTA" + AlignRate Aligner = "ALIGN_RATE" + AlignInterpolate Aligner = "ALIGN_INTERPOLATE" + AlignNextOlder Aligner = "ALIGN_NEXT_OLDER" + AlignMin Aligner = "ALIGN_MIN" + AlignMax Aligner = "ALIGN_MAX" + AlignMean Aligner = "ALIGN_MEAN" + AlignCount Aligner = "ALIGN_COUNT" + AlignSum Aligner = "ALIGN_SUM" + AlignStdDev Aligner = "ALIGN_STDDEV" + AlignCountTrue Aligner = "ALIGN_COUNT_TRUE" + AlignCountFalse Aligner = "ALIGN_COUNT_FALSE" + AlignFractionTrue Aligner = "ALIGN_FRACTION_TRUE" + AlignPercentile99 Aligner = "ALIGN_PERCENTILE_99" + AlignPercentile95 Aligner = "ALIGN_PERCENTILE_95" + AlignPercentile50 Aligner = "ALIGN_PERCENTILE_50" + AlignPercentile05 Aligner = "ALIGN_PERCENTILE_05" + AlignPercentChange Aligner = "ALIGN_PERCENT_CHANGE" +) + +// Reducer operation describes how to aggregate data points from multiple time +// series into a single time series, where the value of each data point in the +// resulting series is a function of all the already aligned values in the +// input time series. +// See https://cloud.google.com/monitoring/api/ref_v3/rest/v3/projects.alertPolicies#reducer +type Reducer string + +const ( + ReduceNone Reducer = "REDUCE_NONE" + ReduceMean Reducer = "REDUCE_MEAN" + ReduceMin Reducer = "REDUCE_MIN" + ReduceMax Reducer = "REDUCE_MAX" + ReduceSum Reducer = "REDUCE_SUM" + ReduceStdDev Reducer = "REDUCE_STDDEV" + ReduceCount Reducer = "REDUCE_COUNT" + ReduceCountTrue Reducer = "REDUCE_COUNT_TRUE" + ReduceCountFalse Reducer = "REDUCE_COUNT_FALSE" + ReduceCountFractionTrue Reducer = "REDUCE_FRACTION_TRUE" + ReducePercentile99 Reducer = "REDUCE_PERCENTILE_99" + ReducePercentile95 Reducer = "REDUCE_PERCENTILE_95" + ReducePercentile50 Reducer = "REDUCE_PERCENTILE_50" + ReducePercentile05 Reducer = "REDUCE_PERCENTILE_05" +) + +// FilterOperator describes the set of all possible operations applicable to filters. +type FilterOperator string + +const ( + FilterOperatorEqual FilterOperator = "=" + FilterOperatorNotEqual FilterOperator = "!=" + FilterOperatorMatchesRegexp FilterOperator = "=~" + FilterOperatorNotMatchesRegexp FilterOperator = "!=~" +) + +// Target is an interface regrouping all the different cloudmonitoring targets. +type Target interface { + Target() *sdk.Target +} diff --git a/target/cloudmonitoring/mql.go b/target/cloudmonitoring/mql.go new file mode 100644 index 00000000..d79b3e95 --- /dev/null +++ b/target/cloudmonitoring/mql.go @@ -0,0 +1,47 @@ +package cloudmonitoring + +import "github.com/K-Phoen/sdk" + +const ( + GraphPeriodDisabled = "disabled" + GraphPeriodAuto = "auto" +) + +type MQLOption func(*MQL) + +func MQLAliasBy(alias string) MQLOption { + return func(m *MQL) { + m.target.AliasBy = alias + } +} + +func GraphPeriod(graphPeriod string) MQLOption { + return func(m *MQL) { + m.target.TimeSeriesQuery.GraphPeriod = graphPeriod + } +} + +type MQL struct { + target *sdk.Target +} + +// NewMQL returns a target builder making an MQL query. +func NewMQL(projectName, query string, options ...MQLOption) *MQL { + mql := &MQL{ + target: &sdk.Target{ + QueryType: "timeSeriesQuery", + TimeSeriesQuery: &sdk.StackdriverTimeSeriesQuery{ + ProjectName: projectName, + Query: query, + }, + }, + } + + for _, opt := range options { + opt(mql) + } + + return mql +} + +func (m *MQL) Target() *sdk.Target { return m.target } diff --git a/target/cloudmonitoring/mql_test.go b/target/cloudmonitoring/mql_test.go new file mode 100644 index 00000000..28e9267c --- /dev/null +++ b/target/cloudmonitoring/mql_test.go @@ -0,0 +1,55 @@ +package cloudmonitoring + +import ( + "testing" + + "github.com/K-Phoen/sdk" + "github.com/stretchr/testify/assert" +) + +func TestMQL(t *testing.T) { + for _, testCase := range []struct { + desc string + query string + options []MQLOption + wantTarget *sdk.Target + }{ + { + desc: "default", + query: "project_id=\"blublu\"", + wantTarget: &sdk.Target{ + QueryType: "timeSeriesQuery", + TimeSeriesQuery: &sdk.StackdriverTimeSeriesQuery{ + ProjectName: testProjectName, + Query: "project_id=\"blublu\"", + }, + }, + }, + { + desc: "all options", + query: "project_id=\"blublu\"", + options: []MQLOption{GraphPeriod("120s"), MQLAliasBy("Bozo")}, + wantTarget: &sdk.Target{ + QueryType: "timeSeriesQuery", + AliasBy: "Bozo", + TimeSeriesQuery: &sdk.StackdriverTimeSeriesQuery{ + ProjectName: testProjectName, + Query: "project_id=\"blublu\"", + GraphPeriod: "120s", + }, + }, + }, + } { + t.Run(testCase.desc, func(t *testing.T) { + assert.Equal( + t, + testCase.wantTarget, + NewMQL( + testProjectName, + testCase.query, + testCase.options..., + ).Target(), + ) + }) + } +} diff --git a/target/cloudmonitoring/promql.go b/target/cloudmonitoring/promql.go new file mode 100644 index 00000000..6cbabee3 --- /dev/null +++ b/target/cloudmonitoring/promql.go @@ -0,0 +1,46 @@ +package cloudmonitoring + +import ( + "github.com/K-Phoen/sdk" +) + +// PromQLOption represents an option that can be used to configure a promQL query. +type PromQLOption func(*PromQL) + +func MinStep(step string) PromQLOption { + return func(q *PromQL) { + q.target.PromQLQuery.Step = step + } +} + +// PromQL represents a google cloud monitoring query. +type PromQL struct { + target *sdk.Target +} + +// NewPromQL returns a target builder making a PromQL query. +func NewPromQL(projectName, expr string, options ...PromQLOption) *PromQL { + promQL := &PromQL{ + target: &sdk.Target{ + QueryType: "promQL", + // For some reason I can't explain, Grafana seems to require TimeSeriesQuery to be set + // when we're making a promQL query. + TimeSeriesQuery: &sdk.StackdriverTimeSeriesQuery{ + ProjectName: projectName, + }, + PromQLQuery: &sdk.StackdriverPromQLQuery{ + ProjectName: projectName, + Expr: expr, + Step: "10s", + }, + }, + } + + for _, opt := range options { + opt(promQL) + } + + return promQL +} + +func (p *PromQL) Target() *sdk.Target { return p.target } diff --git a/target/cloudmonitoring/promql_test.go b/target/cloudmonitoring/promql_test.go new file mode 100644 index 00000000..87f42bee --- /dev/null +++ b/target/cloudmonitoring/promql_test.go @@ -0,0 +1,61 @@ +package cloudmonitoring + +import ( + "testing" + + "github.com/K-Phoen/sdk" + "github.com/stretchr/testify/assert" +) + +func TestPromQL(t *testing.T) { + for _, testCase := range []struct { + desc string + expr string + options []PromQLOption + wantTarget *sdk.Target + }{ + { + desc: "default", + expr: "uptime{foo=\"bar\"}", + wantTarget: &sdk.Target{ + QueryType: "promQL", + TimeSeriesQuery: &sdk.StackdriverTimeSeriesQuery{ + ProjectName: testProjectName, + }, + PromQLQuery: &sdk.StackdriverPromQLQuery{ + ProjectName: testProjectName, + Expr: "uptime{foo=\"bar\"}", + Step: "10s", + }, + }, + }, + { + desc: "with min step", + expr: "uptime{foo=\"bar\"}", + options: []PromQLOption{MinStep("120s")}, + wantTarget: &sdk.Target{ + QueryType: "promQL", + TimeSeriesQuery: &sdk.StackdriverTimeSeriesQuery{ + ProjectName: testProjectName, + }, + PromQLQuery: &sdk.StackdriverPromQLQuery{ + ProjectName: testProjectName, + Expr: "uptime{foo=\"bar\"}", + Step: "120s", + }, + }, + }, + } { + t.Run(testCase.desc, func(t *testing.T) { + assert.Equal( + t, + testCase.wantTarget, + NewPromQL( + testProjectName, + testCase.expr, + testCase.options..., + ).Target(), + ) + }) + } +} diff --git a/target/cloudmonitoring/slo.go b/target/cloudmonitoring/slo.go new file mode 100644 index 00000000..1080ed60 --- /dev/null +++ b/target/cloudmonitoring/slo.go @@ -0,0 +1,68 @@ +package cloudmonitoring + +import "github.com/K-Phoen/sdk" + +// SLOOption represents an option that can be used to configure an SLO query. +type SLOOption func(*SLO) + +func SLOPerSeriesAligner(aligner Aligner, alignmentPeriod string) SLOOption { + return func(s *SLO) { + s.target.SLOQuery.PerSeriesAligner = string(aligner) + s.target.SLOQuery.AlignmentPeriod = alignmentPeriod + } +} + +func SLOAliasBy(alias string) SLOOption { + return func(s *SLO) { + s.target.SLOQuery.AliasBy = alias + } +} + +func SelectorName(name string) SLOOption { + return func(s *SLO) { + s.target.SLOQuery.SelectorName = name + } +} + +func ServiceRef(id, name string) SLOOption { + return func(s *SLO) { + s.target.SLOQuery.ServiceID = id + s.target.SLOQuery.ServiceName = name + } +} + +func SLORef(id, name string) SLOOption { + return func(s *SLO) { + s.target.SLOQuery.SLOID = id + s.target.SLOQuery.SLOName = name + } +} + +func LookbackPeriod(period string) SLOOption { + return func(s *SLO) { + s.target.SLOQuery.LookbackPeriod = period + } +} + +type SLO struct { + target *sdk.Target +} + +func NewSLO(projectName string, options ...SLOOption) *SLO { + slo := &SLO{ + target: &sdk.Target{ + QueryType: "slo", + SLOQuery: &sdk.StackdriverSLOQuery{ + ProjectName: projectName, + }, + }, + } + + for _, opt := range options { + opt(slo) + } + + return slo +} + +func (s *SLO) Target() *sdk.Target { return s.target } diff --git a/target/cloudmonitoring/slo_test.go b/target/cloudmonitoring/slo_test.go new file mode 100644 index 00000000..390d448f --- /dev/null +++ b/target/cloudmonitoring/slo_test.go @@ -0,0 +1,63 @@ +package cloudmonitoring + +import ( + "testing" + + "github.com/K-Phoen/sdk" + "github.com/stretchr/testify/assert" +) + +func TestSLO(t *testing.T) { + for _, testCase := range []struct { + desc string + options []SLOOption + wantTarget *sdk.Target + }{ + { + desc: "default", + wantTarget: &sdk.Target{ + QueryType: "slo", + SLOQuery: &sdk.StackdriverSLOQuery{ + ProjectName: testProjectName, + }, + }, + }, + { + desc: "all options", + options: []SLOOption{ + SLOPerSeriesAligner(AlignPercentChange, "10s"), + SLOAliasBy("banana"), + SelectorName("bah"), + ServiceRef("abc", "service"), + SLORef("cde", "slo"), + LookbackPeriod("120m"), + }, + wantTarget: &sdk.Target{ + QueryType: "slo", + SLOQuery: &sdk.StackdriverSLOQuery{ + ProjectName: testProjectName, + AlignmentPeriod: "10s", + PerSeriesAligner: string(AlignPercentChange), + AliasBy: "banana", + SelectorName: "bah", + ServiceID: "abc", + ServiceName: "service", + SLOID: "cde", + SLOName: "slo", + LookbackPeriod: "120m", + }, + }, + }, + } { + t.Run(testCase.desc, func(t *testing.T) { + assert.Equal( + t, + testCase.wantTarget, + NewSLO( + testProjectName, + testCase.options..., + ).Target(), + ) + }) + } +} diff --git a/target/cloudmonitoring/timeseries.go b/target/cloudmonitoring/timeseries.go new file mode 100644 index 00000000..4df12559 --- /dev/null +++ b/target/cloudmonitoring/timeseries.go @@ -0,0 +1,112 @@ +package cloudmonitoring + +import "github.com/K-Phoen/sdk" + +// TimeSeriesOption represents an option that can be used to configure a timeseries query. +type TimeSeriesOption func(target *TimeSeries) + +func CrossSeriesReducer(reducer Reducer) TimeSeriesOption { + return func(target *TimeSeries) { + target.target.TimeSeriesList.CrossSeriesReducer = string(reducer) + } +} + +func PerSeriesAligner(aligner Aligner, alignmentPeriod string) TimeSeriesOption { + return func(target *TimeSeries) { + target.target.TimeSeriesList.PerSeriesAligner = string(aligner) + target.target.TimeSeriesList.AlignmentPeriod = alignmentPeriod + } +} + +func GroupBy(field string) TimeSeriesOption { + return func(target *TimeSeries) { + target.target.TimeSeriesList.GroupBys = append( + target.target.TimeSeriesList.GroupBys, + field, + ) + } +} + +func Filter(field string, op FilterOperator, value string) TimeSeriesOption { + return func(target *TimeSeries) { + target.target.TimeSeriesList.Filters = append( + target.target.TimeSeriesList.Filters, + "AND", field, string(op), value, + ) + } +} + +func View(view string) TimeSeriesOption { + return func(target *TimeSeries) { + target.target.TimeSeriesList.View = view + } +} + +func Title(title string) TimeSeriesOption { + return func(target *TimeSeries) { + target.target.TimeSeriesList.Title = title + } +} + +func SecondaryCrossSeriesReducer(reducer Reducer) TimeSeriesOption { + return func(target *TimeSeries) { + target.target.TimeSeriesList.SecondaryCrossSeriesReducer = string(reducer) + } +} + +func SecondaryPerSeriesAligner(aligner Aligner, alignmentPeriod string) TimeSeriesOption { + return func(target *TimeSeries) { + target.target.TimeSeriesList.SecondaryPerSeriesAligner = string(aligner) + target.target.TimeSeriesList.SecondaryAlignmentPeriod = alignmentPeriod + } +} + +func SecondaryGroupBy(field string) TimeSeriesOption { + return func(target *TimeSeries) { + target.target.TimeSeriesList.SecondaryGroupBys = append( + target.target.TimeSeriesList.SecondaryGroupBys, + field, + ) + } +} + +func Preprocessor(preprocessor PreprocessorMethod) TimeSeriesOption { + return func(target *TimeSeries) { + target.target.TimeSeriesList.Preprocessor = string(preprocessor) + } +} + +func TimeSeriesAliasBy(alias string) TimeSeriesOption { + return func(target *TimeSeries) { + target.target.AliasBy = alias + } +} + +// TimeSeries represents a google cloud monitoring query. +type TimeSeries struct { + target *sdk.Target +} + +// NewTimeSeries returns a target builder making a classic timeseries query. +func NewTimeSeries(projectName, metricType string, options ...TimeSeriesOption) *TimeSeries { + cloudMonitoring := &TimeSeries{ + target: &sdk.Target{ + QueryType: "timeSeriesList", + TimeSeriesList: &sdk.StackdriverTimeSeriesList{ + ProjectName: projectName, + Filters: []string{ + "metric.type", string(FilterOperatorEqual), metricType, + }, + }, + }, + } + + for _, opt := range options { + opt(cloudMonitoring) + } + + return cloudMonitoring +} + +// Target implements the Target interface +func (t *TimeSeries) Target() *sdk.Target { return t.target } diff --git a/target/cloudmonitoring/timeseries_test.go b/target/cloudmonitoring/timeseries_test.go new file mode 100644 index 00000000..0294f4b6 --- /dev/null +++ b/target/cloudmonitoring/timeseries_test.go @@ -0,0 +1,114 @@ +package cloudmonitoring + +import ( + "testing" + + "github.com/K-Phoen/sdk" + "github.com/stretchr/testify/assert" +) + +const ( + testProjectName = "joelafrite" + testMetricType = "pubsub.googleapis.com/subscription/num_undelivered_messages" +) + +func TestNewTimeSeries(t *testing.T) { + for _, testCase := range []struct { + desc string + options []TimeSeriesOption + wantTarget *sdk.Target + }{ + { + desc: "default values", + wantTarget: &sdk.Target{ + QueryType: "timeSeriesList", + TimeSeriesList: &sdk.StackdriverTimeSeriesList{ + ProjectName: testProjectName, + Filters: []string{ + "metric.type", + "=", + testMetricType, + }, + }, + }, + }, + { + desc: "multiple filters", + options: []TimeSeriesOption{ + Filter("potato", FilterOperatorEqual, "patata"), + }, + wantTarget: &sdk.Target{ + QueryType: "timeSeriesList", + TimeSeriesList: &sdk.StackdriverTimeSeriesList{ + ProjectName: testProjectName, + Filters: []string{ + "metric.type", + "=", + testMetricType, + "AND", + "potato", + "=", + "patata", + }, + }, + }, + }, + { + desc: "all options", + options: []TimeSeriesOption{ + CrossSeriesReducer(ReduceCountTrue), + PerSeriesAligner(AlignMax, "12d"), + GroupBy("foo"), + GroupBy("bar"), + GroupBy("biz"), + View("lafritte"), + Title("banana"), + SecondaryCrossSeriesReducer(ReduceCountFalse), + SecondaryPerSeriesAligner(AlignCount, "12s"), + SecondaryGroupBy("far"), + SecondaryGroupBy("fuz"), + SecondaryGroupBy("fiz"), + Preprocessor(PreprocessDelta), + TimeSeriesAliasBy("pwet"), + }, + wantTarget: &sdk.Target{ + QueryType: "timeSeriesList", + AliasBy: "pwet", + TimeSeriesList: &sdk.StackdriverTimeSeriesList{ + ProjectName: testProjectName, + CrossSeriesReducer: string(ReduceCountTrue), + PerSeriesAligner: string(AlignMax), + AlignmentPeriod: "12d", + GroupBys: []string{ + "foo", + "bar", + "biz", + }, + View: "lafritte", + Title: "banana", + SecondaryCrossSeriesReducer: string(ReduceCountFalse), + SecondaryPerSeriesAligner: string(AlignCount), + SecondaryAlignmentPeriod: "12s", + SecondaryGroupBys: []string{ + "far", + "fuz", + "fiz", + }, + Preprocessor: string(PreprocessDelta), + Filters: []string{ + "metric.type", + "=", + testMetricType, + }, + }, + }, + }, + } { + t.Run(testCase.desc, func(t *testing.T) { + assert.Equal(t, + testCase.wantTarget, + NewTimeSeries(testProjectName, testMetricType, testCase.options...).target, + ) + }) + } +} From 6c380e1c97a78d7093917f44b2d020e96753350a Mon Sep 17 00:00:00 2001 From: Julien Levesy Date: Sun, 7 Apr 2024 11:53:39 +0200 Subject: [PATCH 2/4] feat(timeseries): add a new cloudmonitoring target --- timeseries/targets.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/timeseries/targets.go b/timeseries/targets.go index 700f5e63..66663e4a 100644 --- a/timeseries/targets.go +++ b/timeseries/targets.go @@ -1,6 +1,7 @@ package timeseries import ( + "github.com/K-Phoen/grabana/target/cloudmonitoring" "github.com/K-Phoen/grabana/target/graphite" "github.com/K-Phoen/grabana/target/influxdb" "github.com/K-Phoen/grabana/target/loki" @@ -61,6 +62,14 @@ func WithStackdriverTarget(target *stackdriver.Stackdriver) Option { } } +// WithCloudMonitoringTarget adds a cloud monitoring query to the graph. +func WithCloudMonitoringTarget(target cloudmonitoring.Target) Option { + return func(graph *TimeSeries) error { + graph.Builder.AddTarget(target.Target()) + return nil + } +} + // WithLokiTarget adds a loki query to the graph. func WithLokiTarget(query string, options ...loki.Option) Option { target := loki.New(query, options...) From 0721de3cb0c861408589ceaa6cc6e0e35dee5cad Mon Sep 17 00:00:00 2001 From: Julien Levesy Date: Thu, 11 Apr 2024 21:58:15 +0200 Subject: [PATCH 3/4] wip --- alert/queries.go | 18 ++++++++++++++++++ target/cloudmonitoring/cloudmonitoring.go | 5 +++++ target/cloudmonitoring/mql.go | 8 ++++++++ target/cloudmonitoring/promql.go | 7 +++++++ target/cloudmonitoring/slo.go | 6 ++++++ target/cloudmonitoring/timeseries.go | 8 ++++++++ 6 files changed, 52 insertions(+) diff --git a/alert/queries.go b/alert/queries.go index 94235dee..06526abc 100644 --- a/alert/queries.go +++ b/alert/queries.go @@ -6,6 +6,8 @@ import ( "github.com/K-Phoen/grabana/alert/queries/loki" "github.com/K-Phoen/grabana/alert/queries/prometheus" "github.com/K-Phoen/grabana/alert/queries/stackdriver" + "github.com/K-Phoen/grabana/target/cloudmonitoring" + "github.com/K-Phoen/sdk" ) // WithPrometheusQuery adds a prometheus query to the alert. @@ -39,6 +41,22 @@ func WithStackdriverQuery(query *stackdriver.Stackdriver) Option { } } +// WithStackdriverQuery adds a cloud montioring query to the alert. +func WithCloudMonitoringQuery(ref, datasource string, q cloudmonitoring.AlertModel) Option { + return func(alert *Alert) { + alert.Builder.Rules[0].GrafanaAlert.Data = append( + alert.Builder.Rules[0].GrafanaAlert.Data, + sdk.AlertQuery{ + RefID: ref, + QueryType: "", + DatasourceUID: "__FILL_ME__", + RelativeTimeRange: &sdk.AlertRelativeTimeRange{}, + Model: q.AlertModel(), + }, + ) + } +} + // WithInfluxDBQuery adds an InfluxDB query to the alert. func WithInfluxDBQuery(ref string, query string, options ...influxdb.Option) Option { return func(alert *Alert) { diff --git a/target/cloudmonitoring/cloudmonitoring.go b/target/cloudmonitoring/cloudmonitoring.go index 1af97fb1..1c10c743 100644 --- a/target/cloudmonitoring/cloudmonitoring.go +++ b/target/cloudmonitoring/cloudmonitoring.go @@ -79,3 +79,8 @@ const ( type Target interface { Target() *sdk.Target } + +// Target allows to return an alert model for a specific builder. +type AlertModel interface { + AlertModel() sdk.AlertModel +} diff --git a/target/cloudmonitoring/mql.go b/target/cloudmonitoring/mql.go index d79b3e95..521d6dbc 100644 --- a/target/cloudmonitoring/mql.go +++ b/target/cloudmonitoring/mql.go @@ -45,3 +45,11 @@ func NewMQL(projectName, query string, options ...MQLOption) *MQL { } func (m *MQL) Target() *sdk.Target { return m.target } + +// AlertModel implements the AlertModel interface +func (m *MQL) AlertModel() sdk.AlertModel { + return sdk.AlertModel{ + QueryType: m.target.QueryType, + TimeSeriesQuery: m.target.TimeSeriesQuery, + } +} diff --git a/target/cloudmonitoring/promql.go b/target/cloudmonitoring/promql.go index 6cbabee3..863f2c12 100644 --- a/target/cloudmonitoring/promql.go +++ b/target/cloudmonitoring/promql.go @@ -44,3 +44,10 @@ func NewPromQL(projectName, expr string, options ...PromQLOption) *PromQL { } func (p *PromQL) Target() *sdk.Target { return p.target } + +func (p *PromQL) AlertModel() sdk.AlertModel { + return sdk.AlertModel{ + QueryType: p.target.QueryType, + PromQLQuery: p.target.PromQLQuery, + } +} diff --git a/target/cloudmonitoring/slo.go b/target/cloudmonitoring/slo.go index 1080ed60..13cdb431 100644 --- a/target/cloudmonitoring/slo.go +++ b/target/cloudmonitoring/slo.go @@ -66,3 +66,9 @@ func NewSLO(projectName string, options ...SLOOption) *SLO { } func (s *SLO) Target() *sdk.Target { return s.target } +func (s *SLO) AlertModel() sdk.AlertModel { + return sdk.AlertModel{ + QueryType: s.target.QueryType, + SLOQuery: s.target.SLOQuery, + } +} diff --git a/target/cloudmonitoring/timeseries.go b/target/cloudmonitoring/timeseries.go index 4df12559..9380d32d 100644 --- a/target/cloudmonitoring/timeseries.go +++ b/target/cloudmonitoring/timeseries.go @@ -110,3 +110,11 @@ func NewTimeSeries(projectName, metricType string, options ...TimeSeriesOption) // Target implements the Target interface func (t *TimeSeries) Target() *sdk.Target { return t.target } + +// AlertModel implements the AlertModel interface +func (t *TimeSeries) AlertModel() sdk.AlertModel { + return sdk.AlertModel{ + QueryType: t.target.QueryType, + TimeSeriesList: t.target.TimeSeriesList, + } +} From 5ac0a1494a2dd3b2fe5aa588b8a799c275c93cbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Thu, 11 Apr 2024 22:36:40 +0200 Subject: [PATCH 4/4] Use sdk v0.13.0 --- go.mod | 4 ++-- go.sum | 4 ++-- target/cloudmonitoring/mql.go | 2 +- target/cloudmonitoring/promql.go | 4 ++-- target/cloudmonitoring/slo.go | 8 +++++++- target/cloudmonitoring/timeseries.go | 2 +- 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 244f9928..f0119450 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,9 @@ go 1.19 require ( github.com/K-Phoen/jennifer v0.0.0-20230811102814-e6c78cf40086 - github.com/K-Phoen/sdk v0.12.4 + github.com/K-Phoen/sdk v0.13.0 github.com/blang/semver v3.5.1+incompatible + github.com/invopop/jsonschema v0.12.0 github.com/prometheus/common v0.45.0 github.com/rhysd/go-github-selfupdate v1.2.3 github.com/spf13/cobra v1.8.0 @@ -26,7 +27,6 @@ require ( github.com/gosimple/unidecode v1.0.1 // indirect github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/invopop/jsonschema v0.12.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect diff --git a/go.sum b/go.sum index 9927f696..a8264465 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/K-Phoen/jennifer v0.0.0-20230811102814-e6c78cf40086 h1:cvgm5R+2OIaCzMqyA8YAHuybHEbdvBIC3OAziNiMbEU= github.com/K-Phoen/jennifer v0.0.0-20230811102814-e6c78cf40086/go.mod h1:rm3gx5yYxh/Q3ynk+qaNoN6nQiII0Vn/uz46bIgj0P0= -github.com/K-Phoen/sdk v0.12.4 h1:j2EYuBJm3zDTD0fGKACVFWxAXtkR0q5QzfVqxmHSeGQ= -github.com/K-Phoen/sdk v0.12.4/go.mod h1:qmM0wO23CtoDux528MXPpYvS4XkRWkWX6rvX9Za8EVU= +github.com/K-Phoen/sdk v0.13.0 h1:eMJWVekp0iFBJO5dCyKHWxMDow4EFSy2DURy46+b/sk= +github.com/K-Phoen/sdk v0.13.0/go.mod h1:qmM0wO23CtoDux528MXPpYvS4XkRWkWX6rvX9Za8EVU= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= diff --git a/target/cloudmonitoring/mql.go b/target/cloudmonitoring/mql.go index 521d6dbc..c708969a 100644 --- a/target/cloudmonitoring/mql.go +++ b/target/cloudmonitoring/mql.go @@ -30,7 +30,7 @@ func NewMQL(projectName, query string, options ...MQLOption) *MQL { mql := &MQL{ target: &sdk.Target{ QueryType: "timeSeriesQuery", - TimeSeriesQuery: &sdk.StackdriverTimeSeriesQuery{ + TimeSeriesQuery: &sdk.GCMTimeSeriesQuery{ ProjectName: projectName, Query: query, }, diff --git a/target/cloudmonitoring/promql.go b/target/cloudmonitoring/promql.go index 863f2c12..316795ee 100644 --- a/target/cloudmonitoring/promql.go +++ b/target/cloudmonitoring/promql.go @@ -25,10 +25,10 @@ func NewPromQL(projectName, expr string, options ...PromQLOption) *PromQL { QueryType: "promQL", // For some reason I can't explain, Grafana seems to require TimeSeriesQuery to be set // when we're making a promQL query. - TimeSeriesQuery: &sdk.StackdriverTimeSeriesQuery{ + TimeSeriesQuery: &sdk.GCMTimeSeriesQuery{ ProjectName: projectName, }, - PromQLQuery: &sdk.StackdriverPromQLQuery{ + PromQLQuery: &sdk.GCMPromQLQuery{ ProjectName: projectName, Expr: expr, Step: "10s", diff --git a/target/cloudmonitoring/slo.go b/target/cloudmonitoring/slo.go index 13cdb431..4dfa6dd0 100644 --- a/target/cloudmonitoring/slo.go +++ b/target/cloudmonitoring/slo.go @@ -38,6 +38,12 @@ func SLORef(id, name string) SLOOption { } } +func Goal(goal float64) SLOOption { + return func(s *SLO) { + s.target.SLOQuery.Goal = &goal + } +} + func LookbackPeriod(period string) SLOOption { return func(s *SLO) { s.target.SLOQuery.LookbackPeriod = period @@ -52,7 +58,7 @@ func NewSLO(projectName string, options ...SLOOption) *SLO { slo := &SLO{ target: &sdk.Target{ QueryType: "slo", - SLOQuery: &sdk.StackdriverSLOQuery{ + SLOQuery: &sdk.GCMSLOQuery{ ProjectName: projectName, }, }, diff --git a/target/cloudmonitoring/timeseries.go b/target/cloudmonitoring/timeseries.go index 9380d32d..71c02357 100644 --- a/target/cloudmonitoring/timeseries.go +++ b/target/cloudmonitoring/timeseries.go @@ -92,7 +92,7 @@ func NewTimeSeries(projectName, metricType string, options ...TimeSeriesOption) cloudMonitoring := &TimeSeries{ target: &sdk.Target{ QueryType: "timeSeriesList", - TimeSeriesList: &sdk.StackdriverTimeSeriesList{ + TimeSeriesList: &sdk.GCMTimeSeriesList{ ProjectName: projectName, Filters: []string{ "metric.type", string(FilterOperatorEqual), metricType,