Skip to content

Commit

Permalink
feat: new fields with singular name. (#1248)
Browse files Browse the repository at this point in the history
* feat: new fields with singular name.

Deprecate plural forms

* chore: only use singular form for healths
  • Loading branch information
adityathebe authored Jan 7, 2025
1 parent 6e026dc commit ad030a6
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 32 deletions.
19 changes: 12 additions & 7 deletions query/resource_selector.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,12 +207,17 @@ func SetResourceSelectorClause(ctx context.Context, resourceSelector types.Resou
if len(resourceSelector.Statuses) != 0 {
query = query.Where("status IN ?", resourceSelector.Statuses)
}
if len(resourceSelector.Healths) != 0 {
if len(resourceSelector.Health) != 0 {
// TODO: We need to support NOT IN AND LIKE queries in here
inclusion := lo.Filter(strings.Split(string(resourceSelector.Health), ","), func(s string, _ int) bool {
return !strings.HasPrefix(s, "!") && !strings.Contains(s, "*")
})

switch table {
case "checks":
query = query.Where("status IN ?", resourceSelector.Healths)
query = query.Where("status IN ?", inclusion)
default:
query = query.Where("health IN ?", resourceSelector.Healths)
query = query.Where("health IN ?", inclusion)
}
}

Expand Down Expand Up @@ -251,7 +256,7 @@ func SetResourceSelectorClause(ctx context.Context, resourceSelector types.Resou
} else {
parsedTagSelector, err := labels.Parse(resourceSelector.TagSelector)
if err != nil {
return nil, api.Errorf(api.EINVALID, fmt.Sprintf("failed to parse tag selector: %v", err))
return nil, api.Errorf(api.EINVALID, "failed to parse tag selector: %v", err)
}
requirements, _ := parsedTagSelector.Requirements()
for _, r := range requirements {
Expand All @@ -263,7 +268,7 @@ func SetResourceSelectorClause(ctx context.Context, resourceSelector types.Resou
if len(resourceSelector.LabelSelector) > 0 {
parsedLabelSelector, err := labels.Parse(resourceSelector.LabelSelector)
if err != nil {
return nil, api.Errorf(api.EINVALID, fmt.Sprintf("failed to parse label selector: %v", err))
return nil, api.Errorf(api.EINVALID, "failed to parse label selector: %v", err)
}
requirements, _ := parsedLabelSelector.Requirements()
for _, r := range requirements {
Expand All @@ -274,7 +279,7 @@ func SetResourceSelectorClause(ctx context.Context, resourceSelector types.Resou
if len(resourceSelector.FieldSelector) > 0 {
parsedFieldSelector, err := labels.Parse(resourceSelector.FieldSelector)
if err != nil {
return nil, api.Errorf(api.EINVALID, fmt.Sprintf("failed to parse field selector: %v", err))
return nil, api.Errorf(api.EINVALID, "failed to parse field selector: %v", err)
}

requirements, _ := parsedFieldSelector.Requirements()
Expand Down Expand Up @@ -333,7 +338,7 @@ func setSearchQueryParams(rs *types.ResourceSelector) {
case "status":
rs.Statuses = append(rs.Statuses, strings.Split(items[1], ",")...)
case "health":
rs.Healths = append(rs.Healths, strings.Split(items[1], ",")...)
rs.Health.Add(items[1])
case "limit":
l, _ := strconv.Atoi(items[1])
rs.Limit = l
Expand Down
6 changes: 3 additions & 3 deletions tests/query_resource_selector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ var _ = ginkgo.Describe("SearchResourceSelectors", func() {
{
description: "health",
query: query.SearchResourcesRequest{
Configs: []types.ResourceSelector{{Healths: []string{string(models.HealthHealthy)}}},
Components: []types.ResourceSelector{{Healths: []string{string(models.HealthHealthy)}}},
Checks: []types.ResourceSelector{{Healths: []string{string(models.HealthHealthy)}}},
Configs: []types.ResourceSelector{{Health: types.MatchExpression(models.HealthHealthy)}},
Components: []types.ResourceSelector{{Health: types.MatchExpression(models.HealthHealthy)}},
Checks: []types.ResourceSelector{{Health: types.MatchExpression(models.HealthHealthy)}},
},
Components: []models.Component{dummy.Logistics},
Checks: []models.Check{dummy.LogisticsAPIHealthHTTPCheck, dummy.LogisticsAPIHomeHTTPCheck},
Expand Down
11 changes: 10 additions & 1 deletion types/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package types

import (
"encoding/json"
"fmt"
"sort"
"strings"

Expand All @@ -28,7 +29,15 @@ func (t GoTemplate) Run(env map[string]any) (string, error) {
type MatchExpression string

func (t MatchExpression) Match(item string) bool {
return collections.MatchItems(item, string(t))
return collections.MatchItems(item, strings.Split(string(t), ",")...)
}

func (t *MatchExpression) Add(item string) {
if *t == "" {
*t = MatchExpression(item)
} else {
*t = MatchExpression(fmt.Sprintf("%s,%s", *t, item))
}
}

type MatchExpressions []MatchExpression
Expand Down
3 changes: 1 addition & 2 deletions types/envvar.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,14 +342,13 @@ func (t EnvVarResourceSelector) Hydrate(env map[string]any) (*ResourceSelector,
}

if len(t.Healths) > 0 {
rs.Healths = make([]string, len(t.Healths))
for i, expr := range t.Healths {
if !expr.Empty() {
result, err := expr.Eval(env)
if err != nil {
return nil, fmt.Errorf("failed to evaluate health at index %d: %v", i, err)
}
rs.Healths[i] = result
rs.Health.Add(result)
}
}
}
Expand Down
16 changes: 9 additions & 7 deletions types/resource_selector.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,14 +128,15 @@ type ResourceSelector struct {
LabelSelector string `json:"labelSelector,omitempty" yaml:"labelSelector,omitempty"`
FieldSelector string `json:"fieldSelector,omitempty" yaml:"fieldSelector,omitempty"`

// Health filters resources by the health.
// Multiple healths can be provided separated by comma.
Health MatchExpression `json:"health,omitempty"`

// Types filter resources by the type
Types Items `yaml:"types,omitempty" json:"types,omitempty"`

// Statuses filter resources by the status
Statuses Items `yaml:"statuses,omitempty" json:"statuses,omitempty"`

// Healths filter resources by the health
Healths Items `yaml:"healths,omitempty" json:"healths,omitempty"`
}

// ParseFilteringQuery parses a filtering query string.
Expand Down Expand Up @@ -197,7 +198,7 @@ func (c ResourceSelector) allEmptyButName() bool {
return c.ID == "" && c.Namespace == "" && c.Agent == "" && c.Scope == "" && c.Search == "" &&
len(c.Types) == 0 &&
len(c.Statuses) == 0 &&
len(c.Healths) == 0 &&
len(c.Health) == 0 &&
len(c.TagSelector) == 0 &&
len(c.LabelSelector) == 0 &&
len(c.FieldSelector) == 0
Expand Down Expand Up @@ -232,7 +233,7 @@ func (c ResourceSelector) Immutable() bool {
return false // still not specific enough
}

if len(c.TagSelector) != 0 || len(c.LabelSelector) != 0 || len(c.FieldSelector) != 0 || len(c.Statuses) != 0 || len(c.Healths) != 0 {
if len(c.TagSelector) != 0 || len(c.LabelSelector) != 0 || len(c.FieldSelector) != 0 || len(c.Statuses) != 0 || len(c.Health) != 0 {
// These selectors work on mutable part of the resource, so they can't be cached indefinitely
return false
}
Expand All @@ -249,7 +250,7 @@ func (c ResourceSelector) Hash() string {
c.Scope,
strings.Join(c.Types.Sort(), ","),
strings.Join(c.Statuses.Sort(), ","),
strings.Join(c.Types.Sort(), ","),
string(c.Health),
collections.SortedMap(collections.SelectorToMap(c.TagSelector)),
collections.SortedMap(collections.SelectorToMap(c.LabelSelector)),
collections.SortedMap(collections.SelectorToMap(c.FieldSelector)),
Expand All @@ -276,6 +277,7 @@ func (rs ResourceSelector) Matches(s ResourceSelectable) bool {
if rs.Namespace != "" && rs.Namespace != s.GetNamespace() {
return false
}

if len(rs.Types) > 0 && !rs.Types.Contains(s.GetType()) {
return false
}
Expand All @@ -290,7 +292,7 @@ func (rs ResourceSelector) Matches(s ResourceSelectable) bool {
if h, err := s.GetHealth(); err != nil {
logger.Errorf("failed to get health: %v", err)
return false
} else if len(rs.Healths) > 0 && !rs.Healths.Contains(h) {
} else if len(rs.Health) > 0 && !rs.Health.Match(h) {
return false
}

Expand Down
51 changes: 44 additions & 7 deletions types/resource_selector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,34 @@ var _ = Describe("Resource Selector", func() {
Type: lo.ToPtr("Kubernetes::Deployment"),
},
},
{
name: "Types multiple",
resourceSelector: types.ResourceSelector{
Types: []string{"Kubernetes::Node", "Kubernetes::Pod"},
},
selectable: models.ConfigItem{
Name: lo.ToPtr("cert-manager"),
Type: lo.ToPtr("Kubernetes::Pod"),
},
unselectable: models.ConfigItem{
Name: lo.ToPtr("cert-manager"),
Type: lo.ToPtr("Kubernetes::Deployment"),
},
},
{
name: "Type negatives",
resourceSelector: types.ResourceSelector{
Types: []string{"!Kubernetes::Deployment", "Kubernetes::Pod"},
},
selectable: models.ConfigItem{
Name: lo.ToPtr("cert-manager"),
Type: lo.ToPtr("Kubernetes::Pod"),
},
unselectable: models.ConfigItem{
Name: lo.ToPtr("cert-manager"),
Type: lo.ToPtr("Kubernetes::Deployment"),
},
},
{
name: "Statuses",
resourceSelector: types.ResourceSelector{
Expand All @@ -135,7 +163,7 @@ var _ = Describe("Resource Selector", func() {
name: "Healths",
resourceSelector: types.ResourceSelector{
Namespace: "default",
Healths: []string{"healthy"},
Health: "healthy",
},
selectable: models.ConfigItem{
Tags: types.JSONStringMap{
Expand All @@ -151,17 +179,22 @@ var _ = Describe("Resource Selector", func() {
},
},
{
name: "Types",
name: "Healths multiple",
resourceSelector: types.ResourceSelector{
Types: []string{"Kubernetes::Pod"},
Namespace: "default",
Health: "healthy,warning",
},
selectable: models.ConfigItem{
Name: lo.ToPtr("cert-manager"),
Type: lo.ToPtr("Kubernetes::Pod"),
Tags: types.JSONStringMap{
"namespace": "default",
},
Health: lo.ToPtr(models.HealthHealthy),
},
unselectable: models.ConfigItem{
Name: lo.ToPtr("cert-manager"),
Type: lo.ToPtr("Kubernetes::Deployment"),
Tags: types.JSONStringMap{
"namespace": "default",
},
Health: lo.ToPtr(models.HealthUnhealthy),
},
},
{
Expand Down Expand Up @@ -287,6 +320,10 @@ var _ = Describe("Resource Selector", func() {
}

for _, tt := range tests {
if tt.name != "Healths multiple - II" {
continue
}

It(tt.name, func() {
if tt.selectable != nil {
Expect(tt.resourceSelector.Matches(tt.selectable)).To(BeTrue())
Expand Down
5 changes: 0 additions & 5 deletions types/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit ad030a6

Please sign in to comment.