assetmatcher.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. package opencost
  2. import (
  3. "fmt"
  4. "strings"
  5. afilter "github.com/opencost/opencost/core/pkg/filter/asset"
  6. "github.com/opencost/opencost/core/pkg/filter/ast"
  7. "github.com/opencost/opencost/core/pkg/filter/matcher"
  8. "github.com/opencost/opencost/core/pkg/filter/transform"
  9. )
  10. // AssetMatcher is a matcher implementation for Asset instances,
  11. // compiled using the matcher.MatchCompiler.
  12. type AssetMatcher matcher.Matcher[Asset]
  13. // NewAssetMatchCompiler creates a new instance of a
  14. // matcher.MatchCompiler[Asset] which can be used to compile filter.Filter
  15. // ASTs into matcher.Matcher[Asset] implementations.
  16. //
  17. // If the label config is nil, the compiler will fail to compile alias filters
  18. // if any are present in the AST.
  19. //
  20. // If storage interfaces every support querying natively by alias (e.g. if a
  21. // data store contained a "product" attribute on an Asset row), that should
  22. // be handled by a purpose-built AST compiler.
  23. func NewAssetMatchCompiler() *matcher.MatchCompiler[Asset] {
  24. passes := []transform.CompilerPass{}
  25. passes = append(passes,
  26. transform.PrometheusKeySanitizePass(),
  27. transform.UnallocatedReplacementPass(),
  28. )
  29. return matcher.NewMatchCompiler(
  30. assetFieldMap,
  31. assetSliceFieldMap,
  32. assetMapFieldMap,
  33. passes...,
  34. )
  35. }
  36. // Maps fields from an asset to a string value based on an identifier
  37. func assetFieldMap(a Asset, identifier ast.Identifier) (string, error) {
  38. if identifier.Field == nil {
  39. return "", fmt.Errorf("cannot map field from identifier with nil field")
  40. }
  41. if a == nil {
  42. return "", fmt.Errorf("cannot map field for nil Asset")
  43. }
  44. // Check special fields before defaulting to properties-based fields
  45. switch afilter.AssetField(identifier.Field.Name) {
  46. case afilter.FieldType:
  47. return strings.ToLower(a.Type().String()), nil
  48. case afilter.FieldLabel:
  49. labels := a.GetLabels()
  50. if labels == nil {
  51. return "", nil
  52. }
  53. return labels[identifier.Key], nil
  54. }
  55. props := a.GetProperties()
  56. if props == nil {
  57. return "", fmt.Errorf("cannot map field for Asset with nil props")
  58. }
  59. switch afilter.AssetField(identifier.Field.Name) {
  60. case afilter.FieldName:
  61. return props.Name, nil
  62. case afilter.FieldCategory:
  63. return props.Category, nil
  64. case afilter.FieldClusterID:
  65. return props.Cluster, nil
  66. case afilter.FieldProject:
  67. return props.Project, nil
  68. case afilter.FieldProvider:
  69. return props.Provider, nil
  70. case afilter.FieldProviderID:
  71. return props.ProviderID, nil
  72. case afilter.FieldAccount:
  73. return props.Account, nil
  74. case afilter.FieldService:
  75. return props.Service, nil
  76. }
  77. return "", fmt.Errorf("Failed to find string identifier on Asset: %s", identifier.Field.Name)
  78. }
  79. // Maps slice fields from an asset to a []string value based on an identifier
  80. func assetSliceFieldMap(a Asset, identifier ast.Identifier) ([]string, error) {
  81. return nil, fmt.Errorf("Assets have no slice fields")
  82. }
  83. // Maps map fields from an Asset to a map[string]string value based on an identifier
  84. func assetMapFieldMap(a Asset, identifier ast.Identifier) (map[string]string, error) {
  85. if a == nil {
  86. return nil, fmt.Errorf("cannot get map field for nil Asset")
  87. }
  88. switch afilter.AssetField(identifier.Field.Name) {
  89. case afilter.FieldLabel:
  90. return a.GetLabels(), nil
  91. }
  92. return nil, fmt.Errorf("Failed to find map[string]string identifier on Asset: %s", identifier.Field.Name)
  93. }