Skip to content

Commit 07b68ea

Browse files
authored
Adding name filtering (#559)
Issue #540
1 parent c589f60 commit 07b68ea

7 files changed

Lines changed: 111 additions & 21 deletions

File tree

cli/backup/alerting_alertrules.go

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import (
66
"log/slog"
77

88
"github.com/bep/simplecobra"
9-
domain3 "github.com/esnet/gdg/cli/domain"
9+
"github.com/esnet/gdg/cli/domain"
1010
"github.com/esnet/gdg/internal/adapter/grafana/api"
1111
"github.com/esnet/gdg/internal/config/config_domain"
12-
domain2 "github.com/esnet/gdg/internal/domain"
12+
appDomain "github.com/esnet/gdg/internal/domain"
1313
"github.com/esnet/gdg/internal/ports"
1414
"github.com/esnet/gdg/pkg/ptr"
1515
"github.com/go-openapi/strfmt"
@@ -19,10 +19,11 @@ import (
1919

2020
// getAlertRulesFilter constructs alert rule filters from command-line flags and returns both the parsed
2121
// AlertRuleFilterParams and a corresponding Filter for use in alert rule operations.
22-
func getAlertRulesFilter(cfg *config_domain.GDGAppConfiguration, grafanaService ports.GrafanaService, command *cobra.Command) (domain2.AlertRuleFilterParams, ports.Filter) {
23-
f := domain2.AlertRuleFilterParams{}
22+
func getAlertRulesFilter(cfg *config_domain.GDGAppConfiguration, grafanaService ports.GrafanaService, command *cobra.Command) (appDomain.AlertRuleFilterParams, ports.Filter) {
23+
f := appDomain.AlertRuleFilterParams{}
2424
f.Folder, _ = command.Flags().GetString("folder")
2525
f.Label, _ = command.Flags().GetStringArray("label")
26+
f.Name, _ = command.Flags().GetString("name")
2627
f.IgnoreWatchedFolders, _ = command.Flags().GetBool("ignore-watched-folders")
2728

2829
return f, api.NewAlertRuleFilter(cfg, grafanaService, f)
@@ -33,14 +34,15 @@ func getAlertRulesFilter(cfg *config_domain.GDGAppConfiguration, grafanaService
3334
// watched folders and labels.
3435
func newAlertingRulesCommand() simplecobra.Commander {
3536
description := "Manage Alerting Rules"
36-
return &domain3.SimpleCommand{
37+
return &domain.SimpleCommand{
3738
NameP: "rules",
3839
Short: description,
3940
Long: description,
40-
WithCFunc: func(cmd *cobra.Command, r *domain3.RootCommand) {
41+
WithCFunc: func(cmd *cobra.Command, r *domain.RootCommand) {
4142
cmd.Aliases = []string{"rule", "alert-rules", "alert-rule"}
4243
cmd.PersistentFlags().Bool("ignore-watched-folders", false, "Default to false, but if passed then will only operate on the list of folders listed in the configuration file")
43-
cmd.PersistentFlags().String("folder", "", "Add a folder filter")
44+
cmd.PersistentFlags().String("folder", "", "filter by folder")
45+
cmd.PersistentFlags().String("name", "", "filter by name")
4446
cmd.PersistentFlags().StringArray("label", []string{}, "Filter by label name value pair. (Additive behavior dashboard includes: label1 AND label2). ex --label env=staging")
4547
},
4648
CommandsList: []simplecobra.Commander{
@@ -49,7 +51,7 @@ func newAlertingRulesCommand() simplecobra.Commander {
4951
newClearAlertRulesCmd(),
5052
newUploadAlertRulesCmd(),
5153
},
52-
RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *domain3.RootCommand, args []string) error {
54+
RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *domain.RootCommand, args []string) error {
5355
return cd.CobraCommand.Help()
5456
},
5557
}
@@ -59,14 +61,14 @@ func newAlertingRulesCommand() simplecobra.Commander {
5961
// It supports folder and label filtering and is aliased as "u".
6062
func newUploadAlertRulesCmd() simplecobra.Commander {
6163
description := "Upload all alert rules for the given Organization"
62-
return &domain3.SimpleCommand{
64+
return &domain.SimpleCommand{
6365
NameP: "upload",
6466
Short: description,
6567
Long: description,
66-
WithCFunc: func(cmd *cobra.Command, r *domain3.RootCommand) {
68+
WithCFunc: func(cmd *cobra.Command, r *domain.RootCommand) {
6769
cmd.Aliases = []string{"u"}
6870
},
69-
RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *domain3.RootCommand, args []string) error {
71+
RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *domain.RootCommand, args []string) error {
7072
filtersList, rulesFilter := getAlertRulesFilter(rootCmd.ConfigSvc(), rootCmd.GrafanaSvc(), cd.CobraCommand)
7173
rootCmd.TableObj.AppendHeader(table.Row{"uid"})
7274
slog.Info("Uploading all alert rules for context",
@@ -91,14 +93,14 @@ func newUploadAlertRulesCmd() simplecobra.Commander {
9193
// The command is aliased as "c".
9294
func newClearAlertRulesCmd() simplecobra.Commander {
9395
description := "Clear all alert rules for the given Organization"
94-
return &domain3.SimpleCommand{
96+
return &domain.SimpleCommand{
9597
NameP: "clear",
9698
Short: description,
9799
Long: description,
98-
WithCFunc: func(cmd *cobra.Command, r *domain3.RootCommand) {
100+
WithCFunc: func(cmd *cobra.Command, r *domain.RootCommand) {
99101
cmd.Aliases = []string{"c"}
100102
},
101-
RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *domain3.RootCommand, args []string) error {
103+
RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *domain.RootCommand, args []string) error {
102104
filtersList, rulesFilter := getAlertRulesFilter(rootCmd.ConfigSvc(), rootCmd.GrafanaSvc(), cd.CobraCommand)
103105
slog.Info("Deleting all alert rules for context",
104106
slog.String("Organization", GetOrganizationName(rootCmd.ConfigSvc())),
@@ -131,14 +133,14 @@ func newClearAlertRulesCmd() simplecobra.Commander {
131133
// The command is aliased as "l".
132134
func newListAlertRulesCmd() simplecobra.Commander {
133135
description := "List all alert rules for the given Organization"
134-
return &domain3.SimpleCommand{
136+
return &domain.SimpleCommand{
135137
NameP: "list",
136138
Short: description,
137139
Long: description,
138-
WithCFunc: func(cmd *cobra.Command, r *domain3.RootCommand) {
140+
WithCFunc: func(cmd *cobra.Command, r *domain.RootCommand) {
139141
cmd.Aliases = []string{"l"}
140142
},
141-
RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *domain3.RootCommand, args []string) error {
143+
RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *domain.RootCommand, args []string) error {
142144
filtersList, rulesFilter := getAlertRulesFilter(rootCmd.ConfigSvc(), rootCmd.GrafanaSvc(), cd.CobraCommand)
143145
rootCmd.TableObj.AppendHeader(table.Row{"name", "uid", "folder", "ruleGroup", "Labels", "For"})
144146
slog.Info("Listing alert rules for context",
@@ -185,14 +187,14 @@ func newListAlertRulesCmd() simplecobra.Commander {
185187
// It applies any configured filters (folder, label) and renders the resulting list of downloaded rule files.
186188
func newDownloadAlertRulesCmd() simplecobra.Commander {
187189
description := "Download all alert rules for the given Organization"
188-
return &domain3.SimpleCommand{
190+
return &domain.SimpleCommand{
189191
NameP: "download",
190192
Short: description,
191193
Long: description,
192-
WithCFunc: func(cmd *cobra.Command, r *domain3.RootCommand) {
194+
WithCFunc: func(cmd *cobra.Command, r *domain.RootCommand) {
193195
cmd.Aliases = []string{"d"}
194196
},
195-
RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *domain3.RootCommand, args []string) error {
197+
RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *domain.RootCommand, args []string) error {
196198
filtersList, rulesFilter := getAlertRulesFilter(rootCmd.ConfigSvc(), rootCmd.GrafanaSvc(), cd.CobraCommand)
197199
rootCmd.TableObj.AppendHeader(table.Row{"alert-rule"})
198200
slog.Info("Downloading alert rules for context",

config/assets_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package config
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/esnet/gdg/pkg/test_tooling/path"
8+
"github.com/matryer/is"
9+
)
10+
11+
func TestAssets(t *testing.T) {
12+
path.FixTestDir("config", "..")
13+
is := is.New(t)
14+
file, err := GetFile("gdg-example.yml")
15+
is.NoErr(err)
16+
is.True(strings.Contains(file, "storage_engine"))
17+
//failing test
18+
file, err = GetFile("dummy")
19+
is.True(err != nil)
20+
is.Equal(err.Error(), "open dummy: file does not exist")
21+
is.Equal(file, "")
22+
}

internal/adapter/grafana/api/alerting_alertrules.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package api
22

33
import (
44
"encoding/json"
5+
"errors"
56
"fmt"
67
"log"
78
"log/slog"
@@ -29,6 +30,8 @@ func setupAlertRulesReaders(filterObj ports.Filter, grafanaSvc ports.GrafanaServ
2930
return nil, fmt.Errorf("unsupported data type")
3031
}
3132
switch filterType {
33+
case domain.Name:
34+
return ptr.ValueOrDefault(val.Title, ""), nil
3235
case domain.FolderFilter:
3336
return val.NestedPath, nil
3437
case domain.TagsFilter:
@@ -48,6 +51,14 @@ func setupAlertRulesReaders(filterObj ports.Filter, grafanaSvc ports.GrafanaServ
4851
return nil, fmt.Errorf("unsupported data type")
4952
}
5053
switch filterType {
54+
case domain.Name:
55+
{
56+
r := gjson.GetBytes(val, "title")
57+
if !r.Exists() || r.IsArray() {
58+
return nil, errors.New("no valid rule name was found")
59+
}
60+
return r.String(), nil
61+
}
5162
case domain.FolderFilter:
5263
{
5364
r := gjson.GetBytes(val, "folderUID")
@@ -155,6 +166,22 @@ func NewAlertRuleFilter(cfg *configDomain.GDGAppConfiguration, grafanaSvc ports.
155166
return fmt.Errorf("invalid folder filter. Expected: %v", expressions)
156167
}, folderArr)
157168

169+
filterObj.AddValidation(domain.Name, func(value any, expected any) error {
170+
val, expressions, convErr := v2.GetParams[string](value, expected, domain.Name)
171+
if convErr != nil {
172+
return convErr
173+
}
174+
//no filter active
175+
if expressions == "" {
176+
return nil
177+
}
178+
if expected == val {
179+
return nil
180+
}
181+
182+
return fmt.Errorf("invalid folder filter. Expected: %v", expressions)
183+
}, filterEntities.Name)
184+
158185
// Tags
159186
filterObj.AddValidation(domain.TagsFilter, func(value any, expected any) error {
160187
val, exp, convErr := v2.GetMismatchParams[map[string]string, []string](value, expected, domain.TagsFilter)

internal/domain/cli_filter_models.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package domain
44
// It allows filtering by folder name, a set of labels, and provides an option to ignore folder-based rules.
55
type AlertRuleFilterParams struct {
66
Folder string
7+
Name string
78
Label []string
89
IgnoreWatchedFolders bool
910
}

test/alerting_rules_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,34 @@ func TestAlertingRulesFilterTest(t *testing.T) {
192192
rulesList, err = apiClient.ListAlertRules(alertFilters)
193193
is.NoErr(err)
194194
is.Equal(len(rulesList), 2)
195+
// Adding a name based filter
196+
f = domain.AlertRuleFilterParams{
197+
IgnoreWatchedFolders: true,
198+
Name: "boom",
199+
}
200+
alertFilters = api.NewAlertRuleFilter(cfg, apiClient, f)
201+
rulesList, err = apiClient.ListAlertRules(alertFilters)
202+
is.Equal(len(rulesList), 1)
203+
f = domain.AlertRuleFilterParams{
204+
IgnoreWatchedFolders: false,
205+
Name: "boom",
206+
}
207+
alertFilters = api.NewAlertRuleFilter(cfg, apiClient, f)
208+
rulesList, err = apiClient.ListAlertRules(alertFilters)
209+
is.Equal(len(rulesList), 0)
210+
// Now run test for a rule under the monitored folders
211+
f = domain.AlertRuleFilterParams{
212+
IgnoreWatchedFolders: false,
213+
Name: "woof",
214+
}
215+
alertFilters = api.NewAlertRuleFilter(cfg, apiClient, f)
216+
rulesList, err = apiClient.ListAlertRules(alertFilters)
217+
is.Equal(len(rulesList), 1)
218+
f = domain.AlertRuleFilterParams{
219+
IgnoreWatchedFolders: true,
220+
Name: "woof",
221+
}
222+
is.Equal(len(rulesList), 1)
195223
}
196224

197225
func TestAlertingRulesNoFilterCrud(t *testing.T) {

website/content/docs/releases/gdg_0.9.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: "Version 0.9"
33
description: "Release Notes for v0.9"
4-
date: 2026-01-07T00:00:00
4+
date: 2026-03-07T00:00:00
55
draft: false
66
images: [ ]
77
weight: 2
@@ -11,6 +11,8 @@ toc: true
1111
## Release Notes for v0.9.3
1212

1313
### Changes
14+
- [#557](https://github.com/esnet/gdg/pull/557) Adds label and folder based filtering to alert rules.
15+
- [#559](https://github.com/esnet/gdg/pull/559) Adds name based filtering to alert rules.
1416

1517
### BugFixes
1618
- [#543](https://github.com/esnet/gdg/issues/543) Env vars not working due to a conflict with the secure_location pattern that was previously introduced. Env will work if secure location not used. Next major release (0.10.x) will fix this to allow for Env to take precedence over any config values.
@@ -21,6 +23,7 @@ toc: true
2123
- [#488](https://github.com/esnet/gdg/issues/488) Added an Env to disable enterprise tests. Allow contributors to not be blocked without a license.
2224
- [#531](https://github.com/esnet/gdg/issues/531) Fixing documentation issues with the Getting Started guide and css/icons rendering behavior.
2325
- General dependency and tooling update, upgrade to go 1.26.0, npm updates
26+
- [#558](https://github.com/esnet/gdg/pull/558) Refactors code base to follow port/adapters/domains
2427

2528
## Release Notes for v0.9.2
2629

website/content/docs/usage_guide/backup_guide.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ gdg backup alerting contactpoints download -- Download all known contact points
3434
gdg backup alerting contactpoints upload -- Upload all contact points
3535
gdg backup alerting contactpoints clear -- Clear all contact points
3636
```
37+
3738
{{< details "Example Output:" >}}
3839
```
3940
┌────────────────┬─────────┬─────────┬───────────────────────────────────────────────────┐
@@ -78,6 +79,12 @@ gdg backup alerting rules upload -- Upload all rules
7879
gdg backup alerting rules clear -- Clear all rules
7980
```
8081

82+
**Available Options:**
83+
- `--folder string` filter by folder
84+
- `--ignore-watched-folders` Default to false, but if passed then will only operate on the list of folders listed in the configuration file
85+
- `--label stringArray` Filter by label name value pair. (Additive behavior dashboard includes: label1 AND label2). ex --label env=staging
86+
- `--name string` filter by name
87+
8188

8289
{{< details "Example Output:" >}}
8390
```

0 commit comments

Comments
 (0)