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/go.mod b/go.mod index eca2bec6..b157d71b 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/BurntSushi/toml v1.4.0 github.com/MicahParks/jwkset v0.5.18 github.com/MicahParks/keyfunc/v3 v3.3.3 - github.com/aws/aws-sdk-go v1.54.7 + github.com/aws/aws-sdk-go v1.54.11 github.com/bmatcuk/doublestar/v4 v4.6.1 github.com/goccy/go-yaml v1.11.3 github.com/golang-jwt/jwt/v5 v5.2.1 @@ -31,3 +31,6 @@ require ( golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +// We might in the end decide to just go with the fork direcly. +replace github.com/goccy/go-yaml => github.com/nobl9/go-yaml v0.0.0-20240626115914-6b82fd0d61b9 diff --git a/go.sum b/go.sum index 2cc59f62..b2898d87 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/MicahParks/jwkset v0.5.18 h1:WLdyMngF7rCrnstQxA7mpRoxeaWqGzPM/0z40PJU github.com/MicahParks/jwkset v0.5.18/go.mod h1:q8ptTGn/Z9c4MwbcfeCDssADeVQb3Pk7PnVxrvi+2QY= github.com/MicahParks/keyfunc/v3 v3.3.3 h1:c6j9oSu1YUo0k//KwF1miIQlEMtqNlj7XBFLB8jtEmY= github.com/MicahParks/keyfunc/v3 v3.3.3/go.mod h1:f/UMyXdKfkZzmBeBFUeYk+zu066J1Fcl48f7Wnl5Z48= -github.com/aws/aws-sdk-go v1.54.7 h1:k1wJ+NMOsXgq/Lsa0y1mS0DFoDeHFPcz2OjCq5H5Mjg= -github.com/aws/aws-sdk-go v1.54.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.54.11 h1:Zxuv/R+IVS0B66yz4uezhxH9FN9/G2nbxejYqAMFjxk= +github.com/aws/aws-sdk-go v1.54.11/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -19,8 +19,6 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87 github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I= -github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -42,6 +40,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/nobl9/go-yaml v0.0.0-20240626115914-6b82fd0d61b9 h1:HWAY7k8zA8T3dgQRg6llwyWshguD6bIeaVXukNrnen4= +github.com/nobl9/go-yaml v0.0.0-20240626115914-6b82fd0d61b9/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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..efdd5ce6 --- /dev/null +++ b/manifest/v1alpha/alert_methods.go @@ -0,0 +1,18 @@ +package v1alpha + +//go:generate ../../bin/go-enum --values + +// AlertMethodType 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..921f172e --- /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 ( + // AlertMethodTypeWebhook is a AlertMethodType of type Webhook. + AlertMethodTypeWebhook AlertMethodType = iota + 1 + // AlertMethodTypePagerDuty is a AlertMethodType of type PagerDuty. + AlertMethodTypePagerDuty + // AlertMethodTypeSlack is a AlertMethodType of type Slack. + AlertMethodTypeSlack + // AlertMethodTypeDiscord is a AlertMethodType of type Discord. + AlertMethodTypeDiscord + // AlertMethodTypeOpsgenie is a AlertMethodType of type Opsgenie. + AlertMethodTypeOpsgenie + // AlertMethodTypeServiceNow is a AlertMethodType of type ServiceNow. + AlertMethodTypeServiceNow + // AlertMethodTypeJira is a AlertMethodType of type Jira. + AlertMethodTypeJira + // AlertMethodTypeTeams is a AlertMethodType of type Teams. + AlertMethodTypeTeams + // AlertMethodTypeEmail is a AlertMethodType of type Email. + AlertMethodTypeEmail +) + +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{ + AlertMethodTypeWebhook, + AlertMethodTypePagerDuty, + AlertMethodTypeSlack, + AlertMethodTypeDiscord, + AlertMethodTypeOpsgenie, + AlertMethodTypeServiceNow, + AlertMethodTypeJira, + AlertMethodTypeTeams, + AlertMethodTypeEmail, + } +} + +var _AlertMethodTypeMap = map[AlertMethodType]string{ + AlertMethodTypeWebhook: _AlertMethodTypeName[0:7], + AlertMethodTypePagerDuty: _AlertMethodTypeName[7:16], + AlertMethodTypeSlack: _AlertMethodTypeName[16:21], + AlertMethodTypeDiscord: _AlertMethodTypeName[21:28], + AlertMethodTypeOpsgenie: _AlertMethodTypeName[28:36], + AlertMethodTypeServiceNow: _AlertMethodTypeName[36:46], + AlertMethodTypeJira: _AlertMethodTypeName[46:50], + AlertMethodTypeTeams: _AlertMethodTypeName[50:55], + AlertMethodTypeEmail: _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]: AlertMethodTypeWebhook, + _AlertMethodTypeName[7:16]: AlertMethodTypePagerDuty, + _AlertMethodTypeName[16:21]: AlertMethodTypeSlack, + _AlertMethodTypeName[21:28]: AlertMethodTypeDiscord, + _AlertMethodTypeName[28:36]: AlertMethodTypeOpsgenie, + _AlertMethodTypeName[36:46]: AlertMethodTypeServiceNow, + _AlertMethodTypeName[46:50]: AlertMethodTypeJira, + _AlertMethodTypeName[50:55]: AlertMethodTypeTeams, + _AlertMethodTypeName[55:60]: AlertMethodTypeEmail, +} + +// 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..66427852 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.AlertMethodTypeWebhook, nil + case s.PagerDuty != nil: + return v1alpha.AlertMethodTypePagerDuty, nil + case s.Slack != nil: + return v1alpha.AlertMethodTypeSlack, nil + case s.Discord != nil: + return v1alpha.AlertMethodTypeDiscord, nil + case s.Opsgenie != nil: + return v1alpha.AlertMethodTypeOpsgenie, nil + case s.ServiceNow != nil: + return v1alpha.AlertMethodTypeServiceNow, nil + case s.Jira != nil: + return v1alpha.AlertMethodTypeJira, nil + case s.Teams != nil: + return v1alpha.AlertMethodTypeTeams, nil + case s.Email != nil: + return v1alpha.AlertMethodTypeEmail, 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())) +} diff --git a/manifest/v1alpha/parser/parser_test.go b/manifest/v1alpha/parser/parser_test.go index 5d091c58..82785287 100644 --- a/manifest/v1alpha/parser/parser_test.go +++ b/manifest/v1alpha/parser/parser_test.go @@ -136,6 +136,15 @@ func Test_ParseObject_EnsureAllKindsAreParsed(t *testing.T) { } } +// References: +// - https://github.com/nobl9/go-yaml/pull/3 (fork) +// - https://github.com/nobl9/go-yaml/pull/457 (upstream) +func Test_ParseObject_DoubleQuotedJSONHandling(t *testing.T) { + data, format := readParserTestFile(t, "cloudwatch_slo.yaml") + _, err := ParseObject(data, manifest.KindSLO, format) + require.NoError(t, err) +} + func readParserTestFile(t *testing.T, filename string) ([]byte, manifest.ObjectFormat) { t.Helper() data, err := parserTestData.ReadFile(filepath.Join("test_data", filename)) diff --git a/manifest/v1alpha/parser/test_data/cloudwatch_slo.yaml b/manifest/v1alpha/parser/test_data/cloudwatch_slo.yaml new file mode 100644 index 00000000..90780895 --- /dev/null +++ b/manifest/v1alpha/parser/test_data/cloudwatch_slo.yaml @@ -0,0 +1,29 @@ +apiVersion: n9/v1alpha +kind: SLO +metadata: + name: cloudwatch-json + project: cloudwatch +spec: + budgetingMethod: Occurrences + description: "" + indicator: + metricSource: + kind: Agent + name: cloudwatch + project: cloudwatch + objectives: + - displayName: "" + op: lte + rawMetric: + query: + cloudWatch: + json: "[{\"Id\": \"e1\",\"Expression\": \"MAX(FILL(METRICS(), 0))\",\"Period\": 60},{\"Id\": \"m1\",\"MetricStat\": {\"Metric\": {\"Namespace\": \"AWS/ApplicationELB\",\"MetricName\": \"TargetResponseTime\",\"Dimensions\": [{\"Name\": \"LoadBalancer\",\"Value\": \"app/123/123\"},{\"Name\": \"TargetGroup\",\"Value\": \"targetgroup/123/123\"}]},\"Period\": 60,\"Stat\": \"Average\"},\"ReturnData\": false}]" + region: eu-central-1 + target: 0.8 + value: 0.9 + name: objective-1 + service: cloudwatch-service + timeWindows: + - count: 1 + isRolling: true + unit: Hour \ No newline at end of file