Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a new cloudmonitoring target #259

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions alert/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"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"

Check failure on line 9 in alert/queries.go

View workflow job for this annotation

GitHub Actions / Tests (1.20)

could not import github.com/K-Phoen/grabana/target/cloudmonitoring (-: # github.com/K-Phoen/grabana/target/cloudmonitoring

Check failure on line 9 in alert/queries.go

View workflow job for this annotation

GitHub Actions / Tests (1.21)

could not import github.com/K-Phoen/grabana/target/cloudmonitoring (-: # github.com/K-Phoen/grabana/target/cloudmonitoring
"github.com/K-Phoen/sdk"
)

// WithPrometheusQuery adds a prometheus query to the alert.
Expand Down Expand Up @@ -39,6 +41,22 @@
}
}

// 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) {
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -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=
Expand Down
86 changes: 86 additions & 0 deletions target/cloudmonitoring/cloudmonitoring.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
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
}

// Target allows to return an alert model for a specific builder.
type AlertModel interface {
AlertModel() sdk.AlertModel
}
55 changes: 55 additions & 0 deletions target/cloudmonitoring/mql.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
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.GCMTimeSeriesQuery{
ProjectName: projectName,
Query: query,
},
},
}

for _, opt := range options {
opt(mql)
}

return 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,

Check failure on line 53 in target/cloudmonitoring/mql.go

View workflow job for this annotation

GitHub Actions / Tests (1.20)

unknown field TimeSeriesQuery in struct literal of type sdk.AlertModel

Check failure on line 53 in target/cloudmonitoring/mql.go

View workflow job for this annotation

GitHub Actions / Tests (1.20)

unknown field TimeSeriesQuery in struct literal of type sdk.AlertModel

Check failure on line 53 in target/cloudmonitoring/mql.go

View workflow job for this annotation

GitHub Actions / Tests (1.20)

unknown field TimeSeriesQuery in struct literal of type sdk.AlertModel

Check failure on line 53 in target/cloudmonitoring/mql.go

View workflow job for this annotation

GitHub Actions / Tests (1.21)

unknown field TimeSeriesQuery in struct literal of type sdk.AlertModel

Check failure on line 53 in target/cloudmonitoring/mql.go

View workflow job for this annotation

GitHub Actions / Tests (1.21)

unknown field TimeSeriesQuery in struct literal of type sdk.AlertModel

Check failure on line 53 in target/cloudmonitoring/mql.go

View workflow job for this annotation

GitHub Actions / Tests (1.21)

unknown field TimeSeriesQuery in struct literal of type sdk.AlertModel
}
}
55 changes: 55 additions & 0 deletions target/cloudmonitoring/mql_test.go
Original file line number Diff line number Diff line change
@@ -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{

Check failure on line 22 in target/cloudmonitoring/mql_test.go

View workflow job for this annotation

GitHub Actions / Tests (1.20)

undefined: sdk.StackdriverTimeSeriesQuery

Check failure on line 22 in target/cloudmonitoring/mql_test.go

View workflow job for this annotation

GitHub Actions / Tests (1.21)

undefined: 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(),
)
})
}
}
53 changes: 53 additions & 0 deletions target/cloudmonitoring/promql.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
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.GCMTimeSeriesQuery{
ProjectName: projectName,
},
PromQLQuery: &sdk.GCMPromQLQuery{
ProjectName: projectName,
Expr: expr,
Step: "10s",
},
},
}

for _, opt := range options {
opt(promQL)
}

return 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,

Check failure on line 51 in target/cloudmonitoring/promql.go

View workflow job for this annotation

GitHub Actions / Tests (1.20)

unknown field PromQLQuery in struct literal of type sdk.AlertModel

Check failure on line 51 in target/cloudmonitoring/promql.go

View workflow job for this annotation

GitHub Actions / Tests (1.20)

unknown field PromQLQuery in struct literal of type sdk.AlertModel

Check failure on line 51 in target/cloudmonitoring/promql.go

View workflow job for this annotation

GitHub Actions / Tests (1.20)

unknown field PromQLQuery in struct literal of type sdk.AlertModel

Check failure on line 51 in target/cloudmonitoring/promql.go

View workflow job for this annotation

GitHub Actions / Tests (1.21)

unknown field PromQLQuery in struct literal of type sdk.AlertModel

Check failure on line 51 in target/cloudmonitoring/promql.go

View workflow job for this annotation

GitHub Actions / Tests (1.21)

unknown field PromQLQuery in struct literal of type sdk.AlertModel

Check failure on line 51 in target/cloudmonitoring/promql.go

View workflow job for this annotation

GitHub Actions / Tests (1.21)

unknown field PromQLQuery in struct literal of type sdk.AlertModel
}
}
61 changes: 61 additions & 0 deletions target/cloudmonitoring/promql_test.go
Original file line number Diff line number Diff line change
@@ -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(),
)
})
}
}
Loading
Loading