containerkeys.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. package costmodel
  2. import (
  3. "errors"
  4. "strings"
  5. "github.com/kubecost/cost-model/pkg/log"
  6. "k8s.io/api/core/v1"
  7. )
  8. var (
  9. // Static KeyTuple Errors
  10. NewKeyTupleErr = errors.New("NewKeyTuple() Provided key not containing exactly 3 components.")
  11. // Static Errors for ContainerMetric creation
  12. InvalidKeyErr error = errors.New("Not a valid key")
  13. NoContainerErr error = errors.New("Prometheus vector does not have container name")
  14. NoContainerNameErr error = errors.New("Prometheus vector does not have string container name")
  15. NoPodErr error = errors.New("Prometheus vector does not have pod name")
  16. NoPodNameErr error = errors.New("Prometheus vector does not have string pod name")
  17. NoNamespaceErr error = errors.New("Prometheus vector does not have namespace")
  18. NoNamespaceNameErr error = errors.New("Prometheus vector does not have string namespace")
  19. NoNodeNameErr error = errors.New("Prometheus vector does not have string node")
  20. NoClusterIDErr error = errors.New("Prometheus vector does not have string cluster_id")
  21. )
  22. //--------------------------------------------------------------------------
  23. // KeyTuple
  24. //--------------------------------------------------------------------------
  25. // KeyTuple contains is a utility which parses Namespace, Key, and ClusterID from a
  26. // comma delimitted string.
  27. type KeyTuple struct {
  28. key string
  29. kIndex int
  30. cIndex int
  31. }
  32. // Namespace returns the the namespace from the string key.
  33. func (kt *KeyTuple) Namespace() string {
  34. return kt.key[0 : kt.kIndex-1]
  35. }
  36. // Key returns the identifier from the string key.
  37. func (kt *KeyTuple) Key() string {
  38. return kt.key[kt.kIndex : kt.cIndex-1]
  39. }
  40. // ClusterID returns the cluster identifier from the string key.
  41. func (kt *KeyTuple) ClusterID() string {
  42. return kt.key[kt.cIndex:]
  43. }
  44. // NewKeyTuple creates a new KeyTuple instance by determining the exact indices of each tuple
  45. // entry. When each component is requested, a string slice is returned using the boundaries.
  46. func NewKeyTuple(key string) (*KeyTuple, error) {
  47. kIndex := strings.IndexRune(key, ',')
  48. if kIndex < 0 {
  49. return nil, NewKeyTupleErr
  50. }
  51. kIndex += 1
  52. subIndex := strings.IndexRune(key[kIndex:], ',')
  53. if subIndex < 0 {
  54. return nil, NewKeyTupleErr
  55. }
  56. cIndex := kIndex + subIndex + 1
  57. if strings.ContainsRune(key[cIndex:], ',') {
  58. return nil, NewKeyTupleErr
  59. }
  60. return &KeyTuple{
  61. key: key,
  62. kIndex: kIndex,
  63. cIndex: cIndex,
  64. }, nil
  65. }
  66. //--------------------------------------------------------------------------
  67. // ContainerMetric
  68. //--------------------------------------------------------------------------
  69. // ContainerMetric contains a set of identifiers specific to a kubernetes container including
  70. // a unique string key
  71. type ContainerMetric struct {
  72. Namespace string
  73. PodName string
  74. ContainerName string
  75. NodeName string
  76. ClusterID string
  77. key string
  78. }
  79. // Key returns a unique string key that can be used in map[string]interface{}
  80. func (c *ContainerMetric) Key() string {
  81. return c.key
  82. }
  83. // containerMetricKey creates a unique string key, a comma delimitted list of the provided
  84. // parameters.
  85. func containerMetricKey(ns, podName, containerName, nodeName, clusterID string) string {
  86. return ns + "," + podName + "," + containerName + "," + nodeName + "," + clusterID
  87. }
  88. // NewContainerMetricFromKey creates a new ContainerMetric instance using a provided comma delimitted
  89. // string key.
  90. func NewContainerMetricFromKey(key string) (*ContainerMetric, error) {
  91. s := strings.Split(key, ",")
  92. if len(s) == 5 {
  93. return &ContainerMetric{
  94. Namespace: s[0],
  95. PodName: s[1],
  96. ContainerName: s[2],
  97. NodeName: s[3],
  98. ClusterID: s[4],
  99. key: key,
  100. }, nil
  101. }
  102. return nil, InvalidKeyErr
  103. }
  104. // NewContainerMetricFromValues creates a new ContainerMetric instance using the provided string parameters.
  105. func NewContainerMetricFromValues(ns, podName, containerName, nodeName, clusterId string) *ContainerMetric {
  106. return &ContainerMetric{
  107. Namespace: ns,
  108. PodName: podName,
  109. ContainerName: containerName,
  110. NodeName: nodeName,
  111. ClusterID: clusterId,
  112. key: containerMetricKey(ns, podName, containerName, nodeName, clusterId),
  113. }
  114. }
  115. // NewContainerMetricsFromPod creates a slice of ContainerMetric instances for each container in the
  116. // provided Pod.
  117. func NewContainerMetricsFromPod(pod *v1.Pod, clusterID string) ([]*ContainerMetric, error) {
  118. podName := pod.GetObjectMeta().GetName()
  119. ns := pod.GetObjectMeta().GetNamespace()
  120. node := pod.Spec.NodeName
  121. var cs []*ContainerMetric
  122. for _, container := range pod.Spec.Containers {
  123. containerName := container.Name
  124. cs = append(cs, &ContainerMetric{
  125. Namespace: ns,
  126. PodName: podName,
  127. ContainerName: containerName,
  128. NodeName: node,
  129. ClusterID: clusterID,
  130. key: containerMetricKey(ns, podName, containerName, node, clusterID),
  131. })
  132. }
  133. return cs, nil
  134. }
  135. // NewContainerMetricFromPrometheus accepts the metrics map from a QueryResult and returns a new ContainerMetric
  136. // instance
  137. func NewContainerMetricFromPrometheus(metrics map[string]interface{}, defaultClusterID string) (*ContainerMetric, error) {
  138. // TODO: Can we use *prom.QueryResult.GetString() here?
  139. cName, ok := metrics["container_name"]
  140. if !ok {
  141. return nil, NoContainerErr
  142. }
  143. containerName, ok := cName.(string)
  144. if !ok {
  145. return nil, NoContainerNameErr
  146. }
  147. pName, ok := metrics["pod_name"]
  148. if !ok {
  149. return nil, NoPodErr
  150. }
  151. podName, ok := pName.(string)
  152. if !ok {
  153. return nil, NoPodNameErr
  154. }
  155. ns, ok := metrics["namespace"]
  156. if !ok {
  157. return nil, NoNamespaceErr
  158. }
  159. namespace, ok := ns.(string)
  160. if !ok {
  161. return nil, NoNamespaceNameErr
  162. }
  163. node, ok := metrics["node"]
  164. if !ok {
  165. log.Debugf("Prometheus vector does not have node name")
  166. node = ""
  167. }
  168. nodeName, ok := node.(string)
  169. if !ok {
  170. return nil, NoNodeNameErr
  171. }
  172. cid, ok := metrics["cluster_id"]
  173. if !ok {
  174. log.Debugf("Prometheus vector does not have cluster id")
  175. cid = defaultClusterID
  176. }
  177. clusterID, ok := cid.(string)
  178. if !ok {
  179. return nil, NoClusterIDErr
  180. }
  181. return &ContainerMetric{
  182. ContainerName: containerName,
  183. PodName: podName,
  184. Namespace: namespace,
  185. NodeName: nodeName,
  186. ClusterID: clusterID,
  187. key: containerMetricKey(namespace, podName, containerName, nodeName, clusterID),
  188. }, nil
  189. }