Просмотр исходного кода

Opencost Core Refactor (#2440)

* Make FileStorage's Read and Write go through `flock()` (#2429)

* Add ReadLocked and WriteLocked to fileutil

Signed-off-by: Michael Dresser <michaelmdresser@gmail.com>

* Use locking r/w in FileStorage

This is necessary as we plan to use multiple processes with this library
accessing files on the same filesystem.

Signed-off-by: Michael Dresser <michaelmdresser@gmail.com>

* Add FD-based flock()-ed read and write

Signed-off-by: Michael Dresser <michaelmdresser@gmail.com>

* Move file lock code to separate file

Signed-off-by: Michael Dresser <michaelmdresser@gmail.com>

* Make locks unimplemented for Windows

This should allow Windows users to still import Opencost but functionality
which requires these functions will not work. This should have minimal impact.
Refer to the following when wishing to implement for windows:
- https://github.com/golang/go/blob/master/src/cmd/go/internal/lockedfile/internal/filelock/filelock_windows.go
- maybe also https://github.com/gofrs/flock/blob/master/flock_windows.go

Signed-off-by: Michael Dresser <michaelmdresser@gmail.com>

* Unlock after failed read and write

Signed-off-by: Michael Dresser <michaelmdresser@gmail.com>

* Add further unit test

Signed-off-by: Michael Dresser <michaelmdresser@gmail.com>

* Make r/w locked robust to FD position

Signed-off-by: Michael Dresser <michaelmdresser@gmail.com>

---------

Signed-off-by: Michael Dresser <michaelmdresser@gmail.com>
Signed-off-by: Matt Bolt <mbolt35@gmail.com>

* First pass at refactoring 'opencost-core' types into separate module. First pass at unifying filters

Signed-off-by: Matt Bolt <mbolt35@gmail.com>

* Update coverageset to use legacy filters

Signed-off-by: Matt Bolt <mbolt35@gmail.com>

* have opencost leverage promutil from core

Signed-off-by: Matt Bolt <mbolt35@gmail.com>

* fix errors, port over allocation and asset props enhancements

Signed-off-by: Matt Bolt <mbolt35@gmail.com>

* moved version to core

Signed-off-by: Matt Bolt <mbolt35@gmail.com>

* rename proto package to protocol

Signed-off-by: Matt Bolt <mbolt35@gmail.com>

* Fix aws spot price with default config
Signed-off-by: Sachin Kumar <sachin@rafay.co>

Signed-off-by: Matt Bolt <mbolt35@gmail.com>

* Pick public pricing if available else pick default
Signed-off-by: Sachin Kumar <sachin@rafay.co>

Signed-off-by: Matt Bolt <mbolt35@gmail.com>

* Adding a log when public price is not found
Signed-off-by: Sachin Kumar <sachin@rafay.co>

Signed-off-by: Matt Bolt <mbolt35@gmail.com>

* Update log as suggested

Signed-off-by: Sachin Kumar <sachin@rafay.co>
Signed-off-by: Matt Bolt <mbolt35@gmail.com>

* Add HTTP and filter helper functions for collections

Signed-off-by: Niko Kovacevic <nikovacevic@gmail.com>
Signed-off-by: Matt Bolt <mbolt35@gmail.com>

* Rename core/pkg/kubecost => core/pkg/opencost

Signed-off-by: Matt Bolt <mbolt35@gmail.com>

* remove duplicate file locking API

* Ignore UnmountedPVCost, and regenerate codecs

* Fix url replacement

Signed-off-by: Matt Bolt <mbolt35@gmail.com>

---------

Signed-off-by: Michael Dresser <michaelmdresser@gmail.com>
Signed-off-by: Matt Bolt <mbolt35@gmail.com>
Signed-off-by: Sachin Kumar <sachin@rafay.co>
Signed-off-by: Niko Kovacevic <nikovacevic@gmail.com>
Co-authored-by: Michael Dresser <michael@kubecost.com>
Co-authored-by: Sachin Kumar <sachin@rafay.co>
Co-authored-by: sachin-rafay <94062930+sachin-rafay@users.noreply.github.com>
Co-authored-by: Niko Kovacevic <nikovacevic@gmail.com>
Matt Bolt 2 лет назад
Родитель
Сommit
194ebd1cbb
100 измененных файлов с 1620 добавлено и 502 удалено
  1. 50 0
      core/go.mod
  2. 650 0
      core/go.sum
  3. 75 0
      core/pkg/clusters/clusterinfo.go
  4. 0 0
      core/pkg/collections/blockingqueue.go
  5. 1 1
      core/pkg/env/env.go
  6. 0 0
      core/pkg/filter/allocation/fields.go
  7. 1 1
      core/pkg/filter/allocation/parser.go
  8. 1 1
      core/pkg/filter/allocation/parser_test.go
  9. 0 0
      core/pkg/filter/asset/fields.go
  10. 1 1
      core/pkg/filter/asset/parser.go
  11. 139 0
      core/pkg/filter/ast/fields.go
  12. 0 0
      core/pkg/filter/ast/lexer.go
  13. 0 0
      core/pkg/filter/ast/lexer_test.go
  14. 9 0
      core/pkg/filter/ast/ops.go
  15. 0 0
      core/pkg/filter/ast/parser.go
  16. 0 0
      core/pkg/filter/ast/tree.go
  17. 2 2
      core/pkg/filter/ast/walker.go
  18. 0 0
      core/pkg/filter/ast/walker_test.go
  19. 0 0
      core/pkg/filter/cloudcost/fields.go
  20. 1 1
      core/pkg/filter/cloudcost/parser.go
  21. 1 1
      core/pkg/filter/filter.go
  22. 1 1
      core/pkg/filter/legacy/allcut.go
  23. 1 1
      core/pkg/filter/legacy/allpass.go
  24. 1 1
      core/pkg/filter/legacy/and.go
  25. 23 23
      core/pkg/filter/legacy/cloudcost/cloudcost.go
  26. 0 0
      core/pkg/filter/legacy/cloudcost/cloudcost_test.go
  27. 1 1
      core/pkg/filter/legacy/filter.go
  28. 237 237
      core/pkg/filter/legacy/filter_test.go
  29. 1 1
      core/pkg/filter/legacy/not.go
  30. 1 1
      core/pkg/filter/legacy/or.go
  31. 2 2
      core/pkg/filter/legacy/stringmapproperty.go
  32. 2 2
      core/pkg/filter/legacy/stringproperty.go
  33. 2 2
      core/pkg/filter/legacy/stringsliceproperty.go
  34. 3 3
      core/pkg/filter/legacy/window.go
  35. 12 12
      core/pkg/filter/legacy/window_test.go
  36. 0 0
      core/pkg/filter/matcher/allcut.go
  37. 0 0
      core/pkg/filter/matcher/allpass.go
  38. 0 0
      core/pkg/filter/matcher/and.go
  39. 3 3
      core/pkg/filter/matcher/compiler.go
  40. 0 0
      core/pkg/filter/matcher/matcher.go
  41. 4 4
      core/pkg/filter/matcher/matcher_test.go
  42. 0 0
      core/pkg/filter/matcher/not.go
  43. 0 0
      core/pkg/filter/matcher/or.go
  44. 2 2
      core/pkg/filter/matcher/stringmapmatcher.go
  45. 2 2
      core/pkg/filter/matcher/stringmatcher.go
  46. 2 2
      core/pkg/filter/matcher/stringslicematcher.go
  47. 11 5
      core/pkg/filter/ops/ops.go
  48. 5 5
      core/pkg/filter/ops/ops_test.go
  49. 1 4
      core/pkg/filter/transform/pass.go
  50. 1 1
      core/pkg/filter/transform/promlabels.go
  51. 1 1
      core/pkg/filter/transform/unallocated.go
  52. 0 0
      core/pkg/filter/util/stack.go
  53. 0 0
      core/pkg/log/counter.go
  54. 0 0
      core/pkg/log/counter_test.go
  55. 0 0
      core/pkg/log/log.go
  56. 0 0
      core/pkg/log/profiler.go
  57. 11 11
      core/pkg/opencost/allocation.go
  58. 2 2
      core/pkg/opencost/allocation_json.go
  59. 2 2
      core/pkg/opencost/allocation_json_test.go
  60. 13 14
      core/pkg/opencost/allocation_test.go
  61. 5 5
      core/pkg/opencost/allocationfilter_test.go
  62. 6 6
      core/pkg/opencost/allocationmatcher.go
  63. 4 4
      core/pkg/opencost/allocationmatcher_test.go
  64. 111 29
      core/pkg/opencost/allocationprops.go
  65. 1 1
      core/pkg/opencost/allocationprops_test.go
  66. 9 9
      core/pkg/opencost/asset.go
  67. 2 2
      core/pkg/opencost/asset_json.go
  68. 2 2
      core/pkg/opencost/asset_json_test.go
  69. 2 2
      core/pkg/opencost/asset_test.go
  70. 5 5
      core/pkg/opencost/assetmatcher.go
  71. 40 1
      core/pkg/opencost/assetprops.go
  72. 2 2
      core/pkg/opencost/bingen.go
  73. 8 8
      core/pkg/opencost/cloudcost.go
  74. 2 2
      core/pkg/opencost/cloudcost_test.go
  75. 5 5
      core/pkg/opencost/cloudcostmatcher.go
  76. 2 2
      core/pkg/opencost/cloudcostprops.go
  77. 1 1
      core/pkg/opencost/cloudcostprops_test.go
  78. 1 1
      core/pkg/opencost/cloudusage.go
  79. 1 1
      core/pkg/opencost/common.go
  80. 6 6
      core/pkg/opencost/config.go
  81. 2 2
      core/pkg/opencost/config_test.go
  82. 1 1
      core/pkg/opencost/costmetric.go
  83. 3 3
      core/pkg/opencost/coverage.go
  84. 1 1
      core/pkg/opencost/diff_test.go
  85. 2 2
      core/pkg/opencost/json.go
  86. 1 1
      core/pkg/opencost/mock.go
  87. 4 3
      core/pkg/opencost/opencost_codecs.go
  88. 1 1
      core/pkg/opencost/opencost_codecs_test.go
  89. 2 2
      core/pkg/opencost/query.go
  90. 1 1
      core/pkg/opencost/status.go
  91. 5 5
      core/pkg/opencost/summaryallocation.go
  92. 2 2
      core/pkg/opencost/summaryallocation_json.go
  93. 2 2
      core/pkg/opencost/summaryallocation_json_test.go
  94. 2 2
      core/pkg/opencost/summaryallocation_test.go
  95. 2 2
      core/pkg/opencost/totals.go
  96. 2 2
      core/pkg/opencost/totals_json.go
  97. 1 1
      core/pkg/opencost/totals_test.go
  98. 66 11
      core/pkg/opencost/window.go
  99. 14 9
      core/pkg/opencost/window_test.go
  100. 13 2
      core/pkg/protocol/http.go

+ 50 - 0
core/go.mod

@@ -0,0 +1,50 @@
+module github.com/opencost/opencost/core
+
+go 1.21.0
+
+require (
+	github.com/davecgh/go-spew v1.1.1
+	github.com/goccy/go-json v0.9.11
+	github.com/google/go-cmp v0.6.0
+	github.com/hashicorp/go-multierror v1.1.1
+	github.com/json-iterator/go v1.1.12
+	github.com/patrickmn/go-cache v2.1.0+incompatible
+	github.com/rs/zerolog v1.26.1
+	github.com/spf13/viper v1.8.1
+	golang.org/x/exp v0.0.0-20221031165847-c99f073a8326
+	golang.org/x/sync v0.1.0
+	golang.org/x/text v0.14.0
+	google.golang.org/protobuf v1.30.0
+	k8s.io/api v0.25.3
+	k8s.io/apimachinery v0.25.3
+)
+
+require (
+	github.com/fsnotify/fsnotify v1.6.0 // indirect
+	github.com/go-logr/logr v1.2.4 // indirect
+	github.com/gogo/protobuf v1.3.2 // indirect
+	github.com/google/gofuzz v1.2.0 // indirect
+	github.com/hashicorp/errwrap v1.0.0 // indirect
+	github.com/hashicorp/hcl v1.0.0 // indirect
+	github.com/magiconair/properties v1.8.5 // indirect
+	github.com/mitchellh/mapstructure v1.5.0 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/pelletier/go-toml v1.9.3 // indirect
+	github.com/spf13/afero v1.6.0 // indirect
+	github.com/spf13/cast v1.3.1 // indirect
+	github.com/spf13/jwalterweatherman v1.1.0 // indirect
+	github.com/spf13/pflag v1.0.5 // indirect
+	github.com/stretchr/testify v1.8.4 // indirect
+	github.com/subosito/gotenv v1.2.0 // indirect
+	golang.org/x/net v0.17.0 // indirect
+	golang.org/x/sys v0.13.0 // indirect
+	gopkg.in/inf.v0 v0.9.1 // indirect
+	gopkg.in/ini.v1 v1.67.0 // indirect
+	gopkg.in/yaml.v2 v2.4.0 // indirect
+	k8s.io/klog/v2 v2.80.0 // indirect
+	k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
+	sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
+	sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
+	sigs.k8s.io/yaml v1.3.0 // indirect
+)

+ 650 - 0
core/go.sum

@@ -0,0 +1,650 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
+cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
+cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
+cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
+cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
+cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
+cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
+cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
+cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
+cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
+cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
+cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
+cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
+cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
+cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
+cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
+cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
+cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
+cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
+cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
+cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
+cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
+cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
+cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
+cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
+cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
+cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
+cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
+cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
+cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
+cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
+cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
+cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
+github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
+github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
+github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
+github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
+github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
+github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
+github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
+github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
+github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
+github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
+github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
+github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
+github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
+github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
+github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
+github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
+github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
+github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
+github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc=
+github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
+github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
+github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
+github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
+github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44=
+github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
+github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
+github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
+go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
+go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
+go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
+go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
+golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 h1:QfTh0HpN6hlw6D3vu8DAwC8pBIwikq0AI1evdm+FksE=
+golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
+golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
+golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
+golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
+golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
+golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
+google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
+google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
+google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
+google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
+google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
+google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
+google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
+google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
+google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
+google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
+google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
+google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
+google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
+google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
+google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
+google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+k8s.io/api v0.25.3 h1:Q1v5UFfYe87vi5H7NU0p4RXC26PPMT8KOpr1TLQbCMQ=
+k8s.io/api v0.25.3/go.mod h1:o42gKscFrEVjHdQnyRenACrMtbuJsVdP+WVjqejfzmI=
+k8s.io/apimachinery v0.25.3 h1:7o9ium4uyUOM76t6aunP0nZuex7gDf8VGwkR5RcJnQc=
+k8s.io/apimachinery v0.25.3/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo=
+k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
+k8s.io/klog/v2 v2.80.0 h1:lyJt0TWMPaGoODa8B8bUuxgHS3W/m/bNr2cca3brA/g=
+k8s.io/klog/v2 v2.80.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
+k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4=
+k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k=
+sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
+sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
+sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
+sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
+sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=

+ 75 - 0
core/pkg/clusters/clusterinfo.go

@@ -0,0 +1,75 @@
+package clusters
+
+// The following constants are used as keys into the cluster info map data structure
+const (
+	ClusterInfoIdKey               = "id"
+	ClusterInfoNameKey             = "name"
+	ClusterInfoProviderKey         = "provider"
+	ClusterInfoProjectKey          = "project"
+	ClusterInfoAccountKey          = "account"
+	ClusterInfoRegionKey           = "region"
+	ClusterInfoProvisionerKey      = "provisioner"
+	ClusterInfoProfileKey          = "clusterProfile"
+	ClusterInfoLogCollectionKey    = "logCollection"
+	ClusterInfoProductAnalyticsKey = "productAnalytics"
+	ClusterInfoErrorReportingKey   = "errorReporting"
+	ClusterInfoValuesReportingKey  = "valuesReporting"
+	ClusterInfoThanosEnabledKey    = "thanosEnabled"
+	ClusterInfoThanosOffsetKey     = "thanosOffset"
+	ClusterInfoVersionKey          = "version"
+)
+
+// ClusterInfo holds attributes of Cluster from metrics pulled from Prometheus
+type ClusterInfo struct {
+	ID          string `json:"id"`
+	Name        string `json:"name"`
+	Profile     string `json:"profile"`
+	Provider    string `json:"provider"`
+	Account     string `json:"account"`
+	Project     string `json:"project"`
+	Region      string `json:"region"`
+	Provisioner string `json:"provisioner"`
+}
+
+// Clone creates a copy of ClusterInfo and returns it
+func (ci *ClusterInfo) Clone() *ClusterInfo {
+	if ci == nil {
+		return nil
+	}
+
+	return &ClusterInfo{
+		ID:          ci.ID,
+		Name:        ci.Name,
+		Profile:     ci.Profile,
+		Provider:    ci.Provider,
+		Account:     ci.Account,
+		Project:     ci.Project,
+		Region:      ci.Region,
+		Provisioner: ci.Provisioner,
+	}
+}
+
+type ClusterMap interface {
+	// GetClusterIDs returns a slice containing all of the cluster identifiers.
+	GetClusterIDs() []string
+
+	// AsMap returns the cluster map as a standard go map
+	AsMap() map[string]*ClusterInfo
+
+	// InfoFor returns the ClusterInfo entry for the provided clusterID or nil if it
+	// doesn't exist
+	InfoFor(clusterID string) *ClusterInfo
+
+	// NameFor returns the name of the cluster provided the clusterID.
+	NameFor(clusterID string) string
+
+	// NameIDFor returns an identifier in the format "<clusterName>/<clusterID>" if the cluster has an
+	// assigned name. Otherwise, just the clusterID is returned.
+	NameIDFor(clusterID string) string
+}
+
+// ClusterInfoProvider is a contract which is capable of performing cluster info lookups.
+type ClusterInfoProvider interface {
+	// GetClusterInfo returns a string map containing the local/remote connected cluster info
+	GetClusterInfo() map[string]string
+}

+ 0 - 0
pkg/collections/blockingqueue.go → core/pkg/collections/blockingqueue.go


+ 1 - 1
pkg/env/env.go → core/pkg/env/env.go

@@ -4,7 +4,7 @@ import (
 	"os"
 	"time"
 
-	"github.com/opencost/opencost/pkg/util/mapper"
+	"github.com/opencost/opencost/core/pkg/util/mapper"
 )
 
 //--------------------------------------------------------------------------

+ 0 - 0
pkg/filter21/allocation/fields.go → core/pkg/filter/allocation/fields.go


+ 1 - 1
pkg/filter21/allocation/parser.go → core/pkg/filter/allocation/parser.go

@@ -1,6 +1,6 @@
 package allocation
 
-import "github.com/opencost/opencost/pkg/filter21/ast"
+import "github.com/opencost/opencost/core/pkg/filter/ast"
 
 // a slice of all the allocation field instances the lexer should recognize as
 // valid left-hand comparators

+ 1 - 1
pkg/filter21/allocation/parser_test.go → core/pkg/filter/allocation/parser_test.go

@@ -6,7 +6,7 @@ import (
 	"testing"
 
 	"github.com/hashicorp/go-multierror"
-	"github.com/opencost/opencost/pkg/filter21/ast"
+	"github.com/opencost/opencost/core/pkg/filter/ast"
 )
 
 var parser ast.FilterParser = NewAllocationFilterParser()

+ 0 - 0
pkg/filter21/asset/fields.go → core/pkg/filter/asset/fields.go


+ 1 - 1
pkg/filter21/asset/parser.go → core/pkg/filter/asset/parser.go

@@ -1,6 +1,6 @@
 package asset
 
-import "github.com/opencost/opencost/pkg/filter21/ast"
+import "github.com/opencost/opencost/core/pkg/filter/ast"
 
 // a slice of all the asset field instances the lexer should recognize as
 // valid left-hand comparators

+ 139 - 0
core/pkg/filter/ast/fields.go

@@ -0,0 +1,139 @@
+package ast
+
+// FieldType is an enumeration of specific types relevant to lexing and
+// parsing a filter.
+type FieldType int
+
+const (
+	FieldTypeDefault FieldType = 1 << iota
+	FieldTypeSlice
+	FieldTypeMap
+	FieldTypeAlias
+)
+
+// FieldAttribute is an enumeration of specific attributes that can be set
+// on each type of field.
+type FieldAttribute int
+
+const (
+	FieldAttributeNilable FieldAttribute = 1 << (iota + 4)
+)
+
+// fieldType with attributes is a convenience function for creating a field type with
+// attributes flags set.
+func fieldTypeWithAttributes(ft FieldType, attrs ...FieldAttribute) FieldType {
+	for _, attr := range attrs {
+		ft.Set(attr)
+	}
+
+	return ft
+}
+
+// Set updates the field type with the provided attribute.
+func (ft *FieldType) Set(attr FieldAttribute) {
+	*ft |= FieldType(attr)
+}
+
+// Unset removes the provided attribute from the field type.
+func (ft *FieldType) Unset(attr FieldAttribute) {
+	*ft &= ^FieldType(attr)
+}
+
+// Is returns true if the field type has the provided attribute.
+func (ft FieldType) Is(attr FieldAttribute) bool {
+	return ft&FieldType(attr) != 0
+}
+
+// IsDefault returns true if the type is the default/base type.
+func (ft FieldType) IsDefault() bool {
+	return ft&FieldTypeDefault != 0
+}
+
+// IsSlice returns true if the type is a slice type.
+func (ft FieldType) IsSlice() bool {
+	return ft&FieldTypeSlice != 0
+}
+
+// IsMap returns true if the type is a map type.
+func (ft FieldType) IsMap() bool {
+	return ft&FieldTypeMap != 0
+}
+
+// IsAlias returns true if the type is an alias type.
+func (ft FieldType) IsAlias() bool {
+	return ft&FieldTypeAlias != 0
+}
+
+// Field is a Lexer input which acts as a mapping of identifiers used to lex/parse filters.
+type Field struct {
+	// Name contains the name of the specific field as it appears in language.
+	Name string
+
+	fieldType FieldType
+}
+
+// Field equivalence is determined by name and type.
+func (f *Field) Equal(other *Field) bool {
+	if f == nil || other == nil {
+		return false
+	}
+
+	return f.Name == other.Name && f.fieldType == other.fieldType
+}
+
+// IsSlice returns true if the field is a slice. This instructs the lexer that the field
+// should allow contains operations.
+func (f *Field) IsSlice() bool {
+	return f.fieldType.IsSlice()
+}
+
+// IsMap returns true if the field is a map. This instructs the lexer that the field should
+// allow keyed-access operations.
+func (f *Field) IsMap() bool {
+	return f.fieldType.IsMap()
+}
+
+// IsAlias returns true if the field is an alias type. This instructs the lexer that the field
+// is an alias for custom logical resolution by an external compiler.
+func (f *Field) IsAlias() bool {
+	return f.fieldType.IsAlias()
+}
+
+// IsNilable returns true if the field is an default field type that can contain a nil value. Only
+// specific compilers will need to know this information. ie: Go does not have a nil value for strings,
+// but SQL does.
+func (f *Field) IsNilable() bool {
+	return f.fieldType.Is(FieldAttributeNilable)
+}
+
+// NewField creates a default string field using the provided name.
+func NewField[T ~string](name T, attrs ...FieldAttribute) *Field {
+	return &Field{
+		Name:      string(name),
+		fieldType: fieldTypeWithAttributes(FieldTypeDefault, attrs...),
+	}
+}
+
+// NewSliceField creates a slice field using the provided name.
+func NewSliceField[T ~string](name T, attrs ...FieldAttribute) *Field {
+	return &Field{
+		Name:      string(name),
+		fieldType: fieldTypeWithAttributes(FieldTypeSlice, attrs...),
+	}
+}
+
+// NewMapField creates a new map field using the provided name.
+func NewMapField[T ~string](name T, attrs ...FieldAttribute) *Field {
+	return &Field{
+		Name:      string(name),
+		fieldType: fieldTypeWithAttributes(FieldTypeMap, attrs...),
+	}
+}
+
+// NewAliasField creates a new alias field using the provided name.
+func NewAliasField[T ~string](name T, attrs ...FieldAttribute) *Field {
+	return &Field{
+		Name:      string(name),
+		fieldType: fieldTypeWithAttributes(FieldTypeAlias, attrs...),
+	}
+}

+ 0 - 0
pkg/filter21/ast/lexer.go → core/pkg/filter/ast/lexer.go


+ 0 - 0
pkg/filter21/ast/lexer_test.go → core/pkg/filter/ast/lexer_test.go


+ 9 - 0
pkg/filter21/ast/ops.go → core/pkg/filter/ast/ops.go

@@ -191,3 +191,12 @@ type ContainsSuffixOp struct {
 func (_ *ContainsSuffixOp) Op() FilterOp {
 	return FilterOpContainsSuffix
 }
+
+func Not(fn FilterNode) FilterNode {
+	return &NotOp{Operand: fn}
+}
+
+func IsVoid(fn FilterNode) bool {
+	_, ok := fn.(*VoidOp)
+	return ok
+}

+ 0 - 0
pkg/filter21/ast/parser.go → core/pkg/filter/ast/parser.go


+ 0 - 0
pkg/filter21/ast/tree.go → core/pkg/filter/ast/tree.go


+ 2 - 2
pkg/filter21/ast/walker.go → core/pkg/filter/ast/walker.go

@@ -5,7 +5,7 @@ import (
 	"sort"
 	"strings"
 
-	"github.com/opencost/opencost/pkg/filter21/util"
+	"github.com/opencost/opencost/core/pkg/filter/util"
 	"golang.org/x/text/cases"
 	"golang.org/x/text/language"
 )
@@ -167,7 +167,7 @@ func OpStringFor(node FilterNode, traversalState TraversalState, depth int) stri
 
 	switch n := node.(type) {
 	case *VoidOp:
-		open += ")"
+		open += "Empty }\n"
 	case *EqualOp:
 		open += fmt.Sprintf("Left: %s, Right: %s }\n", n.Left.String(), n.Right)
 	case *ContainsOp:

+ 0 - 0
pkg/filter21/ast/walker_test.go → core/pkg/filter/ast/walker_test.go


+ 0 - 0
pkg/filter21/cloudcost/fields.go → core/pkg/filter/cloudcost/fields.go


+ 1 - 1
pkg/filter21/cloudcost/parser.go → core/pkg/filter/cloudcost/parser.go

@@ -1,6 +1,6 @@
 package cloudcost
 
-import "github.com/opencost/opencost/pkg/filter21/ast"
+import "github.com/opencost/opencost/core/pkg/filter/ast"
 
 // a slice of all the cloud costs field instances the lexer should recognize as
 // valid left-hand comparators

+ 1 - 1
pkg/filter21/filter.go → core/pkg/filter/filter.go

@@ -1,6 +1,6 @@
 package filter
 
-import "github.com/opencost/opencost/pkg/filter21/ast"
+import "github.com/opencost/opencost/core/pkg/filter/ast"
 
 // Filter is just the root node of an AST. There are various compiler implementations
 // available to create data source specific filtering from the AST.

+ 1 - 1
pkg/filter/allcut.go → core/pkg/filter/legacy/allcut.go

@@ -1,4 +1,4 @@
-package filter
+package legacy
 
 // AllCut is a filter that matches nothing. This is useful
 // for applications like authorization, where a user/group/role may be disallowed

+ 1 - 1
pkg/filter/allpass.go → core/pkg/filter/legacy/allpass.go

@@ -1,4 +1,4 @@
-package filter
+package legacy
 
 // AllPass is a filter that matches everything and is the same as no filter. It is implemented here as a guard
 // against universal operations occurring in the absence of filters.

+ 1 - 1
pkg/filter/and.go → core/pkg/filter/legacy/and.go

@@ -1,4 +1,4 @@
-package filter
+package legacy
 
 import (
 	"fmt"

+ 23 - 23
pkg/filter/cloudcost/cloudcost.go → core/pkg/filter/legacy/cloudcost/cloudcost.go

@@ -4,10 +4,10 @@ import (
 	"reflect"
 	"strings"
 
-	"github.com/opencost/opencost/pkg/filter"
-	"github.com/opencost/opencost/pkg/kubecost"
-	"github.com/opencost/opencost/pkg/log"
-	"github.com/opencost/opencost/pkg/util/mapper"
+	filter "github.com/opencost/opencost/core/pkg/filter/legacy"
+	"github.com/opencost/opencost/core/pkg/log"
+	"github.com/opencost/opencost/core/pkg/opencost"
+	"github.com/opencost/opencost/core/pkg/util/mapper"
 )
 
 type CloudCostFilter struct {
@@ -33,7 +33,7 @@ func parseWildcardEnd(rawFilterValue string) (string, bool) {
 	return strings.TrimSuffix(rawFilterValue, "*"), strings.HasSuffix(rawFilterValue, "*")
 }
 
-func CloudCostFilterFromParams(pmr mapper.PrimitiveMapReader) filter.Filter[*kubecost.CloudCost] {
+func CloudCostFilterFromParams(pmr mapper.PrimitiveMapReader) filter.Filter[*opencost.CloudCost] {
 	ccFilter := convertFilterQueryParams(pmr)
 	return ParseCloudCostFilter(ccFilter)
 }
@@ -49,37 +49,37 @@ func convertFilterQueryParams(pmr mapper.PrimitiveMapReader) CloudCostFilter {
 		Services:         pmr.GetList("filterServices", ","),
 	}
 }
-func ParseCloudCostFilter(filters CloudCostFilter) filter.Filter[*kubecost.CloudCost] {
-	result := filter.And[*kubecost.CloudCost]{
-		Filters: []filter.Filter[*kubecost.CloudCost]{},
+func ParseCloudCostFilter(filters CloudCostFilter) filter.Filter[*opencost.CloudCost] {
+	result := filter.And[*opencost.CloudCost]{
+		Filters: []filter.Filter[*opencost.CloudCost]{},
 	}
 
 	if len(filters.InvoiceEntityIDs) > 0 {
-		result.Filters = append(result.Filters, filterV1SingleValueFromList(filters.InvoiceEntityIDs, kubecost.CloudCostInvoiceEntityIDProp))
+		result.Filters = append(result.Filters, filterV1SingleValueFromList(filters.InvoiceEntityIDs, opencost.CloudCostInvoiceEntityIDProp))
 	}
 
 	if len(filters.AccountIDs) > 0 {
-		result.Filters = append(result.Filters, filterV1SingleValueFromList(filters.AccountIDs, kubecost.CloudCostAccountIDProp))
+		result.Filters = append(result.Filters, filterV1SingleValueFromList(filters.AccountIDs, opencost.CloudCostAccountIDProp))
 	}
 
 	if len(filters.Providers) > 0 {
-		result.Filters = append(result.Filters, filterV1SingleValueFromList(filters.Providers, kubecost.CloudCostProviderProp))
+		result.Filters = append(result.Filters, filterV1SingleValueFromList(filters.Providers, opencost.CloudCostProviderProp))
 	}
 
 	if len(filters.ProviderIDs) > 0 {
-		result.Filters = append(result.Filters, filterV1SingleValueFromList(filters.ProviderIDs, kubecost.CloudCostProviderIDProp))
+		result.Filters = append(result.Filters, filterV1SingleValueFromList(filters.ProviderIDs, opencost.CloudCostProviderIDProp))
 	}
 
 	if len(filters.Services) > 0 {
-		result.Filters = append(result.Filters, filterV1SingleValueFromList(filters.Services, kubecost.CloudCostServiceProp))
+		result.Filters = append(result.Filters, filterV1SingleValueFromList(filters.Services, opencost.CloudCostServiceProp))
 	}
 
 	if len(filters.Categories) > 0 {
-		result.Filters = append(result.Filters, filterV1SingleValueFromList(filters.Categories, kubecost.CloudCostCategoryProp))
+		result.Filters = append(result.Filters, filterV1SingleValueFromList(filters.Categories, opencost.CloudCostCategoryProp))
 	}
 
 	if len(filters.Labels) > 0 {
-		result.Filters = append(result.Filters, filterV1DoubleValueFromList(filters.Labels, kubecost.CloudCostLabelProp))
+		result.Filters = append(result.Filters, filterV1DoubleValueFromList(filters.Labels, opencost.CloudCostLabelProp))
 	}
 
 	if len(result.Filters) == 0 {
@@ -89,16 +89,16 @@ func ParseCloudCostFilter(filters CloudCostFilter) filter.Filter[*kubecost.Cloud
 	return result
 }
 
-func filterV1SingleValueFromList(rawFilterValues []string, field string) filter.Filter[*kubecost.CloudCost] {
-	result := filter.Or[*kubecost.CloudCost]{
-		Filters: []filter.Filter[*kubecost.CloudCost]{},
+func filterV1SingleValueFromList(rawFilterValues []string, field string) filter.Filter[*opencost.CloudCost] {
+	result := filter.Or[*opencost.CloudCost]{
+		Filters: []filter.Filter[*opencost.CloudCost]{},
 	}
 
 	for _, filterValue := range rawFilterValues {
 		filterValue = strings.TrimSpace(filterValue)
 		filterValue, wildcard := parseWildcardEnd(filterValue)
 
-		subFilter := filter.StringProperty[*kubecost.CloudCost]{
+		subFilter := filter.StringProperty[*opencost.CloudCost]{
 			Field: field,
 			Op:    filter.StringEquals,
 			Value: filterValue,
@@ -119,9 +119,9 @@ func filterV1SingleValueFromList(rawFilterValues []string, field string) filter.
 //
 // The v1 query language (e.g. "filterLabels=app:foo,l2:bar") uses OR within
 // a field (e.g. label[app] = foo OR label[l2] = bar)
-func filterV1DoubleValueFromList(rawFilterValuesUnsplit []string, filterField string) filter.Filter[*kubecost.CloudCost] {
-	result := filter.Or[*kubecost.CloudCost]{
-		Filters: []filter.Filter[*kubecost.CloudCost]{},
+func filterV1DoubleValueFromList(rawFilterValuesUnsplit []string, filterField string) filter.Filter[*opencost.CloudCost] {
+	result := filter.Or[*opencost.CloudCost]{
+		Filters: []filter.Filter[*opencost.CloudCost]{},
 	}
 
 	for _, unsplit := range rawFilterValuesUnsplit {
@@ -135,7 +135,7 @@ func filterV1DoubleValueFromList(rawFilterValuesUnsplit []string, filterField st
 			val := strings.TrimSpace(split[1])
 			val, wildcard := parseWildcardEnd(val)
 
-			subFilter := filter.StringMapProperty[*kubecost.CloudCost]{
+			subFilter := filter.StringMapProperty[*opencost.CloudCost]{
 				Field: filterField,
 				// All v1 filters are equality comparisons
 				Op:    filter.StringMapEquals,

+ 0 - 0
pkg/filter/cloudcost/cloudcost_test.go → core/pkg/filter/legacy/cloudcost/cloudcost_test.go


+ 1 - 1
pkg/filter/filter.go → core/pkg/filter/legacy/filter.go

@@ -1,4 +1,4 @@
-package filter
+package legacy
 
 // Filter represents anything that can be used to filter given generic type T.
 //

Разница между файлами не показана из-за своего большого размера
+ 237 - 237
core/pkg/filter/legacy/filter_test.go


+ 1 - 1
pkg/filter/not.go → core/pkg/filter/legacy/not.go

@@ -1,4 +1,4 @@
-package filter
+package legacy
 
 import "fmt"
 

+ 1 - 1
pkg/filter/or.go → core/pkg/filter/legacy/or.go

@@ -1,4 +1,4 @@
-package filter
+package legacy
 
 import (
 	"fmt"

+ 2 - 2
pkg/filter/stringmapproperty.go → core/pkg/filter/legacy/stringmapproperty.go

@@ -1,10 +1,10 @@
-package filter
+package legacy
 
 import (
 	"fmt"
 	"strings"
 
-	"github.com/opencost/opencost/pkg/log"
+	"github.com/opencost/opencost/core/pkg/log"
 )
 
 const unallocatedSuffix = "__unallocated__"

+ 2 - 2
pkg/filter/stringproperty.go → core/pkg/filter/legacy/stringproperty.go

@@ -1,10 +1,10 @@
-package filter
+package legacy
 
 import (
 	"fmt"
 	"strings"
 
-	"github.com/opencost/opencost/pkg/log"
+	"github.com/opencost/opencost/core/pkg/log"
 )
 
 // StringPropertied is used to validate the name of a property field and return its value

+ 2 - 2
pkg/filter/stringsliceproperty.go → core/pkg/filter/legacy/stringsliceproperty.go

@@ -1,10 +1,10 @@
-package filter
+package legacy
 
 import (
 	"fmt"
 	"strings"
 
-	"github.com/opencost/opencost/pkg/log"
+	"github.com/opencost/opencost/core/pkg/log"
 )
 
 type StringSlicePropertied interface {

+ 3 - 3
pkg/filter/window.go → core/pkg/filter/legacy/window.go

@@ -1,4 +1,4 @@
-package filter
+package legacy
 
 //
 //import (
@@ -8,7 +8,7 @@ package filter
 //)
 //
 //type Windowed interface {
-//	GetWindow() kubecost.Window
+//	GetWindow() opencost.Window
 //}
 //
 //// WindowOperation are operations that can be performed on types that have windows
@@ -20,7 +20,7 @@ package filter
 //
 //// WindowCondition is a filter can be used on any type that has a window and implements GetWindow()
 //type WindowCondition[T Windowed] struct {
-//	Window kubecost.Window
+//	Window opencost.Window
 //	Op     WindowOperation
 //}
 //

+ 12 - 12
pkg/filter/window_test.go → core/pkg/filter/legacy/window_test.go

@@ -1,4 +1,4 @@
-package filter_test
+package legacy_test
 
 // import (
 // 	"github.com/opencost/opencost/pkg/kubecost"
@@ -7,15 +7,15 @@ package filter_test
 // )
 
 // type windowedImpl struct {
-// 	kubecost.Window
+// 	opencost.Window
 // }
 
-// func (w *windowedImpl) GetWindow() kubecost.Window {
+// func (w *windowedImpl) GetWindow() opencost.Window {
 // 	return w.Window
 // }
 
 // func newWindowedImpl(start, end *time.Time) *windowedImpl {
-// 	return &windowedImpl{kubecost.NewWindow(start, end)}
+// 	return &windowedImpl{opencost.NewWindow(start, end)}
 // }
 
 // func Test_WindowContains_Matches(t *testing.T) {
@@ -31,7 +31,7 @@ package filter_test
 // 		"fully contains": {
 // 			windowed: newWindowedImpl(&one, &two),
 // 			filter: WindowCondition[*windowedImpl]{
-// 				Window: kubecost.NewWindow(&noon, &three),
+// 				Window: opencost.NewWindow(&noon, &three),
 // 				Op:     WindowContains,
 // 			},
 
@@ -40,7 +40,7 @@ package filter_test
 // 		"window matches": {
 // 			windowed: newWindowedImpl(&one, &two),
 // 			filter: WindowCondition[*windowedImpl]{
-// 				Window: kubecost.NewWindow(&one, &two),
+// 				Window: opencost.NewWindow(&one, &two),
 // 				Op:     WindowContains,
 // 			},
 
@@ -49,7 +49,7 @@ package filter_test
 // 		"contains start": {
 // 			windowed: newWindowedImpl(&one, &three),
 // 			filter: WindowCondition[*windowedImpl]{
-// 				Window: kubecost.NewWindow(&noon, &two),
+// 				Window: opencost.NewWindow(&noon, &two),
 // 				Op:     WindowContains,
 // 			},
 
@@ -58,7 +58,7 @@ package filter_test
 // 		"contains end": {
 // 			windowed: newWindowedImpl(&noon, &two),
 // 			filter: WindowCondition[*windowedImpl]{
-// 				Window: kubecost.NewWindow(&one, &three),
+// 				Window: opencost.NewWindow(&one, &three),
 // 				Op:     WindowContains,
 // 			},
 
@@ -67,7 +67,7 @@ package filter_test
 // 		"window start = filter end": {
 // 			windowed: newWindowedImpl(&one, &two),
 // 			filter: WindowCondition[*windowedImpl]{
-// 				Window: kubecost.NewWindow(&noon, &one),
+// 				Window: opencost.NewWindow(&noon, &one),
 // 				Op:     WindowContains,
 // 			},
 
@@ -76,7 +76,7 @@ package filter_test
 // 		"window end = filter start": {
 // 			windowed: newWindowedImpl(&noon, &one),
 // 			filter: WindowCondition[*windowedImpl]{
-// 				Window: kubecost.NewWindow(&one, &two),
+// 				Window: opencost.NewWindow(&one, &two),
 // 				Op:     WindowContains,
 // 			},
 
@@ -85,7 +85,7 @@ package filter_test
 // 		"window before": {
 // 			windowed: newWindowedImpl(&noon, &one),
 // 			filter: WindowCondition[*windowedImpl]{
-// 				Window: kubecost.NewWindow(&two, &three),
+// 				Window: opencost.NewWindow(&two, &three),
 // 				Op:     WindowContains,
 // 			},
 
@@ -94,7 +94,7 @@ package filter_test
 // 		"window after": {
 // 			windowed: newWindowedImpl(&two, &three),
 // 			filter: WindowCondition[*windowedImpl]{
-// 				Window: kubecost.NewWindow(&noon, &one),
+// 				Window: opencost.NewWindow(&noon, &one),
 // 				Op:     WindowContains,
 // 			},
 

+ 0 - 0
pkg/filter21/matcher/allcut.go → core/pkg/filter/matcher/allcut.go


+ 0 - 0
pkg/filter21/matcher/allpass.go → core/pkg/filter/matcher/allpass.go


+ 0 - 0
pkg/filter21/matcher/and.go → core/pkg/filter/matcher/and.go


+ 3 - 3
pkg/filter21/matcher/compiler.go → core/pkg/filter/matcher/compiler.go

@@ -3,9 +3,9 @@ package matcher
 import (
 	"fmt"
 
-	"github.com/opencost/opencost/pkg/filter21/ast"
-	"github.com/opencost/opencost/pkg/filter21/transform"
-	"github.com/opencost/opencost/pkg/filter21/util"
+	"github.com/opencost/opencost/core/pkg/filter/ast"
+	"github.com/opencost/opencost/core/pkg/filter/transform"
+	"github.com/opencost/opencost/core/pkg/filter/util"
 )
 
 // FieldMapper is the adapter which can fetch actual T instance data of type U

+ 0 - 0
pkg/filter21/matcher/matcher.go → core/pkg/filter/matcher/matcher.go


+ 4 - 4
pkg/filter21/matcher/matcher_test.go → core/pkg/filter/matcher/matcher_test.go

@@ -5,10 +5,10 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/opencost/opencost/pkg/filter21/allocation"
-	"github.com/opencost/opencost/pkg/filter21/ast"
-	"github.com/opencost/opencost/pkg/filter21/matcher"
-	"github.com/opencost/opencost/pkg/filter21/transform"
+	"github.com/opencost/opencost/core/pkg/filter/allocation"
+	"github.com/opencost/opencost/core/pkg/filter/ast"
+	"github.com/opencost/opencost/core/pkg/filter/matcher"
+	"github.com/opencost/opencost/core/pkg/filter/transform"
 )
 
 // MatcherCompiler for Allocation instances providing functions which map identifers

+ 0 - 0
pkg/filter21/matcher/not.go → core/pkg/filter/matcher/not.go


+ 0 - 0
pkg/filter21/matcher/or.go → core/pkg/filter/matcher/or.go


+ 2 - 2
pkg/filter21/matcher/stringmapmatcher.go → core/pkg/filter/matcher/stringmapmatcher.go

@@ -4,8 +4,8 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/opencost/opencost/pkg/filter21/ast"
-	"github.com/opencost/opencost/pkg/log"
+	"github.com/opencost/opencost/core/pkg/filter/ast"
+	"github.com/opencost/opencost/core/pkg/log"
 )
 
 // StringMapMatcherFactory leverages a single MapFieldMapper[T] to generate instances of

+ 2 - 2
pkg/filter21/matcher/stringmatcher.go → core/pkg/filter/matcher/stringmatcher.go

@@ -4,8 +4,8 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/opencost/opencost/pkg/filter21/ast"
-	"github.com/opencost/opencost/pkg/log"
+	"github.com/opencost/opencost/core/pkg/filter/ast"
+	"github.com/opencost/opencost/core/pkg/log"
 )
 
 // StringMatcherFactory leverages a single StringFieldMapper[T] to generate instances of

+ 2 - 2
pkg/filter21/matcher/stringslicematcher.go → core/pkg/filter/matcher/stringslicematcher.go

@@ -4,8 +4,8 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/opencost/opencost/pkg/filter21/ast"
-	"github.com/opencost/opencost/pkg/log"
+	"github.com/opencost/opencost/core/pkg/filter/ast"
+	"github.com/opencost/opencost/core/pkg/log"
 )
 
 // StringMatcherFactory leverages a single StringSliceFieldMapper[T] to generate instances of

+ 11 - 5
pkg/filter21/ops/ops.go → core/pkg/filter/ops/ops.go

@@ -9,10 +9,11 @@ import (
 	"reflect"
 	"strings"
 
-	"github.com/opencost/opencost/pkg/filter21/allocation"
-	"github.com/opencost/opencost/pkg/filter21/asset"
-	"github.com/opencost/opencost/pkg/filter21/ast"
-	"github.com/opencost/opencost/pkg/util/typeutil"
+	"github.com/opencost/opencost/core/pkg/filter/allocation"
+	"github.com/opencost/opencost/core/pkg/filter/asset"
+	"github.com/opencost/opencost/core/pkg/filter/ast"
+	"github.com/opencost/opencost/core/pkg/filter/cloudcost"
+	"github.com/opencost/opencost/core/pkg/util/typeutil"
 )
 
 // keyFieldType is used to extract field, key, and field type
@@ -25,12 +26,17 @@ type keyFieldType interface {
 // This is somewhat of a fancy solution, but allows us to "register" DefaultFieldByName funcs
 // funcs by Field type.
 var defaultFieldByType = map[string]any{
-	// typeutil.TypeOf[cloud.CloudAggregationField]():        cloud.DefaultFieldByName,
 	typeutil.TypeOf[allocation.AllocationField](): allocation.DefaultFieldByName,
 	typeutil.TypeOf[asset.AssetField]():           asset.DefaultFieldByName,
+	typeutil.TypeOf[cloudcost.CloudCostField]():   cloudcost.DefaultFieldByName,
 	// typeutil.TypeOf[containerstats.ContainerStatsField](): containerstats.DefaultFieldByName,
 }
 
+// RegisterDefaultFieldLookup registers a function that can be used to lookup a specific field type.
+func RegisterDefaultFieldLookup[T ~string](lookup func(T) *ast.Field) {
+	defaultFieldByType[typeutil.TypeOf[T]()] = lookup
+}
+
 // asField looks up a specific T field instance by name and returns the default
 // ast.Field value for that type.
 func asField[T ~string](field T) *ast.Field {

+ 5 - 5
pkg/filter21/ops/ops_test.go → core/pkg/filter/ops/ops_test.go

@@ -4,9 +4,9 @@ import (
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
-	"github.com/opencost/opencost/pkg/filter21/allocation"
-	"github.com/opencost/opencost/pkg/filter21/ast"
-	"github.com/opencost/opencost/pkg/filter21/ops"
+	"github.com/opencost/opencost/core/pkg/filter/allocation"
+	"github.com/opencost/opencost/core/pkg/filter/ast"
+	"github.com/opencost/opencost/core/pkg/filter/ops"
 )
 
 func TestBasicOpsBuilder(t *testing.T) {
@@ -23,8 +23,8 @@ func TestBasicOpsBuilder(t *testing.T) {
 	)
 
 	otherTree, err := parser.Parse(`
-		(namespace: "kubecost" | cluster: "cluster-one") +
-		services!~:"service-a" +
+		(namespace: "kubecost" | cluster: "cluster-one") + 
+		services!~:"service-a" + 
 		label[app]!: "cost-analyzer" +
 		label~:"foo"
 	`)

+ 1 - 4
pkg/filter21/transform/pass.go → core/pkg/filter/transform/pass.go

@@ -3,7 +3,7 @@ package transform
 import (
 	"fmt"
 
-	"github.com/opencost/opencost/pkg/filter21/ast"
+	"github.com/opencost/opencost/core/pkg/filter/ast"
 )
 
 // CompilerPass is an interface which defines an implementation capable of
@@ -16,9 +16,6 @@ type CompilerPass interface {
 	Exec(filter ast.FilterNode) (ast.FilterNode, error)
 }
 
-// func CompilerPass(transformFunc func(ast.FilterNode) (ast.FilterNode, error)) (ast.FilterNode, error) {
-// }
-
 // ApplyAll applies all the compiler passes serially and returns the resulting
 // tree. This method copies the passes AST before executing the compiler passes.
 func ApplyAll(filter ast.FilterNode, passes []CompilerPass) (ast.FilterNode, error) {

+ 1 - 1
pkg/filter21/transform/promlabels.go → core/pkg/filter/transform/promlabels.go

@@ -3,7 +3,7 @@ package transform
 import (
 	"regexp"
 
-	"github.com/opencost/opencost/pkg/filter21/ast"
+	"github.com/opencost/opencost/core/pkg/filter/ast"
 )
 
 // regex for invalid prometheus label characters

+ 1 - 1
pkg/filter21/transform/unallocated.go → core/pkg/filter/transform/unallocated.go

@@ -1,6 +1,6 @@
 package transform
 
-import "github.com/opencost/opencost/pkg/filter21/ast"
+import "github.com/opencost/opencost/core/pkg/filter/ast"
 
 const unallocatedSuffix = "__unallocated__"
 

+ 0 - 0
pkg/filter21/util/stack.go → core/pkg/filter/util/stack.go


+ 0 - 0
pkg/log/counter.go → core/pkg/log/counter.go


+ 0 - 0
pkg/log/counter_test.go → core/pkg/log/counter_test.go


+ 0 - 0
pkg/log/log.go → core/pkg/log/log.go


+ 0 - 0
pkg/log/profiler.go → core/pkg/log/profiler.go


+ 11 - 11
pkg/kubecost/allocation.go → core/pkg/opencost/allocation.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 import (
 	"fmt"
@@ -7,12 +7,12 @@ import (
 	"strings"
 	"time"
 
-	filter21 "github.com/opencost/opencost/pkg/filter21"
-	"github.com/opencost/opencost/pkg/filter21/ast"
-	"github.com/opencost/opencost/pkg/filter21/matcher"
-	"github.com/opencost/opencost/pkg/log"
-	"github.com/opencost/opencost/pkg/util"
-	"github.com/opencost/opencost/pkg/util/timeutil"
+	"github.com/opencost/opencost/core/pkg/filter"
+	"github.com/opencost/opencost/core/pkg/filter/ast"
+	"github.com/opencost/opencost/core/pkg/filter/matcher"
+	"github.com/opencost/opencost/core/pkg/log"
+	"github.com/opencost/opencost/core/pkg/util"
+	"github.com/opencost/opencost/core/pkg/util/timeutil"
 	"golang.org/x/exp/slices"
 )
 
@@ -93,11 +93,11 @@ type Allocation struct {
 	// and appended to an Allocation, and so by default is is nil.
 	ProportionalAssetResourceCosts ProportionalAssetResourceCosts `json:"proportionalAssetResourceCosts"` //@bingen:field[ignore]
 	SharedCostBreakdown            SharedCostBreakdowns           `json:"sharedCostBreakdown"`            //@bingen:field[ignore]
-	LoadBalancers                  LbAllocations                  `json:"LoadBalancers"`                  // @bingen:field[version=18]
+	LoadBalancers                  LbAllocations                  `json:"LoadBalancers"`                  //@bingen:field[version=18]
 	// UnmountedPVCost is used to track how much of the cost in PVs is for an
 	// unmounted PV. It is not additive of PVCost() and need not be sent in API
 	// responses.
-	UnmountedPVCost float64 `json:"-"`
+	UnmountedPVCost float64 `json:"-"` //@bingen:field[ignore]
 }
 
 type LbAllocations map[string]*LbAllocation
@@ -1303,14 +1303,14 @@ func NewAllocationSet(start, end time.Time, allocs ...*Allocation) *AllocationSe
 // simple flag for sharing idle resources.
 type AllocationAggregationOptions struct {
 	AllocationTotalsStore                 AllocationTotalsStore
-	Filter                                filter21.Filter
+	Filter                                filter.Filter
 	IdleByNode                            bool
 	IncludeProportionalAssetResourceCosts bool
 	LabelConfig                           *LabelConfig
 	MergeUnallocated                      bool
 	Reconcile                             bool
 	ReconcileNetwork                      bool
-	Share                                 filter21.Filter
+	Share                                 filter.Filter
 	SharedNamespaces                      []string
 	SharedLabels                          map[string][]string
 	ShareIdle                             string

+ 2 - 2
pkg/kubecost/allocation_json.go → core/pkg/opencost/allocation_json.go

@@ -1,11 +1,11 @@
-package kubecost
+package opencost
 
 import (
 	"fmt"
 	"math"
 	"time"
 
-	"github.com/opencost/opencost/pkg/util/json"
+	"github.com/opencost/opencost/core/pkg/util/json"
 )
 
 // AllocationJSON  exists because there are expected JSON response fields

+ 2 - 2
pkg/kubecost/allocation_json_test.go → core/pkg/opencost/allocation_json_test.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 import (
 	"encoding/json"
@@ -6,7 +6,7 @@ import (
 	"testing"
 	"time"
 
-	"github.com/opencost/opencost/pkg/util/mathutil"
+	"github.com/opencost/opencost/core/pkg/util/mathutil"
 )
 
 func TestAllocation_MarshalJSON(t *testing.T) {

+ 13 - 14
pkg/kubecost/allocation_test.go → core/pkg/opencost/allocation_test.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 import (
 	"fmt"
@@ -9,23 +9,22 @@ import (
 	"time"
 
 	"github.com/davecgh/go-spew/spew"
-	filter21 "github.com/opencost/opencost/pkg/filter21"
-	"github.com/opencost/opencost/pkg/filter21/allocation"
-	afilter "github.com/opencost/opencost/pkg/filter21/allocation"
-	"github.com/opencost/opencost/pkg/filter21/ast"
-	"github.com/opencost/opencost/pkg/filter21/ops"
-	"github.com/opencost/opencost/pkg/log"
-	"github.com/opencost/opencost/pkg/util"
-	"github.com/opencost/opencost/pkg/util/json"
-	"github.com/opencost/opencost/pkg/util/timeutil"
+	"github.com/opencost/opencost/core/pkg/filter"
+	"github.com/opencost/opencost/core/pkg/filter/allocation"
+	"github.com/opencost/opencost/core/pkg/filter/ast"
+	"github.com/opencost/opencost/core/pkg/filter/ops"
+	"github.com/opencost/opencost/core/pkg/log"
+	"github.com/opencost/opencost/core/pkg/util"
+	"github.com/opencost/opencost/core/pkg/util/json"
+	"github.com/opencost/opencost/core/pkg/util/timeutil"
 )
 
-var filterParser = afilter.NewAllocationFilterParser()
+var filterParser = allocation.NewAllocationFilterParser()
 var matcherCompiler = NewAllocationMatchCompiler(nil)
 
 // useful for creating filters on the fly when testing. panics
 // on parse errors!
-func mustParseFilter(s string) filter21.Filter {
+func mustParseFilter(s string) filter.Filter {
 	filter, err := filterParser.Parse(s)
 	if err != nil {
 		panic(err)
@@ -3411,7 +3410,7 @@ func Test_AggregateByService_UnmountedLBs(t *testing.T) {
 	set.Insert(idle)
 
 	set.AggregateBy([]string{AllocationServiceProp}, &AllocationAggregationOptions{
-		Filter: ops.Contains(afilter.FieldServices, "nginx-plus-nginx-ingress"),
+		Filter: ops.Contains(allocation.FieldServices, "nginx-plus-nginx-ingress"),
 	})
 
 	for _, alloc := range set.Allocations {
@@ -3653,7 +3652,7 @@ func TestIsFilterEmptyTrue(t *testing.T) {
 
 func TestIsFilterEmptyFalse(t *testing.T) {
 	compiler := NewAllocationMatchCompiler(nil)
-	matcher, err := compiler.Compile(ops.Eq(afilter.FieldClusterID, "test"))
+	matcher, err := compiler.Compile(ops.Eq(allocation.FieldClusterID, "test"))
 	if err != nil {
 		t.Fatalf("compiling nil filter: %s", err)
 	}

+ 5 - 5
pkg/kubecost/allocationfilter_test.go → core/pkg/opencost/allocationfilter_test.go

@@ -1,12 +1,12 @@
-package kubecost
+package opencost
 
 import (
 	"testing"
 
-	filter21 "github.com/opencost/opencost/pkg/filter21"
-	afilter "github.com/opencost/opencost/pkg/filter21/allocation"
-	"github.com/opencost/opencost/pkg/filter21/ast"
-	"github.com/opencost/opencost/pkg/filter21/ops"
+	filter21 "github.com/opencost/opencost/core/pkg/filter"
+	afilter "github.com/opencost/opencost/core/pkg/filter/allocation"
+	"github.com/opencost/opencost/core/pkg/filter/ast"
+	"github.com/opencost/opencost/core/pkg/filter/ops"
 )
 
 func Test_AllocationFilterCondition_Matches(t *testing.T) {

+ 6 - 6
pkg/kubecost/allocationmatcher.go → core/pkg/opencost/allocationmatcher.go

@@ -1,13 +1,13 @@
-package kubecost
+package opencost
 
 import (
 	"fmt"
 
-	afilter "github.com/opencost/opencost/pkg/filter21/allocation"
-	"github.com/opencost/opencost/pkg/filter21/ast"
-	"github.com/opencost/opencost/pkg/filter21/matcher"
-	"github.com/opencost/opencost/pkg/filter21/ops"
-	"github.com/opencost/opencost/pkg/filter21/transform"
+	afilter "github.com/opencost/opencost/core/pkg/filter/allocation"
+	"github.com/opencost/opencost/core/pkg/filter/ast"
+	"github.com/opencost/opencost/core/pkg/filter/matcher"
+	"github.com/opencost/opencost/core/pkg/filter/ops"
+	"github.com/opencost/opencost/core/pkg/filter/transform"
 )
 
 // AllocationMatcher is a matcher implementation for Allocation instances,

+ 4 - 4
pkg/kubecost/allocationmatcher_test.go → core/pkg/opencost/allocationmatcher_test.go

@@ -1,12 +1,12 @@
-package kubecost
+package opencost
 
 import (
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
-	afilter "github.com/opencost/opencost/pkg/filter21/allocation"
-	"github.com/opencost/opencost/pkg/filter21/ast"
-	"github.com/opencost/opencost/pkg/filter21/ops"
+	afilter "github.com/opencost/opencost/core/pkg/filter/allocation"
+	"github.com/opencost/opencost/core/pkg/filter/ast"
+	"github.com/opencost/opencost/core/pkg/filter/ops"
 )
 
 func TestAliasPass(t *testing.T) {

+ 111 - 29
pkg/kubecost/allocationprops.go → core/pkg/opencost/allocationprops.go

@@ -1,39 +1,121 @@
-package kubecost
+package opencost
 
 import (
 	"fmt"
 	"sort"
 	"strings"
 
-	"github.com/opencost/opencost/pkg/log"
-	"github.com/opencost/opencost/pkg/prom"
+	"github.com/opencost/opencost/core/pkg/log"
+	"github.com/opencost/opencost/core/pkg/util/promutil"
 )
 
+// AllocationProperty represents a specific property on an allocation, which
+// provides utility for extracting custom property metadata.
+type AllocationProperty string
+
+// IsLabel returns true if the allocation property has a label prefix
+func (apt *AllocationProperty) IsLabel() bool {
+	return strings.HasPrefix(string(*apt), "label:")
+}
+
+// GetLabel returns the label string associated with the label property if it exists.
+// Otherwise, empty string is returned.
+func (apt *AllocationProperty) GetLabel() string {
+	if apt.IsLabel() {
+		return strings.TrimSpace(strings.TrimPrefix(string(*apt), "label:"))
+	}
+	return ""
+}
+
+// IsAnnotation returns true if the allocation property has an annotation prefix
+func (apt *AllocationProperty) IsAnnotation() bool {
+	return strings.HasPrefix(string(*apt), "annotation:")
+}
+
+// GetAnnotation returns the annotation string associated with the property if it exists.
+// Otherwise, empty string is returned.
+func (apt *AllocationProperty) GetAnnotation() string {
+	if apt.IsAnnotation() {
+		return strings.TrimSpace(strings.TrimPrefix(string(*apt), "annotation:"))
+	}
+	return ""
+}
+
+// IsAliasedLabel returns true if the allocation property corresponds to an aliased label
+func (apt *AllocationProperty) IsAliasedLabel() bool {
+	if apt == nil {
+		return false
+	}
+
+	return *apt == AllocationDepartmentProp ||
+		*apt == AllocationEnvironmentProp ||
+		*apt == AllocationOwnerProp ||
+		*apt == AllocationProductProp ||
+		*apt == AllocationTeamProp
+}
+
+// GetAliasedLabelDefault returns the corresponding default aliased label name
+func (apt *AllocationProperty) GetAliasedLabelDefault() string {
+	switch *apt {
+	case AllocationDepartmentProp:
+		return "department"
+	case AllocationEnvironmentProp:
+		return "env"
+	case AllocationOwnerProp:
+		return "owner"
+	case AllocationProductProp:
+		return "app"
+	case AllocationTeamProp:
+		return "team"
+	default:
+		return ""
+	}
+}
+
 const (
-	AllocationNilProp            string = ""
-	AllocationClusterProp        string = "cluster"
-	AllocationNodeProp           string = "node"
-	AllocationContainerProp      string = "container"
-	AllocationControllerProp     string = "controller"
-	AllocationControllerKindProp string = "controllerKind"
-	AllocationNamespaceProp      string = "namespace"
-	AllocationPodProp            string = "pod"
-	AllocationProviderIDProp     string = "providerID"
-	AllocationServiceProp        string = "service"
-	AllocationLabelProp          string = "label"
-	AllocationAnnotationProp     string = "annotation"
-	AllocationDeploymentProp     string = "deployment"
-	AllocationStatefulSetProp    string = "statefulset"
-	AllocationDaemonSetProp      string = "daemonset"
-	AllocationJobProp            string = "job"
-	AllocationDepartmentProp     string = "department"
-	AllocationEnvironmentProp    string = "environment"
-	AllocationOwnerProp          string = "owner"
-	AllocationProductProp        string = "product"
-	AllocationTeamProp           string = "team"
+	AllocationNilProp            AllocationProperty = ""
+	AllocationClusterProp                           = "cluster"
+	AllocationNodeProp                              = "node"
+	AllocationContainerProp                         = "container"
+	AllocationControllerProp                        = "controller"
+	AllocationControllerKindProp                    = "controllerKind"
+	AllocationNamespaceProp                         = "namespace"
+	AllocationPodProp                               = "pod"
+	AllocationProviderIDProp                        = "providerID"
+	AllocationServiceProp                           = "service"
+	AllocationLabelProp                             = "label"
+	AllocationAnnotationProp                        = "annotation"
+	AllocationDeploymentProp                        = "deployment"
+	AllocationStatefulSetProp                       = "statefulset"
+	AllocationDaemonSetProp                         = "daemonset"
+	AllocationJobProp                               = "job"
+	AllocationDepartmentProp                        = "department"
+	AllocationEnvironmentProp                       = "environment"
+	AllocationOwnerProp                             = "owner"
+	AllocationProductProp                           = "product"
+	AllocationTeamProp                              = "team"
 )
 
-func ParseProperty(text string) (string, error) {
+func ParseProperties(props []string) ([]AllocationProperty, error) {
+	properties := []AllocationProperty{}
+	added := make(map[AllocationProperty]struct{})
+
+	for _, prop := range props {
+		property, err := ParseProperty(prop)
+		if err != nil {
+			return nil, fmt.Errorf("Failed to parse property: %w", err)
+		}
+
+		if _, ok := added[property]; !ok {
+			added[property] = struct{}{}
+			properties = append(properties, property)
+		}
+	}
+
+	return properties, nil
+}
+
+func ParseProperty(text string) (AllocationProperty, error) {
 	switch strings.TrimSpace(strings.ToLower(text)) {
 	case "cluster":
 		return AllocationClusterProp, nil
@@ -78,13 +160,13 @@ func ParseProperty(text string) (string, error) {
 	}
 
 	if strings.HasPrefix(text, "label:") {
-		label := prom.SanitizeLabelName(strings.TrimSpace(strings.TrimPrefix(text, "label:")))
-		return fmt.Sprintf("label:%s", label), nil
+		label := promutil.SanitizeLabelName(strings.TrimSpace(strings.TrimPrefix(text, "label:")))
+		return AllocationProperty(fmt.Sprintf("label:%s", label)), nil
 	}
 
 	if strings.HasPrefix(text, "annotation:") {
-		annotation := prom.SanitizeLabelName(strings.TrimSpace(strings.TrimPrefix(text, "annotation:")))
-		return fmt.Sprintf("annotation:%s", annotation), nil
+		annotation := promutil.SanitizeLabelName(strings.TrimSpace(strings.TrimPrefix(text, "annotation:")))
+		return AllocationProperty(fmt.Sprintf("annotation:%s", annotation)), nil
 	}
 
 	return AllocationNilProp, fmt.Errorf("invalid allocation property: %s", text)

+ 1 - 1
pkg/kubecost/allocationprops_test.go → core/pkg/opencost/allocationprops_test.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 import (
 	"reflect"

+ 9 - 9
pkg/kubecost/asset.go → core/pkg/opencost/asset.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 import (
 	"encoding"
@@ -7,13 +7,13 @@ import (
 	"strings"
 	"time"
 
-	filter21 "github.com/opencost/opencost/pkg/filter21"
-	"github.com/opencost/opencost/pkg/filter21/ast"
-	"github.com/opencost/opencost/pkg/filter21/matcher"
-	"github.com/opencost/opencost/pkg/log"
-	"github.com/opencost/opencost/pkg/prom"
-	"github.com/opencost/opencost/pkg/util/json"
-	"github.com/opencost/opencost/pkg/util/timeutil"
+	filter21 "github.com/opencost/opencost/core/pkg/filter"
+	"github.com/opencost/opencost/core/pkg/filter/ast"
+	"github.com/opencost/opencost/core/pkg/filter/matcher"
+	"github.com/opencost/opencost/core/pkg/log"
+	"github.com/opencost/opencost/core/pkg/util/json"
+	"github.com/opencost/opencost/core/pkg/util/promutil"
+	"github.com/opencost/opencost/core/pkg/util/timeutil"
 )
 
 // UndefinedKey is used in composing Asset group keys if the group does not have that property defined.
@@ -4139,7 +4139,7 @@ func GetNodePoolName(provider string, labels map[string]string) string {
 }
 
 func getPoolNameHelper(label string, labels map[string]string) string {
-	sanitizedLabel := prom.SanitizeLabelName(label)
+	sanitizedLabel := promutil.SanitizeLabelName(label)
 	if poolName, found := labels[sanitizedLabel]; found {
 		return poolName
 	} else {

+ 2 - 2
pkg/kubecost/asset_json.go → core/pkg/opencost/asset_json.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 import (
 	"bytes"
@@ -6,7 +6,7 @@ import (
 	"reflect"
 	"time"
 
-	"github.com/opencost/opencost/pkg/util/json"
+	"github.com/opencost/opencost/core/pkg/util/json"
 )
 
 // Encoding and decoding logic for Asset types

+ 2 - 2
pkg/kubecost/asset_json_test.go → core/pkg/opencost/asset_json_test.go

@@ -1,7 +1,7 @@
-package kubecost
+package opencost
 
 import (
-	"github.com/opencost/opencost/pkg/util/json"
+	"github.com/opencost/opencost/core/pkg/util/json"
 
 	"testing"
 	"time"

+ 2 - 2
pkg/kubecost/asset_test.go → core/pkg/opencost/asset_test.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 import (
 	"encoding/json"
@@ -8,7 +8,7 @@ import (
 	"testing"
 	"time"
 
-	"github.com/opencost/opencost/pkg/util"
+	"github.com/opencost/opencost/core/pkg/util"
 )
 
 var start1 = time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC)

+ 5 - 5
pkg/kubecost/assetmatcher.go → core/pkg/opencost/assetmatcher.go

@@ -1,13 +1,13 @@
-package kubecost
+package opencost
 
 import (
 	"fmt"
 	"strings"
 
-	afilter "github.com/opencost/opencost/pkg/filter21/asset"
-	"github.com/opencost/opencost/pkg/filter21/ast"
-	"github.com/opencost/opencost/pkg/filter21/matcher"
-	"github.com/opencost/opencost/pkg/filter21/transform"
+	afilter "github.com/opencost/opencost/core/pkg/filter/asset"
+	"github.com/opencost/opencost/core/pkg/filter/ast"
+	"github.com/opencost/opencost/core/pkg/filter/matcher"
+	"github.com/opencost/opencost/core/pkg/filter/transform"
 )
 
 // AssetMatcher is a matcher implementation for Asset instances,

+ 40 - 1
pkg/kubecost/assetprops.go → core/pkg/opencost/assetprops.go

@@ -1,8 +1,10 @@
-package kubecost
+package opencost
 
 import (
 	"fmt"
 	"strings"
+
+	"github.com/opencost/opencost/core/pkg/util/promutil"
 )
 
 // AssetProperty is a kind of property belonging to an Asset
@@ -42,6 +44,9 @@ const (
 	// AssetTypeProp describes the type of the Asset
 	AssetTypeProp AssetProperty = "type"
 
+	// AssetLabelProp describes a single label within an Asset.
+	AssetLabelProp AssetProperty = "label"
+
 	// AssetDepartmentProp describes the department of the Asset
 	AssetDepartmentProp AssetProperty = "department"
 
@@ -58,6 +63,34 @@ const (
 	AssetTeamProp AssetProperty = "team"
 )
 
+// IsLabel returns true if the allocation property has a label prefix
+func (apt *AssetProperty) IsLabel() bool {
+	return strings.HasPrefix(string(*apt), "label:")
+}
+
+// GetLabel returns the label string associated with the label property if it exists.
+// Otherwise, empty string is returned.
+func (apt *AssetProperty) GetLabel() string {
+	if apt.IsLabel() {
+		return strings.TrimSpace(strings.TrimPrefix(string(*apt), "label:"))
+	}
+	return ""
+}
+
+func ParseAssetProperties(values []string) ([]AssetProperty, error) {
+	props := []AssetProperty{}
+
+	for _, value := range values {
+		p, err := ParseAssetProperty(value)
+		if err != nil {
+			return nil, err
+		}
+		props = append(props, p)
+	}
+
+	return props, nil
+}
+
 // ParseAssetProperty attempts to parse a string into an AssetProperty
 func ParseAssetProperty(text string) (AssetProperty, error) {
 	switch strings.TrimSpace(strings.ToLower(text)) {
@@ -90,6 +123,12 @@ func ParseAssetProperty(text string) (AssetProperty, error) {
 	case "team":
 		return AssetTeamProp, nil
 	}
+
+	if strings.HasPrefix(text, "label:") {
+		label := promutil.SanitizeLabelName(strings.TrimSpace(strings.TrimPrefix(text, "label:")))
+		return AssetProperty(fmt.Sprintf("label:%s", label)), nil
+	}
+
 	return AssetNilProp, fmt.Errorf("invalid asset property: %s", text)
 }
 

+ 2 - 2
pkg/kubecost/bingen.go → core/pkg/opencost/bingen.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 ////////////////////////////////////////////////////////////////////////////////
 // NOTE: If you add fields to _any_ struct that is serialized by bingen, please
@@ -71,4 +71,4 @@ package kubecost
 // @bingen:generate:CloudCostLabels
 // @bingen:end
 
-//go:generate bingen -package=kubecost -version=17 -buffer=github.com/opencost/opencost/pkg/util
+//go:generate bingen -package=opencost -version=17 -buffer=github.com/opencost/opencost/core/pkg/util

+ 8 - 8
pkg/kubecost/cloudcost.go → core/pkg/opencost/cloudcost.go

@@ -1,15 +1,15 @@
-package kubecost
+package opencost
 
 import (
 	"errors"
 	"fmt"
 	"time"
 
-	"github.com/opencost/opencost/pkg/filter"
-	filter21 "github.com/opencost/opencost/pkg/filter21"
-	"github.com/opencost/opencost/pkg/filter21/ast"
-	"github.com/opencost/opencost/pkg/log"
-	"github.com/opencost/opencost/pkg/util/timeutil"
+	"github.com/opencost/opencost/core/pkg/filter"
+	"github.com/opencost/opencost/core/pkg/filter/ast"
+	legacyfilter "github.com/opencost/opencost/core/pkg/filter/legacy"
+	"github.com/opencost/opencost/core/pkg/log"
+	"github.com/opencost/opencost/core/pkg/util/timeutil"
 )
 
 // CloudCost represents a CUR line item, identifying a cloud resource and
@@ -279,7 +279,7 @@ func (ccs *CloudCostSet) Equal(that *CloudCostSet) bool {
 	return true
 }
 
-func (ccs *CloudCostSet) Filter(filters filter.Filter[*CloudCost]) *CloudCostSet {
+func (ccs *CloudCostSet) Filter(filters legacyfilter.Filter[*CloudCost]) *CloudCostSet {
 	if ccs == nil {
 		return nil
 	}
@@ -299,7 +299,7 @@ func (ccs *CloudCostSet) Filter(filters filter.Filter[*CloudCost]) *CloudCostSet
 	return result
 }
 
-func (ccs *CloudCostSet) Filter21(filters filter21.Filter) (*CloudCostSet, error) {
+func (ccs *CloudCostSet) Filter21(filters filter.Filter) (*CloudCostSet, error) {
 	if ccs == nil {
 		return nil, nil
 	}

+ 2 - 2
pkg/kubecost/cloudcost_test.go → core/pkg/opencost/cloudcost_test.go

@@ -1,10 +1,10 @@
-package kubecost
+package opencost
 
 import (
 	"testing"
 	"time"
 
-	"github.com/opencost/opencost/pkg/util/timeutil"
+	"github.com/opencost/opencost/core/pkg/util/timeutil"
 )
 
 var ccProperties1 = &CloudCostProperties{

+ 5 - 5
pkg/kubecost/cloudcostmatcher.go → core/pkg/opencost/cloudcostmatcher.go

@@ -1,12 +1,12 @@
-package kubecost
+package opencost
 
 import (
 	"fmt"
 
-	"github.com/opencost/opencost/pkg/filter21/ast"
-	ccfilter "github.com/opencost/opencost/pkg/filter21/cloudcost"
-	"github.com/opencost/opencost/pkg/filter21/matcher"
-	"github.com/opencost/opencost/pkg/filter21/transform"
+	"github.com/opencost/opencost/core/pkg/filter/ast"
+	ccfilter "github.com/opencost/opencost/core/pkg/filter/cloudcost"
+	"github.com/opencost/opencost/core/pkg/filter/matcher"
+	"github.com/opencost/opencost/core/pkg/filter/transform"
 )
 
 // CloudCostMatcher is a matcher implementation for CloudCost instances,

+ 2 - 2
pkg/kubecost/cloudcostprops.go → core/pkg/opencost/cloudcostprops.go

@@ -1,9 +1,9 @@
-package kubecost
+package opencost
 
 import (
 	"strings"
 
-	"github.com/opencost/opencost/pkg/log"
+	"github.com/opencost/opencost/core/pkg/log"
 )
 
 const (

+ 1 - 1
pkg/kubecost/cloudcostprops_test.go → core/pkg/opencost/cloudcostprops_test.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 import "testing"
 

+ 1 - 1
pkg/kubecost/cloudusage.go → core/pkg/opencost/cloudusage.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 // CloudUsage is temporarily aliased as the Cloud Asset type until further infrastructure and pages can be built to support its usage
 type CloudUsage = Cloud

+ 1 - 1
pkg/kubecost/common.go → core/pkg/opencost/common.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 // Pair is a generic struct containing a pair of instances, one of each type similar to std::pair
 type Pair[T any, U any] struct {

+ 6 - 6
pkg/kubecost/config.go → core/pkg/opencost/config.go

@@ -1,11 +1,11 @@
-package kubecost
+package opencost
 
 import (
 	"fmt"
 	"strings"
 
-	"github.com/opencost/opencost/pkg/prom"
-	"github.com/opencost/opencost/pkg/util/cloudutil"
+	"github.com/opencost/opencost/core/pkg/util/cloudutil"
+	"github.com/opencost/opencost/core/pkg/util/promutil"
 )
 
 // LabelConfig is a port of type AnalyzerConfig. We need to be more thoughtful
@@ -173,7 +173,7 @@ func (lc *LabelConfig) Map() map[string]string {
 // all illegal characters to underscores. Illegal characters are those that
 // Prometheus does not support; i.e. [^a-zA-Z0-9_]
 func (lc *LabelConfig) Sanitize(label string) string {
-	return prom.SanitizeLabelName(strings.TrimSpace(label))
+	return promutil.SanitizeLabelName(strings.TrimSpace(label))
 }
 
 // GetExternalAllocationName derives an external allocation name from a set of
@@ -192,7 +192,7 @@ func (lc *LabelConfig) GetExternalAllocationName(labels map[string]string, aggre
 	// Determine if the aggregation property is, itself, a label or not. If
 	// not, determine the label associated with the given aggregation property.
 	if strings.HasPrefix(aggregateBy, "label:") {
-		labelNames = append(labelNames, prom.SanitizeLabelName(strings.TrimPrefix(aggregateBy, "label:")))
+		labelNames = append(labelNames, promutil.SanitizeLabelName(strings.TrimPrefix(aggregateBy, "label:")))
 		aggByLabel = true
 	} else {
 		// If lc is nil, use a default LabelConfig to do a best-effort match
@@ -230,7 +230,7 @@ func (lc *LabelConfig) GetExternalAllocationName(labels map[string]string, aggre
 		}
 
 		for i, labelName := range labelNames {
-			labelNames[i] = prom.SanitizeLabelName(strings.TrimSpace(labelName))
+			labelNames[i] = promutil.SanitizeLabelName(strings.TrimSpace(labelName))
 		}
 	}
 

+ 2 - 2
pkg/kubecost/config_test.go → core/pkg/opencost/config_test.go

@@ -1,9 +1,9 @@
-package kubecost
+package opencost
 
 import (
 	"testing"
 
-	"github.com/opencost/opencost/pkg/util/cloudutil"
+	"github.com/opencost/opencost/core/pkg/util/cloudutil"
 )
 
 func TestLabelConfig_Map(t *testing.T) {

+ 1 - 1
pkg/kubecost/costmetric.go → core/pkg/opencost/costmetric.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 import (
 	"fmt"

+ 3 - 3
pkg/kubecost/coverage.go → core/pkg/opencost/coverage.go

@@ -1,10 +1,10 @@
-package kubecost
+package opencost
 
 import (
 	"time"
 
-	"github.com/opencost/opencost/pkg/filter"
-	"github.com/opencost/opencost/pkg/log"
+	filter "github.com/opencost/opencost/core/pkg/filter/legacy"
+	"github.com/opencost/opencost/core/pkg/log"
 )
 
 // Coverage This is a placeholder struct which can be replaced by a more specific implementation later

+ 1 - 1
pkg/kubecost/diff_test.go → core/pkg/opencost/diff_test.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 import (
 	"reflect"

+ 2 - 2
pkg/kubecost/json.go → core/pkg/opencost/json.go

@@ -1,11 +1,11 @@
-package kubecost
+package opencost
 
 import (
 	"bytes"
 	"fmt"
 	"math"
 
-	"github.com/opencost/opencost/pkg/util/json"
+	"github.com/opencost/opencost/core/pkg/util/json"
 )
 
 // TODO move everything below to a separate package

+ 1 - 1
pkg/kubecost/mock.go → core/pkg/opencost/mock.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 import (
 	"fmt"

+ 4 - 3
pkg/kubecost/kubecost_codecs.go → core/pkg/opencost/opencost_codecs.go

@@ -9,20 +9,21 @@
 //
 ////////////////////////////////////////////////////////////////////////////////
 
-package kubecost
+package opencost
 
 import (
 	"fmt"
-	util "github.com/opencost/opencost/pkg/util"
 	"reflect"
 	"strings"
 	"sync"
 	"time"
+
+	util "github.com/opencost/opencost/core/pkg/util"
 )
 
 const (
 	// GeneratorPackageName is the package the generator is targetting
-	GeneratorPackageName string = "kubecost"
+	GeneratorPackageName string = "opencost"
 )
 
 // BinaryTags represent the formatting tag used for specific optimization features

+ 1 - 1
pkg/kubecost/kubecost_codecs_test.go → core/pkg/opencost/opencost_codecs_test.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 import (
 	"testing"

+ 2 - 2
pkg/kubecost/query.go → core/pkg/opencost/query.go

@@ -1,10 +1,10 @@
-package kubecost
+package opencost
 
 import (
 	"strings"
 	"time"
 
-	filter21 "github.com/opencost/opencost/pkg/filter21"
+	filter21 "github.com/opencost/opencost/core/pkg/filter"
 )
 
 // Querier is an aggregate interface which has the ability to query each Kubecost store type

+ 1 - 1
pkg/kubecost/status.go → core/pkg/opencost/status.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 import "time"
 

+ 5 - 5
pkg/kubecost/summaryallocation.go → core/pkg/opencost/summaryallocation.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 import (
 	"errors"
@@ -7,10 +7,10 @@ import (
 	"sync"
 	"time"
 
-	"github.com/opencost/opencost/pkg/filter21/ast"
-	"github.com/opencost/opencost/pkg/filter21/matcher"
-	"github.com/opencost/opencost/pkg/log"
-	"github.com/opencost/opencost/pkg/util/timeutil"
+	"github.com/opencost/opencost/core/pkg/filter/ast"
+	"github.com/opencost/opencost/core/pkg/filter/matcher"
+	"github.com/opencost/opencost/core/pkg/log"
+	"github.com/opencost/opencost/core/pkg/util/timeutil"
 )
 
 // SummaryAllocation summarizes an Allocation, keeping only fields necessary

+ 2 - 2
pkg/kubecost/summaryallocation_json.go → core/pkg/opencost/summaryallocation_json.go

@@ -1,9 +1,9 @@
-package kubecost
+package opencost
 
 import (
 	"time"
 
-	"github.com/opencost/opencost/pkg/util/formatutil"
+	"github.com/opencost/opencost/core/pkg/util/formatutil"
 )
 
 // SummaryAllocationResponse is a sanitized version of SummaryAllocation, which

+ 2 - 2
pkg/kubecost/summaryallocation_json_test.go → core/pkg/opencost/summaryallocation_json_test.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 import (
 	"encoding/json"
@@ -6,7 +6,7 @@ import (
 	"testing"
 	"time"
 
-	"github.com/opencost/opencost/pkg/util/timeutil"
+	"github.com/opencost/opencost/core/pkg/util/timeutil"
 )
 
 func TestSummaryAllocationSetRangeResponse_MarshalJSON(t *testing.T) {

+ 2 - 2
pkg/kubecost/summaryallocation_test.go → core/pkg/opencost/summaryallocation_test.go

@@ -1,10 +1,10 @@
-package kubecost
+package opencost
 
 import (
 	"testing"
 	"time"
 
-	"github.com/opencost/opencost/pkg/util"
+	"github.com/opencost/opencost/core/pkg/util"
 )
 
 func TestSummaryAllocation_Add(t *testing.T) {

+ 2 - 2
pkg/kubecost/totals.go → core/pkg/opencost/totals.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 import (
 	"errors"
@@ -6,7 +6,7 @@ import (
 	"strconv"
 	"time"
 
-	"github.com/opencost/opencost/pkg/log"
+	"github.com/opencost/opencost/core/pkg/log"
 	"github.com/patrickmn/go-cache"
 )
 

+ 2 - 2
pkg/kubecost/totals_json.go → core/pkg/opencost/totals_json.go

@@ -1,9 +1,9 @@
-package kubecost
+package opencost
 
 import (
 	"time"
 
-	"github.com/opencost/opencost/pkg/util/formatutil"
+	"github.com/opencost/opencost/core/pkg/util/formatutil"
 )
 
 type AllocationTotalsResponse struct {

+ 1 - 1
pkg/kubecost/totals_test.go → core/pkg/opencost/totals_test.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 import (
 	"math"

+ 66 - 11
pkg/kubecost/window.go → core/pkg/opencost/window.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 import (
 	"bytes"
@@ -7,12 +7,12 @@ import (
 	"math"
 	"regexp"
 	"strconv"
+	"sync"
 	"time"
 
-	"github.com/opencost/opencost/pkg/env"
-	"github.com/opencost/opencost/pkg/log"
-	"github.com/opencost/opencost/pkg/thanos"
-	"github.com/opencost/opencost/pkg/util/timeutil"
+	"github.com/opencost/opencost/core/pkg/env"
+	"github.com/opencost/opencost/core/pkg/log"
+	"github.com/opencost/opencost/core/pkg/util/timeutil"
 )
 
 const (
@@ -28,8 +28,60 @@ var (
 	rfc3339             = `\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ`
 	rfcRegex            = regexp.MustCompile(fmt.Sprintf(`(%s),(%s)`, rfc3339, rfc3339))
 	timestampPairRegex  = regexp.MustCompile(`^(\d+)[,|-](\d+)$`)
+
+	tOffsetLock sync.Mutex
+	tOffset     *time.Duration
+
+	utcOffsetLock sync.Mutex
+	utcOffsetDur  *time.Duration
 )
 
+// get and cache the thanos offset duration.
+// TODO: Due to dependencies here, we have to drag a non-core config option into
+// TOOD: core scope. Any solution here would be a one-off until we can generalize
+// TODO: global configuration options.
+func thanosOffset() time.Duration {
+	tOffsetLock.Lock()
+	defer tOffsetLock.Unlock()
+
+	if tOffset == nil {
+		d, err := time.ParseDuration(env.Get("THANOS_QUERY_OFFSET", "3h"))
+		if err != nil {
+			d = 0
+		}
+
+		tOffset = &d
+	}
+
+	return *tOffset
+}
+
+// returns true if thanos is enabled
+// TODO: Same note as thanosOffset above - temporary work-around until more
+// TODO: generalized global configuration.
+func isThanosEnabled() bool {
+	return env.GetBool("THANOS_ENABLED", false)
+}
+
+// returns the configured utc offset as a duration
+// TODO: Same as the above options -- we should provide a one-time initialization configuration
+// TODO: for these values, or deprecate their use.
+func utcOffset() time.Duration {
+	utcOffsetLock.Lock()
+	defer utcOffsetLock.Unlock()
+
+	if utcOffsetDur == nil {
+		utcOff, err := timeutil.ParseUTCOffset(env.Get("UTC_OFFSET", ""))
+		if err != nil {
+			utcOff = time.Duration(0)
+		}
+
+		utcOffsetDur = &utcOff
+	}
+
+	return *utcOffsetDur
+}
+
 // RoundBack rounds the given time back to a multiple of the given resolution
 // in the given time's timezone.
 // e.g. 2020-01-01T12:37:48-0700, 24h = 2020-01-01T00:00:00-0700
@@ -705,8 +757,11 @@ func (w Window) DurationOffsetForPrometheus() (string, string, error) {
 
 	// If using Thanos, increase offset to 3 hours, reducing the duration by
 	// equal measure to maintain the same starting point.
-	thanosDur := thanos.OffsetDuration()
-	if offset < thanosDur && env.IsThanosEnabled() {
+	// TODO: This logic should technically be decoupled from this type, but
+	// TODO: current use cases are unclear. To ensure we do not break existing
+	// TODO: (or legacy) use-cases, temporarily support this one-off logic.
+	thanosDur := thanosOffset()
+	if offset < thanosDur && isThanosEnabled() {
 		diff := thanosDur - offset
 		offset += diff
 		duration -= diff
@@ -1044,8 +1099,8 @@ func GetWindows(start time.Time, end time.Time, windowSize time.Duration) ([]Win
 	if sz != ez {
 		return nil, fmt.Errorf("range has mismatched timezones: %s, %s", start, end)
 	}
-	if sz != int(env.GetParsedUTCOffset().Seconds()) {
-		return nil, fmt.Errorf("range timezone doesn't match configured timezone: expected %s; found %ds", env.GetParsedUTCOffset(), sz)
+	if sz != int(utcOffset().Seconds()) {
+		return nil, fmt.Errorf("range timezone doesn't match configured timezone: expected %s; found %ds", utcOffset(), sz)
 	}
 
 	// Build array of windows to cover the CloudCostSetRange
@@ -1070,8 +1125,8 @@ func GetWindowsForQueryWindow(start time.Time, end time.Time, queryWindow time.D
 	if sz != ez {
 		return nil, fmt.Errorf("range has mismatched timezones: %s, %s", start, end)
 	}
-	if sz != int(env.GetParsedUTCOffset().Seconds()) {
-		return nil, fmt.Errorf("range timezone doesn't match configured timezone: expected %s; found %ds", env.GetParsedUTCOffset(), sz)
+	if sz != int(utcOffset().Seconds()) {
+		return nil, fmt.Errorf("range timezone doesn't match configured timezone: expected %s; found %ds", utcOffset(), sz)
 	}
 
 	// Build array of windows to cover the CloudCostSetRange

+ 14 - 9
pkg/kubecost/window_test.go → core/pkg/opencost/window_test.go

@@ -1,4 +1,4 @@
-package kubecost
+package opencost
 
 import (
 	"encoding/json"
@@ -10,9 +10,14 @@ import (
 
 	"github.com/google/go-cmp/cmp"
 
-	"github.com/opencost/opencost/pkg/util/timeutil"
+	"github.com/opencost/opencost/core/pkg/util/timeutil"
 
-	"github.com/opencost/opencost/pkg/env"
+	"github.com/opencost/opencost/core/pkg/env"
+)
+
+const (
+	ThanosEnabledEnvVarName = "THANOS_ENABLED"
+	UtcOffsetEnvVarName     = "UTC_OFFSET"
 )
 
 func TestRoundBack(t *testing.T) {
@@ -650,12 +655,12 @@ func TestWindow_DurationOffsetStrings(t *testing.T) {
 
 func TestWindow_DurationOffsetForPrometheus(t *testing.T) {
 	// Set-up and tear-down
-	thanosEnabled := env.GetBool(env.ThanosEnabledEnvVar, false)
-	defer env.SetBool(env.ThanosEnabledEnvVar, thanosEnabled)
+	thanosEnabled := env.GetBool(ThanosEnabledEnvVarName, false)
+	defer env.SetBool(ThanosEnabledEnvVarName, thanosEnabled)
 
 	// Test for Prometheus (env.IsThanosEnabled() == false)
-	env.SetBool(env.ThanosEnabledEnvVar, false)
-	if env.IsThanosEnabled() {
+	env.SetBool(ThanosEnabledEnvVarName, false)
+	if env.GetBool(ThanosEnabledEnvVarName, false) {
 		t.Fatalf("expected env.IsThanosEnabled() == false")
 	}
 
@@ -744,8 +749,8 @@ func TestWindow_DurationOffsetForPrometheus(t *testing.T) {
 	}
 
 	// Test for Thanos (env.IsThanosEnabled() == true)
-	env.SetBool(env.ThanosEnabledEnvVar, true)
-	if !env.IsThanosEnabled() {
+	env.SetBool(ThanosEnabledEnvVarName, true)
+	if !env.GetBool(ThanosEnabledEnvVarName, false) {
 		t.Fatalf("expected env.IsThanosEnabled() == true")
 	}
 

+ 13 - 2
pkg/proto/http.go → core/pkg/protocol/http.go

@@ -1,10 +1,10 @@
-package proto
+package protocol
 
 import (
 	"fmt"
 	"net/http"
 
-	"github.com/opencost/opencost/pkg/util/json"
+	"github.com/opencost/opencost/core/pkg/util/json"
 	"google.golang.org/protobuf/encoding/protojson"
 	"google.golang.org/protobuf/proto"
 )
@@ -31,6 +31,17 @@ func (hp HTTPProtocol) BadRequest(message string) HTTPError {
 	}
 }
 
+// UnprocessableEntity creates an UnprocessableEntity HTTPError
+func (hp HTTPProtocol) UnprocessableEntity(message string) HTTPError {
+	if message == "" {
+		message = "Unprocessable Entity"
+	}
+	return HTTPError{
+		StatusCode: http.StatusUnprocessableEntity,
+		Body:       message,
+	}
+}
+
 // InternalServerError creates an InternalServerError HTTPError
 func (hp HTTPProtocol) InternalServerError(message string) HTTPError {
 	if message == "" {

Некоторые файлы не были показаны из-за большого количества измененных файлов