diff --git a/alerter/rules/localStore.go b/alerter/rules/localStore.go index b55439ce..3b2674c0 100644 --- a/alerter/rules/localStore.go +++ b/alerter/rules/localStore.go @@ -43,6 +43,15 @@ func FromPath(path, region string) (*fileStore, error) { } return nil }) + + knownRules := map[string]bool{} + for _, rule := range s.Rules() { + key := rule.Namespace + "/" + rule.Name + if knownRules[key] { + return nil, fmt.Errorf("duplicate rule %s", key) + } + knownRules[key] = true + } return s, err } diff --git a/alerter/rules/localStore_test.go b/alerter/rules/localStore_test.go index 548346b7..9721fcae 100644 --- a/alerter/rules/localStore_test.go +++ b/alerter/rules/localStore_test.go @@ -72,6 +72,49 @@ spec: destination: "peoplewhocare" ` +var dupeRule = `apiVersion: adx-mon.azure.com/v1 +kind: AlertRule +metadata: + name: alert-rule-one + namespace: namespace-one +spec: + database: SomeDB + interval: 1h + query: | + BadThings + | where stuff > 1 + | summarize count() by region + | extend Severity=3 + | extend Title = "Bad Things!" + | extend Summary = "Bad Things! Oh no" + | extend CorrelationId = region + | extend TSG="http://gofixit.com" + autoMitigateAfter: 2h + destination: "peoplewhocare" + +--- + +apiVersion: adx-mon.azure.com/v1 +kind: AlertRule +metadata: + name: alert-rule-one + namespace: namespace-one +spec: + database: SomeDB + interval: 1h + query: | + BadThings + | where stuff > 1 + | summarize count() by region + | extend Severity=3 + | extend Title = "Bad Things!" + | extend Summary = "Bad Things! Oh no" + | extend CorrelationId = region + | extend TSG="http://gofixit.com" + autoMitigateAfter: 2h + destination: "peoplewhocare" +` + // has incorrect indentation, which leads to unknown field errors var invalidRuleExample = `apiVersion: adx-mon.azure.com/v1 kind: AlertRule @@ -192,6 +235,11 @@ func TestFromPath(t *testing.T) { err = os.WriteFile(multiTestfile, []byte(multiRule), 0644) require.NoError(t, err) + dupeRuleDirectory := t.TempDir() + dupeTestfile := filepath.Join(dupeRuleDirectory, "dupetest.yaml") + err = os.WriteFile(dupeTestfile, []byte(dupeRule), 0644) + require.NoError(t, err) + invalidFileDirectory := t.TempDir() invalidTestfile := filepath.Join(invalidFileDirectory, "invalid.yaml") err = os.WriteFile(invalidTestfile, []byte(invalidRuleExample), 0644) @@ -259,6 +307,11 @@ func TestFromPath(t *testing.T) { path: invalidAnnotationFileDirectory, expectErr: true, }, + { + name: "dupe namespace/name in dir should return error", + path: dupeRuleDirectory, + expectErr: true, + }, } for _, tc := range testcases { diff --git a/alerter/service.go b/alerter/service.go index 5901e049..9eaba048 100644 --- a/alerter/service.go +++ b/alerter/service.go @@ -153,6 +153,7 @@ func Lint(ctx context.Context, opts *AlerterOpts, path string) error { if err != nil { return err } + logger.Infof("Linting rules from path=%s", path) lint := NewLinter()