allocationprops.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. package kubecost
  2. import (
  3. "fmt"
  4. "sort"
  5. "strings"
  6. )
  7. type AllocationProperty string
  8. const (
  9. AllocationNilProp AllocationProperty = ""
  10. AllocationClusterProp AllocationProperty = "cluster"
  11. AllocationNodeProp AllocationProperty = "node"
  12. AllocationContainerProp AllocationProperty = "container"
  13. AllocationControllerProp AllocationProperty = "controller"
  14. AllocationControllerKindProp AllocationProperty = "controllerKind"
  15. AllocationNamespaceProp AllocationProperty = "namespace"
  16. AllocationPodProp AllocationProperty = "pod"
  17. AllocationProviderIDProp AllocationProperty = "providerID"
  18. AllocationServiceProp AllocationProperty = "service"
  19. AllocationLabelProp AllocationProperty = "label"
  20. AllocationAnnotationProp AllocationProperty = "annotation"
  21. )
  22. func ParseProperty(text string) (AllocationProperty, error) {
  23. switch strings.TrimSpace(strings.ToLower(text)) {
  24. case "cluster":
  25. return AllocationClusterProp, nil
  26. case "node":
  27. return AllocationNodeProp, nil
  28. case "container":
  29. return AllocationContainerProp, nil
  30. case "controller":
  31. return AllocationControllerProp, nil
  32. case "controllerKind":
  33. return AllocationControllerKindProp, nil
  34. case "namespace":
  35. return AllocationNamespaceProp, nil
  36. case "pod":
  37. return AllocationPodProp, nil
  38. case "providerid":
  39. return AllocationProviderIDProp, nil
  40. case "service":
  41. return AllocationServiceProp, nil
  42. case "label":
  43. return AllocationLabelProp, nil
  44. case "annotation":
  45. return AllocationAnnotationProp, nil
  46. }
  47. return AllocationNilProp, fmt.Errorf("invalid allocation property: %s", text)
  48. }
  49. func (p AllocationProperty) String() string {
  50. return string(p)
  51. }
  52. // AllocationProperties describes a set of Kubernetes objects.
  53. type AllocationProperties struct {
  54. Cluster string `json:"cluster,omitempty"`
  55. Node string `json:"node,omitempty"`
  56. Container string `json:"container,omitempty"`
  57. Controller string `json:"controller,omitempty"`
  58. ControllerKind string `json:"controllerKind,omitempty"`
  59. Namespace string `json:"namespace,omitempty"`
  60. Pod string `json:"pod,omitempty"`
  61. Services []string `json:"services,omitempty"`
  62. ProviderID string `json:"providerID,omitempty"`
  63. Labels AllocationLabels `json:"allocationLabels,omitempty"`
  64. Annotations AllocationAnnotations `json:"allocationAnnotations,omitempty"`
  65. }
  66. // AllocationLabels is a schema-free mapping of key/value pairs that can be
  67. // attributed to an Allocation
  68. type AllocationLabels map[string]string
  69. // AllocationAnnotations is a schema-free mapping of key/value pairs that can be
  70. // attributed to an Allocation
  71. type AllocationAnnotations map[string]string
  72. // TODO niko/etl make sure Services deep copy works correctly
  73. func (p *AllocationProperties) Clone() *AllocationProperties {
  74. if p == nil {
  75. return nil
  76. }
  77. clone := &AllocationProperties{}
  78. clone.Cluster = p.Cluster
  79. clone.Node = p.Node
  80. clone.Container = p.Container
  81. clone.Controller = p.Controller
  82. clone.ControllerKind = p.ControllerKind
  83. clone.Namespace = p.Namespace
  84. clone.Pod = p.Pod
  85. clone.ProviderID = p.ProviderID
  86. clone.Services = p.Services
  87. clone.Labels = p.Labels
  88. clone.Annotations = p.Annotations
  89. return clone
  90. }
  91. func (p *AllocationProperties) Equal(that *AllocationProperties) bool {
  92. if p == nil || that == nil {
  93. return false
  94. }
  95. if p.Cluster != that.Cluster {
  96. return false
  97. }
  98. if p.Node != that.Node {
  99. return false
  100. }
  101. if p.Container != that.Container {
  102. return false
  103. }
  104. if p.Controller != that.Controller {
  105. return false
  106. }
  107. if p.ControllerKind != that.ControllerKind {
  108. return false
  109. }
  110. if p.Namespace != that.Namespace {
  111. return false
  112. }
  113. if p.Pod != that.Pod {
  114. return false
  115. }
  116. if p.ProviderID != that.ProviderID {
  117. return false
  118. }
  119. pLabels := p.Labels
  120. thatLabels := that.Labels
  121. if len(pLabels) != len(thatLabels) {
  122. for k, pv := range pLabels {
  123. tv, ok := thatLabels[k]
  124. if !ok || tv != pv {
  125. return false
  126. }
  127. }
  128. return false
  129. }
  130. pAnnotations := p.Annotations
  131. thatAnnotations := that.Annotations
  132. if len(pAnnotations) != len(thatAnnotations) {
  133. for k, pv := range pAnnotations {
  134. tv, ok := thatAnnotations[k]
  135. if !ok || tv != pv {
  136. return false
  137. }
  138. }
  139. return false
  140. }
  141. pServices := p.Services
  142. thatServices := that.Services
  143. if len(pServices) != len(thatServices) {
  144. sort.Strings(pServices)
  145. sort.Strings(thatServices)
  146. for i, pv := range pServices {
  147. tv := thatServices[i]
  148. if tv != pv {
  149. return false
  150. }
  151. }
  152. return false
  153. }
  154. return true
  155. }
  156. func (p *AllocationProperties) String() string {
  157. if p == nil {
  158. return "<nil>"
  159. }
  160. strs := []string{}
  161. if p.Cluster != "" {
  162. strs = append(strs, "Cluster:"+p.Cluster)
  163. }
  164. if p.Node != "" {
  165. strs = append(strs, "Node:"+p.Node)
  166. }
  167. if p.Container != "" {
  168. strs = append(strs, "Container:"+p.Container)
  169. }
  170. if p.Controller != "" {
  171. strs = append(strs, "Controller:"+p.Controller)
  172. }
  173. if p.ControllerKind != "" {
  174. strs = append(strs, "ControllerKind:"+p.ControllerKind)
  175. }
  176. if p.Namespace != "" {
  177. strs = append(strs, "Namespace:"+p.Namespace)
  178. }
  179. if p.Pod != "" {
  180. strs = append(strs, "Pod:"+p.Pod)
  181. }
  182. if p.ProviderID != "" {
  183. strs = append(strs, "ProviderID:"+p.ProviderID)
  184. }
  185. if len(p.Services) > 0 {
  186. strs = append(strs, "Services:"+strings.Join(p.Services, ";"))
  187. }
  188. var labelStrs []string
  189. for k, prop := range p.Labels {
  190. labelStrs = append(labelStrs, fmt.Sprintf("%s:%s", k, prop))
  191. }
  192. strs = append(strs, fmt.Sprintf("Labels:{%s}", strings.Join(strs, ",")))
  193. var AnnotationStrs []string
  194. for k, prop := range p.Annotations {
  195. AnnotationStrs = append(AnnotationStrs, fmt.Sprintf("%s:%s", k, prop))
  196. }
  197. strs = append(strs, fmt.Sprintf("Annotations:{%s}", strings.Join(strs, ",")))
  198. return strings.Join(strs, ",")
  199. }
  200. // AggregationStrings converts a AllocationProperties object into a slice of strings
  201. // representing a request to aggregate by certain properties.
  202. // NOTE: today, the ordering of the properties *has to match the ordering
  203. // of the allocaiton function generateKey*
  204. func (p *AllocationProperties) AggregationStrings() []string {
  205. if p == nil {
  206. return []string{}
  207. }
  208. aggStrs := []string{}
  209. if p.Cluster != "" {
  210. aggStrs = append(aggStrs, AllocationClusterProp.String())
  211. }
  212. if p.Node != "" {
  213. aggStrs = append(aggStrs, AllocationNodeProp.String())
  214. }
  215. if p.Container != "" {
  216. aggStrs = append(aggStrs, AllocationContainerProp.String())
  217. }
  218. if p.Controller != "" {
  219. aggStrs = append(aggStrs, AllocationControllerProp.String())
  220. }
  221. if p.ControllerKind != "" {
  222. aggStrs = append(aggStrs, AllocationControllerKindProp.String())
  223. }
  224. if p.Namespace != "" {
  225. aggStrs = append(aggStrs, AllocationNamespaceProp.String())
  226. }
  227. if p.Pod != "" {
  228. aggStrs = append(aggStrs, AllocationPodProp.String())
  229. }
  230. if p.ProviderID != "" {
  231. aggStrs = append(aggStrs, AllocationProviderIDProp.String())
  232. }
  233. if len(p.Services) > 0 {
  234. aggStrs = append(aggStrs, AllocationServiceProp.String())
  235. }
  236. if len(p.Services) > 0 {
  237. aggStrs = append(aggStrs, AllocationServiceProp.String())
  238. }
  239. if len(p.Labels) > 0 {
  240. // e.g. expect format map[string]string{
  241. // "env":""
  242. // "app":"",
  243. // }
  244. // for aggregating by "label:app,label:env"
  245. labels := p.Labels
  246. labelAggStrs := []string{}
  247. for labelName := range labels {
  248. labelAggStrs = append(labelAggStrs, fmt.Sprintf("label:%s", labelName))
  249. }
  250. if len(labelAggStrs) > 0 {
  251. // Enforce alphabetical ordering, then append to aggStrs
  252. sort.Strings(labelAggStrs)
  253. for _, labelName := range labelAggStrs {
  254. aggStrs = append(aggStrs, labelName)
  255. }
  256. }
  257. }
  258. return aggStrs
  259. }
  260. func (p *AllocationProperties) isEmpty() bool {
  261. if p == nil {
  262. return true
  263. }
  264. return p.Equal(&AllocationProperties{})
  265. }