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

feat: PC-13027 PC-13029 Add Azure Monitor managed service for Prometh… #437

Merged
merged 12 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
4 changes: 4 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5il
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/lestrrat-go/jwx v1.2.28 h1:uadI6o0WpOVrBSf498tRXZIwPpEtLnR9CvqPFXeI5sA=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
Expand All @@ -22,6 +24,8 @@ golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
9 changes: 9 additions & 0 deletions manifest/v1alpha/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ type Spec struct {
Generic *GenericConfig `json:"generic,omitempty"`
Honeycomb *HoneycombConfig `json:"honeycomb,omitempty"`
LogicMonitor *LogicMonitorConfig `json:"logicMonitor,omitempty"`
AzurePrometheus *AzurePrometheusConfig `json:"azurePrometheus,omitempty"`
HistoricalDataRetrieval *v1alpha.HistoricalDataRetrieval `json:"historicalDataRetrieval,omitempty"`
QueryDelay *v1alpha.QueryDelay `json:"queryDelay,omitempty"`
// Interval, Timeout and Jitter are readonly and cannot be set via API
Expand Down Expand Up @@ -139,6 +140,8 @@ func (spec Spec) GetType() (v1alpha.DataSourceType, error) {
return v1alpha.Honeycomb, nil
case spec.LogicMonitor != nil:
return v1alpha.LogicMonitor, nil
case spec.AzurePrometheus != nil:
return v1alpha.AzurePrometheus, nil
}
return 0, errors.New("unknown agent type")
}
Expand Down Expand Up @@ -258,3 +261,9 @@ type HoneycombConfig struct{}
type LogicMonitorConfig struct {
Account string `json:"account"`
}

// AzurePrometheusConfig represents content of Azure Monitor managed service for Prometheus typical for Agent Object.
type AzurePrometheusConfig struct {
URL string `json:"url"`
TenantID string `json:"tenantId"`
}
18 changes: 18 additions & 0 deletions manifest/v1alpha/agent/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ var specValidation = validation.New[Spec](
validation.ForPointer(func(s Spec) *LogicMonitorConfig { return s.LogicMonitor }).
WithName("logicMonitor").
Include(logicMonitorValidation),
validation.ForPointer(func(s Spec) *AzurePrometheusConfig { return s.AzurePrometheus }).
WithName("azurePrometheus").
Include(azurePrometheusValidation),
)

var (
Expand Down Expand Up @@ -201,6 +204,16 @@ var (
Required().
Rules(validation.StringNotEmpty()),
)
azurePrometheusValidation = validation.New[AzurePrometheusConfig](
validation.For(func(a AzurePrometheusConfig) string { return a.URL }).
WithName("url").
Required().
Rules(validation.StringURL()),
validation.For(func(a AzurePrometheusConfig) string { return a.TenantID }).
WithName("tenantId").
Required().
Rules(validation.StringUUID()),
)
// URL only.
prometheusValidation = newURLValidator(func(p PrometheusConfig) string { return p.URL })
appDynamicsValidation = newURLValidator(func(a AppDynamicsConfig) string { return a.URL })
Expand Down Expand Up @@ -371,6 +384,11 @@ var exactlyOneDataSourceTypeValidationRule = validation.NewSingleRule(func(spec
return err
}
}
if spec.AzurePrometheus != nil {
if err := typesMatch(v1alpha.AzurePrometheus); err != nil {
return err
}
}
if onlyType == 0 {
return errors.New("must have exactly one data source type, none were provided")
}
Expand Down
72 changes: 72 additions & 0 deletions manifest/v1alpha/agent/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,72 @@ func TestValidateSpec_AzureMonitor(t *testing.T) {
})
}

func TestValidateSpec_LogicMonitor(t *testing.T) {
t.Run("passes", func(t *testing.T) {
agent := validAgent(v1alpha.LogicMonitor)
err := validate(agent)
testutils.AssertNoError(t, agent, err)
})
t.Run("required account", func(t *testing.T) {
agent := validAgent(v1alpha.LogicMonitor)
agent.Spec.LogicMonitor.Account = ""
err := validate(agent)
testutils.AssertContainsErrors(t, agent, err, 1, testutils.ExpectedError{
Prop: "spec.logicMonitor.account",
Code: validation.ErrorCodeRequired,
})
})
}

