containerkeys.go 6.7 KB

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