From 5cfb63b975c02da7e0f292066cc800edd36c4f61 Mon Sep 17 00:00:00 2001 From: dawidwisn Date: Fri, 7 Jun 2024 13:40:08 +0200 Subject: [PATCH] PC-13028 Support direct in AzurePrometheus --- manifest/v1alpha/direct/direct.go | 12 ++++ manifest/v1alpha/direct/validation.go | 23 +++++++ manifest/v1alpha/direct/validation_test.go | 77 ++++++++++++++++++++++ 3 files changed, 112 insertions(+) diff --git a/manifest/v1alpha/direct/direct.go b/manifest/v1alpha/direct/direct.go index 55fb9d7d..d4357264 100644 --- a/manifest/v1alpha/direct/direct.go +++ b/manifest/v1alpha/direct/direct.go @@ -60,6 +60,7 @@ type Spec struct { AzureMonitor *AzureMonitorConfig `json:"azureMonitor,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 @@ -93,6 +94,7 @@ var validDirectTypes = map[v1alpha.DataSourceType]struct{}{ v1alpha.AzureMonitor: {}, v1alpha.Honeycomb: {}, v1alpha.LogicMonitor: {}, + v1alpha.AzurePrometheus: {}, } func IsValidDirectType(directType v1alpha.DataSourceType) bool { @@ -140,6 +142,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("BUG: unknown direct type") } @@ -268,3 +272,11 @@ type LogicMonitorConfig struct { AccessID string `json:"accessId"` AccessKey string `json:"accessKey"` } + +// AzurePrometheusConfig represents content of Azure Monitor managed service for Prometheus typical for Direct Object. +type AzurePrometheusConfig struct { + URL string `json:"url"` + TenantID string `json:"tenantId"` + ClientID string `json:"clientId"` + ClientSecret string `json:"clientSecret"` +} diff --git a/manifest/v1alpha/direct/validation.go b/manifest/v1alpha/direct/validation.go index bd7b770e..7b9bb968 100644 --- a/manifest/v1alpha/direct/validation.go +++ b/manifest/v1alpha/direct/validation.go @@ -101,6 +101,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 ( @@ -214,6 +217,21 @@ var ( Required(). Rules(validation.StringNotEmpty()), ) + azurePrometheusValidation = validation.New[AzurePrometheusConfig]( + urlPropertyRules(func(s AzurePrometheusConfig) string { return s.URL }), + validation.For(func(a AzurePrometheusConfig) string { return a.TenantID }). + WithName("tenantId"). + Required(). + Rules(validation.StringUUID()), + validation.For(func(l AzurePrometheusConfig) string { return l.ClientID }). + WithName("clientId"). + Required(). + Rules(validation.StringNotEmpty()), + validation.For(func(l AzurePrometheusConfig) string { return l.ClientSecret }). + WithName("clientSecret"). + Required(). + Rules(validation.StringNotEmpty()), + ) ) const ( @@ -329,6 +347,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") } diff --git a/manifest/v1alpha/direct/validation_test.go b/manifest/v1alpha/direct/validation_test.go index ae4445b7..1b916436 100644 --- a/manifest/v1alpha/direct/validation_test.go +++ b/manifest/v1alpha/direct/validation_test.go @@ -943,6 +943,75 @@ func TestValidateSpec_LogicMonitor(t *testing.T) { }) } +func TestValidateSpec_AzurePrometheus(t *testing.T) { + t.Run("passes", func(t *testing.T) { + direct := validDirect(v1alpha.AzurePrometheus) + err := validate(direct) + testutils.AssertNoError(t, direct, err) + }) + t.Run("invalid tenantId", func(t *testing.T) { + direct := validDirect(v1alpha.AzurePrometheus) + direct.Spec.AzurePrometheus.TenantID = "invalid" + err := validate(direct) + testutils.AssertContainsErrors(t, direct, err, 1, testutils.ExpectedError{ + Prop: "spec.azurePrometheus.tenantId", + Code: validation.ErrorCodeStringUUID, + }) + }) + t.Run("required fields", func(t *testing.T) { + direct := validDirect(v1alpha.AzurePrometheus) + direct.Spec.AzurePrometheus.URL = "" + direct.Spec.AzurePrometheus.TenantID = "" + direct.Spec.AzurePrometheus.ClientID = "" + direct.Spec.AzurePrometheus.ClientSecret = "" + err := validate(direct) + testutils.AssertContainsErrors(t, direct, err, 4, + testutils.ExpectedError{ + Prop: "spec.azurePrometheus.url", + Code: validation.ErrorCodeRequired, + }, + testutils.ExpectedError{ + Prop: "spec.azurePrometheus.tenantId", + Code: validation.ErrorCodeRequired, + }, + testutils.ExpectedError{ + Prop: "spec.azurePrometheus.clientId", + Code: validation.ErrorCodeRequired, + }, + testutils.ExpectedError{ + Prop: "spec.azurePrometheus.clientSecret", + Code: validation.ErrorCodeRequired, + }, + ) + }) + t.Run("invalid fields", func(t *testing.T) { + direct := validDirect(v1alpha.AzurePrometheus) + direct.Spec.AzurePrometheus.URL = "invalid" + direct.Spec.AzurePrometheus.TenantID = strings.Repeat("l", 256) + err := validate(direct) + testutils.AssertContainsErrors(t, direct, err, 2, + testutils.ExpectedError{ + Prop: "spec.azurePrometheus.url", + Code: validation.ErrorCodeURL, + }, + testutils.ExpectedError{ + Prop: "spec.azurePrometheus.tenantId", + Code: validation.ErrorCodeStringUUID, + }, + ) + }) + + t.Run("url must be https", func(t *testing.T) { + direct := validDirect(v1alpha.AzurePrometheus) + direct.Spec.AzurePrometheus.URL = "http://nobl9.com" + err := validate(direct) + testutils.AssertContainsErrors(t, direct, err, 1, testutils.ExpectedError{ + Prop: "spec.azurePrometheus.url", + Code: errorCodeHTTPSSchemeRequired, + }) + }) +} + func validDirect(typ v1alpha.DataSourceType) Direct { spec := validDirectSpec(typ) spec.Description = fmt.Sprintf("Example %s direct", typ) @@ -1070,6 +1139,14 @@ func validDirectSpec(typ v1alpha.DataSourceType) Spec { AccessKey: "secret", }, }, + v1alpha.AzurePrometheus: { + AzurePrometheus: &AzurePrometheusConfig{ + URL: "https://prometheus-service.monitoring:8080", + TenantID: "abf988bf-86f1-41af-91ab-2d7cd011db46", + ClientID: "secret", + ClientSecret: "secret", + }, + }, } return specs[typ]