func TestValidateSpec_AzurePrometheus(t *testing.T) {
t.Run("passes", func(t *testing.T) {
agent := validAgent(v1alpha.AzurePrometheus)
err := validate(agent)
testutils.AssertNoError(t, agent, err)
})
t.Run("invalid tenantId", func(t *testing.T) {
agent := validAgent(v1alpha.AzurePrometheus)
agent.Spec.AzurePrometheus.TenantID = "invalid"
err := validate(agent)
testutils.AssertContainsErrors(t, agent, err, 1, testutils.ExpectedError{
Prop: "spec.azurePrometheus.tenantId",
Code: validation.ErrorCodeStringUUID,
})
})
t.Run("required fields", func(t *testing.T) {
agent := validAgent(v1alpha.AzurePrometheus)
agent.Spec.AzurePrometheus.URL = ""
agent.Spec.AzurePrometheus.TenantID = ""
err := validate(agent)
testutils.AssertContainsErrors(t, agent, err, 2,
testutils.ExpectedError{
Prop: "spec.azurePrometheus.url",
Code: validation.ErrorCodeRequired,
},
testutils.ExpectedError{
Prop: "spec.azurePrometheus.tenantId",
Code: validation.ErrorCodeRequired,
},
)
})
t.Run("invalid fields", func(t *testing.T) {
agent := validAgent(v1alpha.AzurePrometheus)
agent.Spec.AzurePrometheus.URL = "invalid"
agent.Spec.AzurePrometheus.TenantID = strings.Repeat("l", 256)
err := validate(agent)
testutils.AssertContainsErrors(t, agent, err, 2,
testutils.ExpectedError{
Prop: "spec.azurePrometheus.url",
Code: validation.ErrorCodeStringURL,
},
testutils.ExpectedError{
Prop: "spec.azurePrometheus.tenantId",
Code: validation.ErrorCodeStringUUID,
},
)
})
}

func validAgent(typ v1alpha.DataSourceType) Agent {
spec := validAgentSpec(typ)
spec.Description = fmt.Sprintf("Example %s Agent", typ)
Expand Down Expand Up @@ -874,6 +940,12 @@ func validAgentSpec(typ v1alpha.DataSourceType) Spec {
Account: "account",
},
},
v1alpha.AzurePrometheus: {
AzurePrometheus: &AzurePrometheusConfig{
URL: "https://prometheus-service.monitoring:8080",
TenantID: "e190c630-8873-11ee-b9d1-0242ac120002",
},
},
}

return specs[typ]
Expand Down
6 changes: 6 additions & 0 deletions manifest/v1alpha/data_sources.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ AzureMonitor
Generic
Honeycomb
LogicMonitor
AzurePrometheus
)*/
type DataSourceType int

Expand Down Expand Up @@ -321,6 +322,7 @@ var agentDataRetrievalMaxDuration = map[DataSourceType]HistoricalRetrievalDurati
AzureMonitor: {Value: ptr(30), Unit: HRDDay},
Honeycomb: {Value: ptr(7), Unit: HRDDay},
GoogleCloudMonitoring: {Value: ptr(30), Unit: HRDDay},
AzurePrometheus: {Value: ptr(30), Unit: HRDDay},
}

var directDataRetrievalMaxDuration = map[DataSourceType]HistoricalRetrievalDuration{
Expand Down Expand Up @@ -469,6 +471,10 @@ func GetQueryDelayDefaults() QueryDelayDefaults {
Value: ptr(2),
Unit: Minute,
},
AzurePrometheus: {
Value: ptr(0),
Unit: Second,
},
}
}

Expand Down
7 changes: 6 additions & 1 deletion manifest/v1alpha/data_sources_enum.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions manifest/v1alpha/slo/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type MetricSpec struct {
Generic *GenericMetric `json:"generic,omitempty"`
Honeycomb *HoneycombMetric `json:"honeycomb,omitempty"`
LogicMonitor *LogicMonitorMetric `json:"logicMonitor,omitempty"`
AzurePrometheus *AzurePrometheusMetric `json:"azurePrometheus,omitempty"`
}

