stringsliceproperty.go 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. package filter
  2. import (
  3. "fmt"
  4. "strings"
  5. "github.com/opencost/opencost/pkg/log"
  6. )
  7. type StringSlicePropertied interface {
  8. StringSliceProperty(string) ([]string, error)
  9. }
  10. // StringSliceOperation is an enum that represents operations that can be performed
  11. // when filtering (equality, inequality, etc.)
  12. type StringSliceOperation string
  13. const (
  14. // StringSliceContains is an array/slice membership operator
  15. // ["a", "b", "c"] FilterContains "a" = true
  16. StringSliceContains StringSliceOperation = "stringslicecontains"
  17. // StringSliceContainsPrefix is like FilterContains, but using StartsWith instead
  18. // of Equals.
  19. // ["kube-system", "abc123"] ContainsPrefix ["kube"] = true
  20. StringSliceContainsPrefix = "stringslicecontainsprefix"
  21. )
  22. // StringSliceProperty is the lowest-level type of filter. It represents
  23. // a filter operation (equality, inequality, etc.) on a property that contains a string slice
  24. type StringSliceProperty[T StringSlicePropertied] struct {
  25. Field string
  26. Op StringSliceOperation
  27. Value string
  28. }
  29. func (ssp StringSliceProperty[T]) String() string {
  30. return fmt.Sprintf(`(%s %s "%s")`, ssp.Op, ssp.Field, ssp.Value)
  31. }
  32. func (ssp StringSliceProperty[T]) Matches(that T) bool {
  33. thatSlice, err := that.StringSliceProperty(ssp.Field)
  34. if err != nil {
  35. log.Errorf("Filter: StringSliceProperty: could not retrieve field %s: %s", ssp.Field, err.Error())
  36. return false
  37. }
  38. switch ssp.Op {
  39. case StringSliceContains:
  40. if len(thatSlice) == 0 {
  41. return ssp.Value == unallocatedSuffix
  42. }
  43. for _, s := range thatSlice {
  44. if s == ssp.Value {
  45. return true
  46. }
  47. }
  48. case StringSliceContainsPrefix:
  49. // We don't need special __unallocated__ logic here because a query
  50. // asking for "__unallocated__" won't have a wildcard and unallocated
  51. // properties are the empty string.
  52. for _, s := range thatSlice {
  53. if strings.HasPrefix(s, ssp.Value) {
  54. return true
  55. }
  56. }
  57. return false
  58. default:
  59. log.Errorf("Filter: StringSliceProperty: Unhandled filter op. This is a filter implementation error and requires immediate patching. Op: %s", ssp.Op)
  60. return false
  61. }
  62. return false
  63. }