From 4bf666a7cf6241e7df49aa01d9dafc6e1c708b8b Mon Sep 17 00:00:00 2001 From: Mateusz Hawrus <48822818+nieomylnieja@users.noreply.github.com> Date: Mon, 1 Jul 2024 15:23:51 +0200 Subject: [PATCH] feat: Add Alert Method types (#475) ## Motivation Similar to `Agent` or `Direct`, `AlertMethod` has different types based on its `spec` contents. Each type corresponds to the method's integration provider. It would be beneficial to be able to quickly tell what type of integration does given method support, just like `Agent.Spec.GetType`. ## Release Notes Added `AlertMethod.Spec.GetType()` function and `v1alpha.AlertMethodType` enum to help discern different types of alert methods. --- .github/workflows/checks.yml | 4 +- manifest/v1alpha/agent/agent.go | 56 +++++----- manifest/v1alpha/alert_methods.go | 18 ++++ manifest/v1alpha/alert_methods_enum.go | 100 ++++++++++++++++++ manifest/v1alpha/alertmethod/alert_method.go | 27 +++++ .../v1alpha/alertmethod/alert_method_test.go | 35 ++++++ 6 files changed, 210 insertions(+), 30 deletions(-) create mode 100644 manifest/v1alpha/alert_methods.go create mode 100644 manifest/v1alpha/alert_methods_enum.go create mode 100644 manifest/v1alpha/alertmethod/alert_method_test.go diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 348eb139..dbd9634d 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -32,10 +32,10 @@ jobs: ${{ runner.os }}-yarn- - name: Run spell and markdown checkers run: make check/spell check/trailing check/markdown - - name: Check generated code - run: make check/generate - name: Check formatting run: make check/format + - name: Check generated code + run: make check/generate - name: Run go vet run: make check/vet - name: Run golangci-lint diff --git a/manifest/v1alpha/agent/agent.go b/manifest/v1alpha/agent/agent.go index ecc04568..f3065c2e 100644 --- a/manifest/v1alpha/agent/agent.go +++ b/manifest/v1alpha/agent/agent.go @@ -86,61 +86,61 @@ type Status struct { NewestBetaAgentVersion string `json:"newestBetaAgentVersion,omitempty"` } -func (spec Spec) GetType() (v1alpha.DataSourceType, error) { +func (s Spec) GetType() (v1alpha.DataSourceType, error) { switch { - case spec.Prometheus != nil: + case s.Prometheus != nil: return v1alpha.Prometheus, nil - case spec.Datadog != nil: + case s.Datadog != nil: return v1alpha.Datadog, nil - case spec.NewRelic != nil: + case s.NewRelic != nil: return v1alpha.NewRelic, nil - case spec.AppDynamics != nil: + case s.AppDynamics != nil: return v1alpha.AppDynamics, nil - case spec.Splunk != nil: + case s.Splunk != nil: return v1alpha.Splunk, nil - case spec.Lightstep != nil: + case s.Lightstep != nil: return v1alpha.Lightstep, nil - case spec.SplunkObservability != nil: + case s.SplunkObservability != nil: return v1alpha.SplunkObservability, nil - case spec.Dynatrace != nil: + case s.Dynatrace != nil: return v1alpha.Dynatrace, nil - case spec.Elasticsearch != nil: + case s.Elasticsearch != nil: return v1alpha.Elasticsearch, nil - case spec.ThousandEyes != nil: + case s.ThousandEyes != nil: return v1alpha.ThousandEyes, nil - case spec.Graphite != nil: + case s.Graphite != nil: return v1alpha.Graphite, nil - case spec.BigQuery != nil: + case s.BigQuery != nil: return v1alpha.BigQuery, nil - case spec.OpenTSDB != nil: + case s.OpenTSDB != nil: return v1alpha.OpenTSDB, nil - case spec.GrafanaLoki != nil: + case s.GrafanaLoki != nil: return v1alpha.GrafanaLoki, nil - case spec.CloudWatch != nil: + case s.CloudWatch != nil: return v1alpha.CloudWatch, nil - case spec.Pingdom != nil: + case s.Pingdom != nil: return v1alpha.Pingdom, nil - case spec.AmazonPrometheus != nil: + case s.AmazonPrometheus != nil: return v1alpha.AmazonPrometheus, nil - case spec.Redshift != nil: + case s.Redshift != nil: return v1alpha.Redshift, nil - case spec.SumoLogic != nil: + case s.SumoLogic != nil: return v1alpha.SumoLogic, nil - case spec.Instana != nil: + case s.Instana != nil: return v1alpha.Instana, nil - case spec.InfluxDB != nil: + case s.InfluxDB != nil: return v1alpha.InfluxDB, nil - case spec.GCM != nil: + case s.GCM != nil: return v1alpha.GCM, nil - case spec.AzureMonitor != nil: + case s.AzureMonitor != nil: return v1alpha.AzureMonitor, nil - case spec.Generic != nil: + case s.Generic != nil: return v1alpha.Generic, nil - case spec.Honeycomb != nil: + case s.Honeycomb != nil: return v1alpha.Honeycomb, nil - case spec.LogicMonitor != nil: + case s.LogicMonitor != nil: return v1alpha.LogicMonitor, nil - case spec.AzurePrometheus != nil: + case s.AzurePrometheus != nil: return v1alpha.AzurePrometheus, nil } return 0, errors.New("unknown agent type") diff --git a/manifest/v1alpha/alert_methods.go b/manifest/v1alpha/alert_methods.go new file mode 100644 index 00000000..675d0efb --- /dev/null +++ b/manifest/v1alpha/alert_methods.go @@ -0,0 +1,18 @@ +package v1alpha + +//go:generate ../../bin/go-enum --values --noprefix + +// DataSourceType represents the specific type of alert method. +// +/* ENUM( +Webhook = 1 +PagerDuty +Slack +Discord +Opsgenie +ServiceNow +Jira +Teams +Email +)*/ +type AlertMethodType int diff --git a/manifest/v1alpha/alert_methods_enum.go b/manifest/v1alpha/alert_methods_enum.go new file mode 100644 index 00000000..b1591485 --- /dev/null +++ b/manifest/v1alpha/alert_methods_enum.go @@ -0,0 +1,100 @@ +// Code generated by go-enum DO NOT EDIT. +// Version: 0.6.0 +// Revision: 919e61c0174b91303753ee3898569a01abb32c97 +// Build Date: 2023-12-18T15:54:43Z +// Built By: goreleaser + +package v1alpha + +import ( + "fmt" + + "github.com/pkg/errors" +) + +const ( + // Webhook is a AlertMethodType of type Webhook. + Webhook AlertMethodType = iota + 1 + // PagerDuty is a AlertMethodType of type PagerDuty. + PagerDuty + // Slack is a AlertMethodType of type Slack. + Slack + // Discord is a AlertMethodType of type Discord. + Discord + // Opsgenie is a AlertMethodType of type Opsgenie. + Opsgenie + // ServiceNow is a AlertMethodType of type ServiceNow. + ServiceNow + // Jira is a AlertMethodType of type Jira. + Jira + // Teams is a AlertMethodType of type Teams. + Teams + // Email is a AlertMethodType of type Email. + Email +) + +var ErrInvalidAlertMethodType = errors.New("not a valid AlertMethodType") + +const _AlertMethodTypeName = "WebhookPagerDutySlackDiscordOpsgenieServiceNowJiraTeamsEmail" + +// AlertMethodTypeValues returns a list of the values for AlertMethodType +func AlertMethodTypeValues() []AlertMethodType { + return []AlertMethodType{ + Webhook, + PagerDuty, + Slack, + Discord, + Opsgenie, + ServiceNow, + Jira, + Teams, + Email, + } +} + +var _AlertMethodTypeMap = map[AlertMethodType]string{ + Webhook: _AlertMethodTypeName[0:7], + PagerDuty: _AlertMethodTypeName[7:16], + Slack: _AlertMethodTypeName[16:21], + Discord: _AlertMethodTypeName[21:28], + Opsgenie: _AlertMethodTypeName[28:36], + ServiceNow: _AlertMethodTypeName[36:46], + Jira: _AlertMethodTypeName[46:50], + Teams: _AlertMethodTypeName[50:55], + Email: _AlertMethodTypeName[55:60], +} + +// String implements the Stringer interface. +func (x AlertMethodType) String() string { + if str, ok := _AlertMethodTypeMap[x]; ok { + return str + } + return fmt.Sprintf("AlertMethodType(%d)", x) +} + +// IsValid provides a quick way to determine if the typed value is +// part of the allowed enumerated values +func (x AlertMethodType) IsValid() bool { + _, ok := _AlertMethodTypeMap[x] + return ok +} + +var _AlertMethodTypeValue = map[string]AlertMethodType{ + _AlertMethodTypeName[0:7]: Webhook, + _AlertMethodTypeName[7:16]: PagerDuty, + _AlertMethodTypeName[16:21]: Slack, + _AlertMethodTypeName[21:28]: Discord, + _AlertMethodTypeName[28:36]: Opsgenie, + _AlertMethodTypeName[36:46]: ServiceNow, + _AlertMethodTypeName[46:50]: Jira, + _AlertMethodTypeName[50:55]: Teams, + _AlertMethodTypeName[55:60]: Email, +} + +// ParseAlertMethodType attempts to convert a string to a AlertMethodType. +func ParseAlertMethodType(name string) (AlertMethodType, error) { + if x, ok := _AlertMethodTypeValue[name]; ok { + return x, nil + } + return AlertMethodType(0), fmt.Errorf("%s is %w", name, ErrInvalidAlertMethodType) +} diff --git a/manifest/v1alpha/alertmethod/alert_method.go b/manifest/v1alpha/alertmethod/alert_method.go index 494979ff..f1aae016 100644 --- a/manifest/v1alpha/alertmethod/alert_method.go +++ b/manifest/v1alpha/alertmethod/alert_method.go @@ -1,7 +1,10 @@ package alertmethod import ( + "github.com/pkg/errors" + "github.com/nobl9/nobl9-go/manifest" + "github.com/nobl9/nobl9-go/manifest/v1alpha" ) //go:generate go run ../../../internal/cmd/objectimpl AlertMethod @@ -56,6 +59,30 @@ type Spec struct { Email *EmailAlertMethod `json:"email,omitempty"` } +func (s Spec) GetType() (v1alpha.AlertMethodType, error) { + switch { + case s.Webhook != nil: + return v1alpha.Webhook, nil + case s.PagerDuty != nil: + return v1alpha.PagerDuty, nil + case s.Slack != nil: + return v1alpha.Slack, nil + case s.Discord != nil: + return v1alpha.Discord, nil + case s.Opsgenie != nil: + return v1alpha.Opsgenie, nil + case s.ServiceNow != nil: + return v1alpha.ServiceNow, nil + case s.Jira != nil: + return v1alpha.Jira, nil + case s.Teams != nil: + return v1alpha.Teams, nil + case s.Email != nil: + return v1alpha.Email, nil + } + return 0, errors.New("unknown alert method type") +} + type WebhookAlertMethod struct { URL string `json:"url"` // Field required when AlertMethod is created. Template *string `json:"template,omitempty"` diff --git a/manifest/v1alpha/alertmethod/alert_method_test.go b/manifest/v1alpha/alertmethod/alert_method_test.go new file mode 100644 index 00000000..ad79afa4 --- /dev/null +++ b/manifest/v1alpha/alertmethod/alert_method_test.go @@ -0,0 +1,35 @@ +package alertmethod + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/nobl9/nobl9-go/manifest/v1alpha" +) + +func TestAlertMethod_Spec_GetType(t *testing.T) { + for _, method := range v1alpha.AlertMethodTypeValues() { + t.Run(method.String(), func(t *testing.T) { + spec := Spec{} + methodTypeStr := method.String() + setZeroValue(t, &spec, methodTypeStr) + typ, err := spec.GetType() + require.NoError(t, err) + assert.Equal(t, typ.String(), method.String()) + }) + } +} + +// setZeroValue sets a zero value of a pointer field in a struct using reflection. +func setZeroValue(t *testing.T, obj interface{}, fieldName string) { + t.Helper() + objValue := reflect.ValueOf(obj).Elem() + fieldValue := objValue.FieldByName(fieldName) + if !fieldValue.IsValid() || !fieldValue.CanSet() { + t.Fatalf("cannot set value for field: %s", fieldName) + } + fieldValue.Set(reflect.New(fieldValue.Type().Elem())) +}