func (s *Spec) containsIndicatorRawMetric() bool {
Expand Down Expand Up @@ -265,6 +266,8 @@ func (m *MetricSpec) DataSourceType() v1alpha.DataSourceType {
return v1alpha.Honeycomb
case m.LogicMonitor != nil:
return v1alpha.LogicMonitor
case m.AzurePrometheus != nil:
return v1alpha.AzurePrometheus
default:
return 0
}
Expand Down Expand Up @@ -349,6 +352,8 @@ func (m *MetricSpec) Query() interface{} {
return m.Honeycomb
case v1alpha.LogicMonitor:
return m.LogicMonitor
case v1alpha.AzurePrometheus:
return m.AzurePrometheus
default:
return nil
}
Expand Down
15 changes: 15 additions & 0 deletions manifest/v1alpha/slo/metrics_azure_prometheus.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package slo

import "github.com/nobl9/nobl9-go/internal/validation"

// AzurePrometheusMetric represents metric from Azure Monitor managed service for Prometheus
type AzurePrometheusMetric struct {
PromQL *string `json:"promql"`
dawidwisn marked this conversation as resolved.
Show resolved Hide resolved
}

var azurePrometheusValidation = validation.New[AzurePrometheusMetric](
validation.ForPointer(func(p AzurePrometheusMetric) *string { return p.PromQL }).
WithName("promql").
Required().
Rules(validation.StringNotEmpty()),
)
35 changes: 35 additions & 0 deletions manifest/v1alpha/slo/metrics_azure_prometheus_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package slo

import (
"testing"

"github.com/nobl9/nobl9-go/internal/testutils"
"github.com/nobl9/nobl9-go/internal/validation"
"github.com/nobl9/nobl9-go/manifest/v1alpha"
)

func TestAzurePrometheus(t *testing.T) {
t.Run("passes", func(t *testing.T) {
slo := validRawMetricSLO(v1alpha.AzurePrometheus)
err := validate(slo)
testutils.AssertNoError(t, slo, err)
})
t.Run("required", func(t *testing.T) {
slo := validRawMetricSLO(v1alpha.AzurePrometheus)
slo.Spec.Objectives[0].RawMetric.MetricQuery.AzurePrometheus.PromQL = nil
err := validate(slo)
testutils.AssertContainsErrors(t, slo, err, 1, testutils.ExpectedError{
Prop: "spec.objectives[0].rawMetric.query.azurePrometheus.promql",
Code: validation.ErrorCodeRequired,
})
})
t.Run("empty", func(t *testing.T) {
slo := validRawMetricSLO(v1alpha.AzurePrometheus)
slo.Spec.Objectives[0].RawMetric.MetricQuery.AzurePrometheus.PromQL = ptr("")
err := validate(slo)
testutils.AssertContainsErrors(t, slo, err, 1, testutils.ExpectedError{
Prop: "spec.objectives[0].rawMetric.query.azurePrometheus.promql",
Code: validation.ErrorCodeStringNotEmpty,
})
})
}
32 changes: 29 additions & 3 deletions manifest/v1alpha/slo/metrics_logic_monitor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,35 @@ func TestLogicMonitor(t *testing.T) {
testutils.AssertNoError(t, slo, err)
})
t.Run("required", func(t *testing.T) {
slo := validRawMetricSLO(v1alpha.LogicMonitor)
slo.Spec.Objectives[0].RawMetric.MetricQuery.LogicMonitor = &LogicMonitorMetric{}
err := validate(slo)
testutils.AssertContainsErrors(t, slo, err, 4,
testutils.ExpectedError{
Prop: "spec.objectives[0].rawMetric.query.logicMonitor.queryType",
Code: validation.ErrorCodeRequired,
},
testutils.ExpectedError{
Prop: "spec.objectives[0].rawMetric.query.logicMonitor.deviceDataSourceInstanceId",
Code: validation.ErrorCodeRequired,
},
testutils.ExpectedError{
Prop: "spec.objectives[0].rawMetric.query.logicMonitor.graphId",
Code: validation.ErrorCodeRequired,
},
testutils.ExpectedError{
Prop: "spec.objectives[0].rawMetric.query.logicMonitor.line",
Code: validation.ErrorCodeRequired,
},
)
})
t.Run("invalid fields", func(t *testing.T) {
slo := validRawMetricSLO(v1alpha.LogicMonitor)
slo.Spec.Objectives[0].RawMetric.MetricQuery.LogicMonitor = &LogicMonitorMetric{
QueryType: "wrongQueryType",
QueryType: "wrong-type",
DeviceDataSourceInstanceID: -1,
GraphID: -1,
Line: "",
}
err := validate(slo)
testutils.AssertContainsErrors(t, slo, err, 4,
Expand All @@ -27,11 +53,11 @@ func TestLogicMonitor(t *testing.T) {
},
testutils.ExpectedError{
Prop: "spec.objectives[0].rawMetric.query.logicMonitor.deviceDataSourceInstanceId",
Code: validation.ErrorCodeRequired,
Code: validation.ErrorCodeGreaterThanOrEqualTo,
},
testutils.ExpectedError{
Prop: "spec.objectives[0].rawMetric.query.logicMonitor.graphId",
Code: validation.ErrorCodeRequired,
Code: validation.ErrorCodeGreaterThanOrEqualTo,
},
testutils.ExpectedError{
Prop: "spec.objectives[0].rawMetric.query.logicMonitor.line",
Expand Down
9 changes: 9 additions & 0 deletions manifest/v1alpha/slo/metrics_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ var metricSpecValidation = validation.New[MetricSpec](
validation.ForPointer(func(m MetricSpec) *LogicMonitorMetric { return m.LogicMonitor }).
WithName("logicMonitor").
Include(logicMonitorValidation),
validation.ForPointer(func(m MetricSpec) *AzurePrometheusMetric { return m.AzurePrometheus }).
WithName("azurePrometheus").
Include(azurePrometheusValidation),
)

var badOverTotalEnabledSources = []v1alpha.DataSourceType{
Expand All @@ -187,6 +190,7 @@ var badOverTotalEnabledSources = []v1alpha.DataSourceType{
v1alpha.AzureMonitor,
v1alpha.Honeycomb,
v1alpha.LogicMonitor,
v1alpha.AzurePrometheus,
}

// Support for bad/total metrics will be enabled gradually.
Expand Down Expand Up @@ -353,6 +357,11 @@ func validateExactlyOneMetricSpecType(metrics ...*MetricSpec) error {
return err
}
}
if metric.AzurePrometheus != nil {
if err := typesMatch(v1alpha.AzurePrometheus); err != nil {
return err
}
}
}
if onlyType == 0 {
return errors.New("must have exactly one metric spec type, none were provided")
Expand Down
Loading
Loading