2
0

stringproperty.go 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. package filter
  2. import (
  3. "fmt"
  4. "strings"
  5. "github.com/opencost/opencost/pkg/log"
  6. )
  7. // StringPropertied is used to validate the name of a property field and return its value
  8. type StringPropertied interface {
  9. // StringProperty acts as a validator and getter for a structs string properties
  10. StringProperty(string) (string, error)
  11. }
  12. // StringOperation is an enum that represents operations that can be performed
  13. // when filtering (equality, inequality, etc.)
  14. type StringOperation string
  15. // If you add a FilterOp, MAKE SURE TO UPDATE ALL FILTER IMPLEMENTATIONS! Go
  16. // does not enforce exhaustive pattern matching on "enum" types.
  17. const (
  18. // StringEquals is the equality operator
  19. // "kube-system" FilterEquals "kube-system" = true
  20. // "kube-syste" FilterEquals "kube-system" = false
  21. StringEquals StringOperation = "stringequals"
  22. // StringStartsWith matches strings with the given prefix.
  23. // "kube-system" StartsWith "kube" = true
  24. //
  25. // When comparing with a field represented by an array/slice, this is like
  26. // applying FilterContains to every element of the slice.
  27. StringStartsWith = "stringstartswith"
  28. )
  29. // StringProperty is the lowest-level type of filter. It represents
  30. // a filter operation (equality, inequality, etc.) on a field with a string value (namespace,
  31. // node, pod, etc.).
  32. type StringProperty[T StringPropertied] struct {
  33. Field string
  34. Op StringOperation
  35. // Value is for _all_ filters. A filter of 'namespace:"kubecost"' has
  36. // Value="kubecost"
  37. Value string
  38. }
  39. func (sp StringProperty[T]) String() string {
  40. return fmt.Sprintf(`(%s %s "%s")`, sp.Op, sp.Field, sp.Value)
  41. }
  42. func (sp StringProperty[T]) Matches(that T) bool {
  43. thatString, err := that.StringProperty(sp.Field)
  44. if err != nil {
  45. log.Errorf("Filter: StringProperty: could not retrieve field %s: %s", sp.Field, err.Error())
  46. return false
  47. }
  48. switch sp.Op {
  49. case StringEquals:
  50. // namespace:"__unallocated__" should match a.Properties.Namespace = ""
  51. if thatString == "" {
  52. return sp.Value == unallocatedSuffix
  53. }
  54. if thatString == sp.Value {
  55. return true
  56. }
  57. case StringStartsWith:
  58. // We don't need special __unallocated__ logic here because a query
  59. // asking for "__unallocated__" won't have a wildcard and unallocated
  60. // properties are the empty string.
  61. return strings.HasPrefix(thatString, sp.Value)
  62. default:
  63. log.Errorf("Filter: StringProperty: Unhandled filter op. This is a filter implementation error and requires immediate patching. Op: %s", sp.Op)
  64. return false
  65. }
  66. return false
  67. }