diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b42884a126..e3f8535d294 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ 1. [16297](https://github.com/influxdata/influxdb/pull/16297): Add support for notification rule to pkger parser 1. [16298](https://github.com/influxdata/influxdb/pull/16298): Add support for notification rule pkger dry run functionality 1. [16305](https://github.com/influxdata/influxdb/pull/16305): Add support for notification rule pkger apply functionality +1. [16312](https://github.com/influxdata/influxdb/pull/16312): Add support for notification rule pkger export functionality ### Bug Fixes diff --git a/cmd/influx/pkg.go b/cmd/influx/pkg.go index 3169a728dbd..df7c0d86d4b 100644 --- a/cmd/influx/pkg.go +++ b/cmd/influx/pkg.go @@ -55,6 +55,7 @@ type cmdPkgBuilder struct { dashboards string endpoints string labels string + rules string telegrafs string variables string } @@ -229,6 +230,7 @@ func (b *cmdPkgBuilder) cmdPkgExport() *cobra.Command { cmd.Flags().StringVar(&b.exportOpts.dashboards, "dashboards", "", "List of dashboard ids comma separated") cmd.Flags().StringVar(&b.exportOpts.endpoints, "endpoints", "", "List of notification endpoint ids comma separated") cmd.Flags().StringVar(&b.exportOpts.labels, "labels", "", "List of label ids comma separated") + cmd.Flags().StringVar(&b.exportOpts.rules, "rules", "", "List of notification rule ids comma separated") cmd.Flags().StringVar(&b.exportOpts.telegrafs, "telegraf-configs", "", "List of telegraf config ids comma separated") cmd.Flags().StringVar(&b.exportOpts.variables, "variables", "", "List of variable ids comma separated") @@ -253,8 +255,9 @@ func (b *cmdPkgBuilder) pkgExportRunEFn() func(*cobra.Command, []string) error { {kind: pkger.KindBucket, idStrs: strings.Split(b.exportOpts.buckets, ",")}, {kind: pkger.KindCheck, idStrs: strings.Split(b.exportOpts.checks, ",")}, {kind: pkger.KindDashboard, idStrs: strings.Split(b.exportOpts.dashboards, ",")}, - {kind: pkger.KindNotificationEndpoint, idStrs: strings.Split(b.exportOpts.endpoints, ",")}, {kind: pkger.KindLabel, idStrs: strings.Split(b.exportOpts.labels, ",")}, + {kind: pkger.KindNotificationEndpoint, idStrs: strings.Split(b.exportOpts.endpoints, ",")}, + {kind: pkger.KindNotificationRule, idStrs: strings.Split(b.exportOpts.rules, ",")}, {kind: pkger.KindTelegraf, idStrs: strings.Split(b.exportOpts.telegrafs, ",")}, {kind: pkger.KindVariable, idStrs: strings.Split(b.exportOpts.variables, ",")}, } diff --git a/cmd/influxd/launcher/pkger_test.go b/cmd/influxd/launcher/pkger_test.go index 43175c68687..f05cb18444d 100644 --- a/cmd/influxd/launcher/pkger_test.go +++ b/cmd/influxd/launcher/pkger_test.go @@ -400,6 +400,11 @@ spec: } resWithNewName := []pkger.ResourceToClone{ + { + Kind: pkger.KindNotificationRule, + Name: "new rule name", + ID: influxdb.ID(rule.ID), + }, { Kind: pkger.KindVariable, Name: "new name", @@ -457,6 +462,13 @@ spec: assert.Equal(t, endpoints[0].NotificationEndpoint.GetDescription(), newEndpoints[0].NotificationEndpoint.GetDescription()) hasLabelAssociations(t, newEndpoints[0].LabelAssociations, 1, "label_1") + require.Len(t, newSum.NotificationRules, 1) + newRule := newSum.NotificationRules[0] + assert.Equal(t, "new rule name", newRule.Name) + assert.Zero(t, newRule.EndpointID) + assert.Equal(t, rule.EndpointName, newRule.EndpointName) + hasLabelAssociations(t, newRule.LabelAssociations, 1, "label_1") + require.Len(t, newSum.TelegrafConfigs, 1) assert.Equal(t, teles[0].TelegrafConfig.Name, newSum.TelegrafConfigs[0].TelegrafConfig.Name) assert.Equal(t, teles[0].TelegrafConfig.Description, newSum.TelegrafConfigs[0].TelegrafConfig.Description) @@ -636,6 +648,16 @@ spec: level: INfO min: 30.0 max: 45.0 + - type: outside_range + level: WARN + min: 60.0 + max: 70.0 + - type: greater + level: CRIT + val: 80 + - type: lesser + level: OK + val: 30 associations: - kind: Label name: label_1 diff --git a/http/swagger.yml b/http/swagger.yml index 881af4cb54a..086f25d889f 100644 --- a/http/swagger.yml +++ b/http/swagger.yml @@ -7156,6 +7156,7 @@ components: - dashboard - label - notification_endpoint + - notification_rule - telegraf - variable name: diff --git a/pkger/clone_resource.go b/pkger/clone_resource.go index 3823b06a54d..2f65b10c607 100644 --- a/pkger/clone_resource.go +++ b/pkger/clone_resource.go @@ -8,6 +8,7 @@ import ( "github.com/influxdata/influxdb/notification" icheck "github.com/influxdata/influxdb/notification/check" "github.com/influxdata/influxdb/notification/endpoint" + "github.com/influxdata/influxdb/notification/rule" ) // ResourceToClone is a resource that will be cloned. @@ -79,18 +80,14 @@ func checkToResource(ch influxdb.Check, name string) Resource { } assignNonZeroStrings(r, map[string]string{fieldDescription: ch.GetDescription()}) - assignFluxDur := func(field string, dur *notification.Duration) { - if dur == nil { - return - } - r[field] = dur.TimeDuration().String() - } - assignBase := func(base icheck.Base) { r[fieldQuery] = base.Query.Text r[fieldCheckStatusMessageTemplate] = base.StatusMessageTemplate - assignFluxDur(fieldEvery, base.Every) - assignFluxDur(fieldOffset, base.Offset) + assignNonZeroFluxDurs(r, map[string]*notification.Duration{ + fieldEvery: base.Every, + fieldOffset: base.Offset, + }) + var tags []Resource for _, t := range base.Tags { if t.Valid() != nil { @@ -110,8 +107,10 @@ func checkToResource(ch influxdb.Check, name string) Resource { case *icheck.Deadman: r[fieldKind] = KindCheckDeadman.title() assignBase(cT.Base) - assignFluxDur(fieldCheckTimeSince, cT.TimeSince) - assignFluxDur(fieldCheckStaleTime, cT.StaleTime) + assignNonZeroFluxDurs(r, map[string]*notification.Duration{ + fieldCheckTimeSince: cT.TimeSince, + fieldCheckStaleTime: cT.StaleTime, + }) r[fieldLevel] = cT.Level.String() assignNonZeroBools(r, map[string]bool{fieldCheckReportZero: cT.ReportZero}) case *icheck.Threshold: @@ -129,15 +128,21 @@ func checkToResource(ch influxdb.Check, name string) Resource { func convertThreshold(th icheck.ThresholdConfig) Resource { r := Resource{fieldLevel: th.GetLevel().String()} + assignLesser := func(threshType thresholdType, allValues bool, val float64) { + r[fieldType] = string(threshType) + assignNonZeroBools(r, map[string]bool{fieldCheckAllValues: allValues}) + r[fieldValue] = val + } + switch realType := th.(type) { case icheck.Lesser: - r[fieldType] = string(thresholdTypeLesser) - assignNonZeroBools(r, map[string]bool{fieldCheckAllValues: realType.AllValues}) - r[fieldValue] = realType.Value + assignLesser(thresholdTypeLesser, realType.AllValues, realType.Value) + case *icheck.Lesser: + assignLesser(thresholdTypeLesser, realType.AllValues, realType.Value) case icheck.Greater: - r[fieldType] = string(thresholdTypeGreater) - assignNonZeroBools(r, map[string]bool{fieldCheckAllValues: realType.AllValues}) - r[fieldValue] = realType.Value + assignLesser(thresholdTypeGreater, realType.AllValues, realType.Value) + case *icheck.Greater: + assignLesser(thresholdTypeGreater, realType.AllValues, realType.Value) case icheck.Range: assignRangeThreshold(r, realType) case *icheck.Range: @@ -433,6 +438,67 @@ func endpointToResource(e influxdb.NotificationEndpoint, name string) Resource { return r } +func ruleToResource(iRule influxdb.NotificationRule, endpointName, name string) Resource { + if name == "" { + name = iRule.GetName() + } + r := Resource{ + fieldKind: KindNotificationRule.title(), + fieldName: name, + fieldNotificationRuleEndpointName: endpointName, + } + assignNonZeroStrings(r, map[string]string{ + fieldDescription: iRule.GetDescription(), + }) + + assignBase := func(base rule.Base) { + assignNonZeroFluxDurs(r, map[string]*notification.Duration{ + fieldEvery: base.Every, + fieldOffset: base.Offset, + }) + + var tagRes []Resource + for _, tRule := range base.TagRules { + tagRes = append(tagRes, Resource{ + fieldKey: tRule.Key, + fieldValue: tRule.Value, + fieldOperator: tRule.Operator.String(), + }) + } + if len(tagRes) > 0 { + r[fieldNotificationRuleTagRules] = tagRes + } + + var statusRuleRes []Resource + for _, sRule := range base.StatusRules { + sRes := Resource{ + fieldNotificationRuleCurrentLevel: sRule.CurrentLevel.String(), + } + if sRule.PreviousLevel != nil { + sRes[fieldNotificationRulePreviousLevel] = sRule.PreviousLevel.String() + } + statusRuleRes = append(statusRuleRes, sRes) + } + if len(statusRuleRes) > 0 { + r[fieldNotificationRuleStatusRules] = statusRuleRes + } + } + + switch t := iRule.(type) { + case *rule.HTTP: + assignBase(t.Base) + case *rule.PagerDuty: + assignBase(t.Base) + r[fieldNotificationRuleMessageTemplate] = t.MessageTemplate + case *rule.Slack: + assignBase(t.Base) + r[fieldNotificationRuleMessageTemplate] = t.MessageTemplate + assignNonZeroStrings(r, map[string]string{fieldNotificationRuleChannel: t.Channel}) + } + + return r +} + func telegrafToResource(t influxdb.TelegrafConfig, name string) Resource { if name == "" { name = t.Name @@ -487,6 +553,18 @@ func variableToResource(v influxdb.Variable, name string) Resource { return r } +func assignNonZeroFluxDurs(r Resource, m map[string]*notification.Duration) { + for field, dur := range m { + if dur == nil { + continue + } + if dur.TimeDuration() == 0 { + continue + } + r[field] = dur.TimeDuration().String() + } +} + func assignNonZeroBools(r Resource, m map[string]bool) { for k, v := range m { if v { diff --git a/pkger/models.go b/pkger/models.go index cb1fcec1adf..ea5fcbc4669 100644 --- a/pkger/models.go +++ b/pkger/models.go @@ -54,6 +54,19 @@ var kinds = map[Kind]bool{ KindVariable: true, } +var kindsUniqByName = map[Kind]bool{ + KindBucket: true, + KindCheck: true, + KindCheckDeadman: true, + KindCheckThreshold: true, + KindLabel: true, + KindNotificationEndpoint: true, + KindNotificationEndpointHTTP: true, + KindNotificationEndpointPagerDuty: true, + KindNotificationEndpointSlack: true, + KindVariable: true, +} + // Kind is a resource kind. type Kind string diff --git a/pkger/parser.go b/pkger/parser.go index abc866b82fd..8a1f1826870 100644 --- a/pkger/parser.go +++ b/pkger/parser.go @@ -1274,7 +1274,13 @@ func uniqResources(resources []Resource) []Resource { kind Kind name string } + + // these 2 maps are used to eliminate duplicates that come + // from dependencies while keeping the Resource that has any + // associations. If there are no associations, then the resources + // are no different from one another. m := make(map[key]bool) + res := make(map[key]Resource) out := make([]Resource, 0, len(resources)) for _, r := range resources { @@ -1285,18 +1291,22 @@ func uniqResources(resources []Resource) []Resource { if err := k.OK(); err != nil { continue } - switch k { - // these 3 kinds are unique, have existing state identifiable by name - case KindBucket, KindLabel, KindVariable: + + if kindsUniqByName[k] { rKey := key{kind: k, name: r.Name()} - if m[rKey] { + if hasAssociations, ok := m[rKey]; ok && hasAssociations { continue } - m[rKey] = true - fallthrough - default: - out = append(out, r) + _, hasAssociations := r[fieldAssociations] + m[rKey] = hasAssociations + res[rKey] = r + continue } + out = append(out, r) + } + + for _, r := range res { + out = append(out, r) } return out } diff --git a/pkger/service.go b/pkger/service.go index 73a4e983698..389a03139c9 100644 --- a/pkger/service.go +++ b/pkger/service.go @@ -257,10 +257,17 @@ func (s *Service) CreatePkg(ctx context.Context, setters ...CreatePkgSetFn) (*Pk } var kindPriorities = map[Kind]int{ - KindLabel: 1, - KindBucket: 2, - KindVariable: 3, - KindDashboard: 4, + KindLabel: 1, + KindBucket: 2, + KindCheckDeadman: 3, + KindCheckThreshold: 4, + KindNotificationEndpointHTTP: 5, + KindNotificationEndpointPagerDuty: 6, + KindNotificationEndpointSlack: 7, + KindNotificationRule: 8, + KindVariable: 9, + KindTelegraf: 10, + KindDashboard: 11, } sort.Slice(pkg.Spec.Resources, func(i, j int) bool { @@ -302,6 +309,10 @@ func (s *Service) cloneOrgResources(ctx context.Context, orgID influxdb.ID) ([]R resType: KindNotificationEndpoint.ResourceType(), cloneFn: s.cloneOrgNotificationEndpoints, }, + { + resType: KindNotificationRule.ResourceType(), + cloneFn: s.cloneOrgNotificationRules, + }, { resType: KindTelegraf.ResourceType(), cloneFn: s.cloneOrgTelegrafs, @@ -417,6 +428,24 @@ func (s *Service) cloneOrgNotificationEndpoints(ctx context.Context, orgID influ return resources, nil } +func (s *Service) cloneOrgNotificationRules(ctx context.Context, orgID influxdb.ID) ([]ResourceToClone, error) { + rules, _, err := s.ruleSVC.FindNotificationRules(ctx, influxdb.NotificationRuleFilter{ + OrgID: &orgID, + }) + if err != nil { + return nil, err + } + + resources := make([]ResourceToClone, 0, len(rules)) + for _, r := range rules { + resources = append(resources, ResourceToClone{ + Kind: KindNotificationRule, + ID: r.GetID(), + }) + } + return resources, nil +} + func (s *Service) cloneOrgTelegrafs(ctx context.Context, orgID influxdb.ID) ([]ResourceToClone, error) { teles, _, err := s.teleSVC.FindTelegrafConfigs(ctx, influxdb.TelegrafConfigFilter{OrgID: &orgID}) if err != nil { @@ -458,7 +487,11 @@ func (s *Service) resourceCloneToResource(ctx context.Context, r ResourceToClone e = ierrors.Wrap(e, "cloning resource") } }() - var newResource Resource + + var ( + newResource Resource + sidecarResources []Resource + ) switch { case r.Kind.is(KindBucket): bkt, err := s.bucketSVC.FindBucketByID(ctx, r.ID) @@ -495,6 +528,12 @@ func (s *Service) resourceCloneToResource(ctx context.Context, r ResourceToClone return nil, err } newResource = endpointToResource(e, r.Name) + case r.Kind.is(KindNotificationRule): + ruleRes, endpointRes, err := s.exportNotificationRule(ctx, r) + if err != nil { + return nil, err + } + newResource, sidecarResources = ruleRes, append(sidecarResources, endpointRes) case r.Kind.is(KindTelegraf): t, err := s.teleSVC.FindTelegrafConfigByID(ctx, r.ID) if err != nil { @@ -519,7 +558,21 @@ func (s *Service) resourceCloneToResource(ctx context.Context, r ResourceToClone newResource[fieldAssociations] = ass.associations } - return append([]Resource{newResource}, ass.newLableResources...), nil + return append(ass.newLableResources, append(sidecarResources, newResource)...), nil +} + +func (s *Service) exportNotificationRule(ctx context.Context, r ResourceToClone) (Resource, Resource, error) { + rule, err := s.ruleSVC.FindNotificationRuleByID(ctx, r.ID) + if err != nil { + return nil, nil, err + } + + ruleEndpoint, err := s.endpointSVC.FindNotificationEndpointByID(ctx, rule.GetEndpointID()) + if err != nil { + return nil, nil, err + } + + return ruleToResource(rule, ruleEndpoint.GetName(), r.Name), endpointToResource(ruleEndpoint, ""), nil } type ( @@ -1043,10 +1096,8 @@ func (s *Service) Apply(ctx context.Context, orgID, userID influxdb.ID, pkg *Pkg } } - // this is required after first primary run and before secondary run, b/c this has dependencies - // on the notification endpoints being live in the source of truth (store). Hence we break - // it up after the 1st group and before the 2nd. The 2nd group needs these done first so the - // label mappings are accurate. + // this has to be run after the above primary resources, because it relies on + // notification endpoints already being applied. app, err := s.applyNotificationRulesGenerator(ctx, orgID, pkg.notificationRules()) if err != nil { return Summary{}, err diff --git a/pkger/service_test.go b/pkger/service_test.go index 3eead8655e6..7331c640a07 100644 --- a/pkger/service_test.go +++ b/pkger/service_test.go @@ -14,6 +14,7 @@ import ( "github.com/influxdata/influxdb/notification" icheck "github.com/influxdata/influxdb/notification/check" "github.com/influxdata/influxdb/notification/endpoint" + "github.com/influxdata/influxdb/notification/rule" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" @@ -1966,6 +1967,171 @@ func TestService(t *testing.T) { } }) + t.Run("notification rules", func(t *testing.T) { + newRuleBase := func(id int) rule.Base { + return rule.Base{ + ID: 9000, + Name: "old_name", + Description: "desc", + EndpointID: influxdb.ID(id), + Every: mustDuration(t, time.Hour), + Offset: mustDuration(t, time.Minute), + TagRules: []notification.TagRule{ + {Tag: influxdb.Tag{Key: "k1", Value: "v1"}}, + }, + StatusRules: []notification.StatusRule{ + {CurrentLevel: notification.Ok, PreviousLevel: levelPtr(notification.Warn)}, + {CurrentLevel: notification.Critical}, + }, + } + } + + tests := []struct { + name string + newName string + endpoint influxdb.NotificationEndpoint + rule influxdb.NotificationRule + }{ + { + name: "pager duty", + newName: "pager_duty_name", + endpoint: &endpoint.PagerDuty{ + Base: endpoint.Base{ + ID: newTestIDPtr(13), + Name: "endpoint_0", + Description: "desc", + Status: influxdb.TaskStatusActive, + }, + ClientURL: "http://example.com", + RoutingKey: influxdb.SecretField{Key: "-routing-key"}, + }, + rule: &rule.PagerDuty{ + Base: newRuleBase(13), + MessageTemplate: "Template", + }, + }, + { + name: "slack", + endpoint: &endpoint.Slack{ + Base: endpoint.Base{ + ID: newTestIDPtr(13), + Name: "endpoint_0", + Description: "desc", + Status: influxdb.TaskStatusInactive, + }, + URL: "http://example.com", + Token: influxdb.SecretField{Key: "tokne"}, + }, + rule: &rule.Slack{ + Base: newRuleBase(13), + Channel: "abc", + MessageTemplate: "SLACK TEMPlate", + }, + }, + { + name: "http none", + endpoint: &endpoint.HTTP{ + Base: endpoint.Base{ + ID: newTestIDPtr(13), + Name: "endpoint_0", + Description: "desc", + Status: influxdb.TaskStatusInactive, + }, + AuthMethod: "none", + Method: "GET", + URL: "http://example.com", + }, + rule: &rule.HTTP{ + Base: newRuleBase(13), + }, + }, + } + + for _, tt := range tests { + fn := func(t *testing.T) { + endpointSVC := mock.NewNotificationEndpointService() + endpointSVC.FindNotificationEndpointByIDF = func(ctx context.Context, id influxdb.ID) (influxdb.NotificationEndpoint, error) { + if id != tt.endpoint.GetID() { + return nil, errors.New("uh ohhh, wrong id here: " + id.String()) + } + return tt.endpoint, nil + } + ruleSVC := mock.NewNotificationRuleStore() + ruleSVC.FindNotificationRuleByIDF = func(ctx context.Context, id influxdb.ID) (influxdb.NotificationRule, error) { + return tt.rule, nil + } + + svc := newTestService( + WithNotificationEndpointSVC(endpointSVC), + WithNotificationRuleSVC(ruleSVC), + ) + + resToClone := ResourceToClone{ + Kind: KindNotificationRule, + ID: tt.rule.GetID(), + Name: tt.newName, + } + pkg, err := svc.CreatePkg(context.TODO(), CreateWithExistingResources(resToClone)) + require.NoError(t, err) + + require.Len(t, pkg.Summary().NotificationRules, 1) + + actualRule := pkg.Summary().NotificationRules[0] + assert.Zero(t, actualRule.ID) + assert.Zero(t, actualRule.EndpointID) + assert.Zero(t, actualRule.EndpointType) + assert.Equal(t, "endpoint_0", actualRule.EndpointName) + + baseEqual := func(t *testing.T, base rule.Base) { + t.Helper() + expectedName := base.Name + if tt.newName != "" { + expectedName = tt.newName + } + assert.Equal(t, expectedName, actualRule.Name) + assert.Equal(t, base.Description, actualRule.Description) + assert.Equal(t, base.Every.TimeDuration().String(), actualRule.Every) + assert.Equal(t, base.Offset.TimeDuration().String(), actualRule.Offset) + + for _, sRule := range base.StatusRules { + expected := SummaryStatusRule{CurrentLevel: sRule.CurrentLevel.String()} + if sRule.PreviousLevel != nil { + expected.PreviousLevel = sRule.PreviousLevel.String() + } + assert.Contains(t, actualRule.StatusRules, expected) + } + for _, tRule := range base.TagRules { + expected := SummaryTagRule{ + Key: tRule.Key, + Value: tRule.Value, + Operator: tRule.Operator.String(), + } + assert.Contains(t, actualRule.TagRules, expected) + } + } + + switch p := tt.rule.(type) { + case *rule.HTTP: + baseEqual(t, p.Base) + case *rule.PagerDuty: + baseEqual(t, p.Base) + assert.Equal(t, p.MessageTemplate, actualRule.MessageTemplate) + case *rule.Slack: + baseEqual(t, p.Base) + assert.Equal(t, p.MessageTemplate, actualRule.MessageTemplate) + } + + require.Len(t, pkg.Summary().NotificationEndpoints, 1) + + actualEndpoint := pkg.Summary().NotificationEndpoints[0].NotificationEndpoint + assert.Equal(t, tt.endpoint.GetName(), actualEndpoint.GetName()) + assert.Equal(t, tt.endpoint.GetDescription(), actualEndpoint.GetDescription()) + assert.Equal(t, tt.endpoint.GetStatus(), actualEndpoint.GetStatus()) + } + t.Run(tt.name, fn) + } + }) + t.Run("variable", func(t *testing.T) { tests := []struct { name string @@ -2242,7 +2408,17 @@ func TestService(t *testing.T) { endpointSVC.FindNotificationEndpointsF = func(ctx context.Context, f influxdb.NotificationEndpointFilter, _ ...influxdb.FindOptions) ([]influxdb.NotificationEndpoint, int, error) { id := influxdb.ID(2) endpoints := []influxdb.NotificationEndpoint{ - &endpoint.HTTP{Base: endpoint.Base{ID: &id}}, + &endpoint.HTTP{ + Base: endpoint.Base{ + ID: &id, + Name: "http", + }, + URL: "http://example.com", + Username: influxdb.SecretField{Key: id.String() + "-username"}, + Password: influxdb.SecretField{Key: id.String() + "-password"}, + AuthMethod: "basic", + Method: "POST", + }, } return endpoints, len(endpoints), nil } @@ -2260,6 +2436,23 @@ func TestService(t *testing.T) { }, nil } + ruleSVC := mock.NewNotificationRuleStore() + ruleSVC.FindNotificationRulesF = func(ctx context.Context, f influxdb.NotificationRuleFilter, _ ...influxdb.FindOptions) ([]influxdb.NotificationRule, int, error) { + out := []influxdb.NotificationRule{&rule.HTTP{Base: rule.Base{ID: 91}}} + return out, len(out), nil + } + ruleSVC.FindNotificationRuleByIDF = func(ctx context.Context, id influxdb.ID) (influxdb.NotificationRule, error) { + return &rule.HTTP{ + Base: rule.Base{ + ID: id, + Name: "rule_0", + EndpointID: 2, + Every: mustDuration(t, time.Minute), + StatusRules: []notification.StatusRule{{CurrentLevel: notification.Critical}}, + }, + }, nil + } + labelSVC := mock.NewLabelService() labelSVC.FindLabelsFn = func(_ context.Context, f influxdb.LabelFilter) ([]*influxdb.Label, error) { if f.OrgID == nil || *f.OrgID != orgID { @@ -2294,6 +2487,7 @@ func TestService(t *testing.T) { WithDashboardSVC(dashSVC), WithLabelSVC(labelSVC), WithNotificationEndpointSVC(endpointSVC), + WithNotificationRuleSVC(ruleSVC), WithVariableSVC(varSVC), ) @@ -2321,9 +2515,23 @@ func TestService(t *testing.T) { require.Len(t, endpoints, 1) assert.Equal(t, "http", endpoints[0].NotificationEndpoint.GetName()) + rules := summary.NotificationRules + require.Len(t, rules, 1) + assert.Equal(t, "rule_0", rules[0].Name) + assert.Equal(t, "http", rules[0].EndpointName) + vars := summary.Variables require.Len(t, vars, 1) assert.Equal(t, "variable", vars[0].Name) }) }) } + +func newTestIDPtr(i int) *influxdb.ID { + id := influxdb.ID(i) + return &id +} + +func levelPtr(l notification.CheckLevel) *notification.CheckLevel { + return &l +}