kubeview-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (454) hide show
  1. package/.prettierrc.json +11 -0
  2. package/CHANGELOG.md +55 -0
  3. package/LICENSE +21 -0
  4. package/README.md +251 -0
  5. package/RELEASE.md +154 -0
  6. package/TODO.md +0 -0
  7. package/bin/kubeview-mcp.js +274 -0
  8. package/bin/setup.js +368 -0
  9. package/dist/src/cli/cli.d.ts +3 -0
  10. package/dist/src/cli/cli.d.ts.map +1 -0
  11. package/dist/src/cli/cli.js +23 -0
  12. package/dist/src/cli/cli.js.map +1 -0
  13. package/dist/src/index.d.ts +8 -0
  14. package/dist/src/index.d.ts.map +1 -0
  15. package/dist/src/index.js +45 -0
  16. package/dist/src/index.js.map +1 -0
  17. package/dist/src/kubernetes/BaseResourceOperations.d.ts +155 -0
  18. package/dist/src/kubernetes/BaseResourceOperations.d.ts.map +1 -0
  19. package/dist/src/kubernetes/BaseResourceOperations.js +136 -0
  20. package/dist/src/kubernetes/BaseResourceOperations.js.map +1 -0
  21. package/dist/src/kubernetes/CircuitBreaker.d.ts +182 -0
  22. package/dist/src/kubernetes/CircuitBreaker.d.ts.map +1 -0
  23. package/dist/src/kubernetes/CircuitBreaker.js +369 -0
  24. package/dist/src/kubernetes/CircuitBreaker.js.map +1 -0
  25. package/dist/src/kubernetes/ConnectionManager.d.ts +60 -0
  26. package/dist/src/kubernetes/ConnectionManager.d.ts.map +1 -0
  27. package/dist/src/kubernetes/ConnectionManager.js +77 -0
  28. package/dist/src/kubernetes/ConnectionManager.js.map +1 -0
  29. package/dist/src/kubernetes/ConnectionPool.d.ts +183 -0
  30. package/dist/src/kubernetes/ConnectionPool.d.ts.map +1 -0
  31. package/dist/src/kubernetes/ConnectionPool.js +437 -0
  32. package/dist/src/kubernetes/ConnectionPool.js.map +1 -0
  33. package/dist/src/kubernetes/ErrorHandler.d.ts +172 -0
  34. package/dist/src/kubernetes/ErrorHandler.d.ts.map +1 -0
  35. package/dist/src/kubernetes/ErrorHandler.js +328 -0
  36. package/dist/src/kubernetes/ErrorHandler.js.map +1 -0
  37. package/dist/src/kubernetes/ErrorHandling.d.ts +148 -0
  38. package/dist/src/kubernetes/ErrorHandling.d.ts.map +1 -0
  39. package/dist/src/kubernetes/ErrorHandling.js +304 -0
  40. package/dist/src/kubernetes/ErrorHandling.js.map +1 -0
  41. package/dist/src/kubernetes/KubernetesClient.d.ts +162 -0
  42. package/dist/src/kubernetes/KubernetesClient.d.ts.map +1 -0
  43. package/dist/src/kubernetes/KubernetesClient.js +382 -0
  44. package/dist/src/kubernetes/KubernetesClient.js.map +1 -0
  45. package/dist/src/kubernetes/ResourceOperations.d.ts +69 -0
  46. package/dist/src/kubernetes/ResourceOperations.d.ts.map +1 -0
  47. package/dist/src/kubernetes/ResourceOperations.js +99 -0
  48. package/dist/src/kubernetes/ResourceOperations.js.map +1 -0
  49. package/dist/src/kubernetes/RetryStrategy.d.ts +137 -0
  50. package/dist/src/kubernetes/RetryStrategy.d.ts.map +1 -0
  51. package/dist/src/kubernetes/RetryStrategy.js +277 -0
  52. package/dist/src/kubernetes/RetryStrategy.js.map +1 -0
  53. package/dist/src/kubernetes/RetryableOperation.d.ts +118 -0
  54. package/dist/src/kubernetes/RetryableOperation.d.ts.map +1 -0
  55. package/dist/src/kubernetes/RetryableOperation.js +233 -0
  56. package/dist/src/kubernetes/RetryableOperation.js.map +1 -0
  57. package/dist/src/kubernetes/index.d.ts +18 -0
  58. package/dist/src/kubernetes/index.d.ts.map +1 -0
  59. package/dist/src/kubernetes/index.js +28 -0
  60. package/dist/src/kubernetes/index.js.map +1 -0
  61. package/dist/src/kubernetes/resources/ConfigMapOperations.d.ts +60 -0
  62. package/dist/src/kubernetes/resources/ConfigMapOperations.d.ts.map +1 -0
  63. package/dist/src/kubernetes/resources/ConfigMapOperations.js +306 -0
  64. package/dist/src/kubernetes/resources/ConfigMapOperations.js.map +1 -0
  65. package/dist/src/kubernetes/resources/CustomResourceOperations.d.ts +55 -0
  66. package/dist/src/kubernetes/resources/CustomResourceOperations.d.ts.map +1 -0
  67. package/dist/src/kubernetes/resources/CustomResourceOperations.js +363 -0
  68. package/dist/src/kubernetes/resources/CustomResourceOperations.js.map +1 -0
  69. package/dist/src/kubernetes/resources/DeploymentOperations.d.ts +66 -0
  70. package/dist/src/kubernetes/resources/DeploymentOperations.d.ts.map +1 -0
  71. package/dist/src/kubernetes/resources/DeploymentOperations.js +234 -0
  72. package/dist/src/kubernetes/resources/DeploymentOperations.js.map +1 -0
  73. package/dist/src/kubernetes/resources/IngressOperations.d.ts +60 -0
  74. package/dist/src/kubernetes/resources/IngressOperations.d.ts.map +1 -0
  75. package/dist/src/kubernetes/resources/IngressOperations.js +224 -0
  76. package/dist/src/kubernetes/resources/IngressOperations.js.map +1 -0
  77. package/dist/src/kubernetes/resources/MetricOperations.d.ts +218 -0
  78. package/dist/src/kubernetes/resources/MetricOperations.d.ts.map +1 -0
  79. package/dist/src/kubernetes/resources/MetricOperations.js +893 -0
  80. package/dist/src/kubernetes/resources/MetricOperations.js.map +1 -0
  81. package/dist/src/kubernetes/resources/NamespaceOperations.d.ts +38 -0
  82. package/dist/src/kubernetes/resources/NamespaceOperations.d.ts.map +1 -0
  83. package/dist/src/kubernetes/resources/NamespaceOperations.js +101 -0
  84. package/dist/src/kubernetes/resources/NamespaceOperations.js.map +1 -0
  85. package/dist/src/kubernetes/resources/PersistentVolumeClaimOperations.d.ts +109 -0
  86. package/dist/src/kubernetes/resources/PersistentVolumeClaimOperations.d.ts.map +1 -0
  87. package/dist/src/kubernetes/resources/PersistentVolumeClaimOperations.js +383 -0
  88. package/dist/src/kubernetes/resources/PersistentVolumeClaimOperations.js.map +1 -0
  89. package/dist/src/kubernetes/resources/PersistentVolumeOperations.d.ts +97 -0
  90. package/dist/src/kubernetes/resources/PersistentVolumeOperations.d.ts.map +1 -0
  91. package/dist/src/kubernetes/resources/PersistentVolumeOperations.js +321 -0
  92. package/dist/src/kubernetes/resources/PersistentVolumeOperations.js.map +1 -0
  93. package/dist/src/kubernetes/resources/PodOperations.d.ts +99 -0
  94. package/dist/src/kubernetes/resources/PodOperations.d.ts.map +1 -0
  95. package/dist/src/kubernetes/resources/PodOperations.js +333 -0
  96. package/dist/src/kubernetes/resources/PodOperations.js.map +1 -0
  97. package/dist/src/kubernetes/resources/SecretOperations.d.ts +71 -0
  98. package/dist/src/kubernetes/resources/SecretOperations.d.ts.map +1 -0
  99. package/dist/src/kubernetes/resources/SecretOperations.js +254 -0
  100. package/dist/src/kubernetes/resources/SecretOperations.js.map +1 -0
  101. package/dist/src/kubernetes/resources/ServiceOperations.d.ts +64 -0
  102. package/dist/src/kubernetes/resources/ServiceOperations.d.ts.map +1 -0
  103. package/dist/src/kubernetes/resources/ServiceOperations.js +232 -0
  104. package/dist/src/kubernetes/resources/ServiceOperations.js.map +1 -0
  105. package/dist/src/kubernetes/utils/ResourceUtils.d.ts +238 -0
  106. package/dist/src/kubernetes/utils/ResourceUtils.d.ts.map +1 -0
  107. package/dist/src/kubernetes/utils/ResourceUtils.js +439 -0
  108. package/dist/src/kubernetes/utils/ResourceUtils.js.map +1 -0
  109. package/dist/src/plugins/ArgoCDToolsPlugin.d.ts +45 -0
  110. package/dist/src/plugins/ArgoCDToolsPlugin.d.ts.map +1 -0
  111. package/dist/src/plugins/ArgoCDToolsPlugin.js +155 -0
  112. package/dist/src/plugins/ArgoCDToolsPlugin.js.map +1 -0
  113. package/dist/src/plugins/ArgoToolsPlugin.d.ts +45 -0
  114. package/dist/src/plugins/ArgoToolsPlugin.d.ts.map +1 -0
  115. package/dist/src/plugins/ArgoToolsPlugin.js +149 -0
  116. package/dist/src/plugins/ArgoToolsPlugin.js.map +1 -0
  117. package/dist/src/plugins/HelmToolsPlugin.d.ts +41 -0
  118. package/dist/src/plugins/HelmToolsPlugin.d.ts.map +1 -0
  119. package/dist/src/plugins/HelmToolsPlugin.js +146 -0
  120. package/dist/src/plugins/HelmToolsPlugin.js.map +1 -0
  121. package/dist/src/plugins/KubernetesToolsPlugin.d.ts +44 -0
  122. package/dist/src/plugins/KubernetesToolsPlugin.d.ts.map +1 -0
  123. package/dist/src/plugins/KubernetesToolsPlugin.js +159 -0
  124. package/dist/src/plugins/KubernetesToolsPlugin.js.map +1 -0
  125. package/dist/src/plugins/SamplePlugin.d.ts +12 -0
  126. package/dist/src/plugins/SamplePlugin.d.ts.map +1 -0
  127. package/dist/src/plugins/SamplePlugin.js +51 -0
  128. package/dist/src/plugins/SamplePlugin.js.map +1 -0
  129. package/dist/src/plugins/index.d.ts +5 -0
  130. package/dist/src/plugins/index.d.ts.map +1 -0
  131. package/dist/src/plugins/index.js +5 -0
  132. package/dist/src/plugins/index.js.map +1 -0
  133. package/dist/src/server/MCPServer.d.ts +93 -0
  134. package/dist/src/server/MCPServer.d.ts.map +1 -0
  135. package/dist/src/server/MCPServer.js +398 -0
  136. package/dist/src/server/MCPServer.js.map +1 -0
  137. package/dist/src/tools/argo/ArgoCronListTool.d.ts +10 -0
  138. package/dist/src/tools/argo/ArgoCronListTool.d.ts.map +1 -0
  139. package/dist/src/tools/argo/ArgoCronListTool.js +70 -0
  140. package/dist/src/tools/argo/ArgoCronListTool.js.map +1 -0
  141. package/dist/src/tools/argo/ArgoGetTool.d.ts +10 -0
  142. package/dist/src/tools/argo/ArgoGetTool.d.ts.map +1 -0
  143. package/dist/src/tools/argo/ArgoGetTool.js +80 -0
  144. package/dist/src/tools/argo/ArgoGetTool.js.map +1 -0
  145. package/dist/src/tools/argo/ArgoListTool.d.ts +10 -0
  146. package/dist/src/tools/argo/ArgoListTool.d.ts.map +1 -0
  147. package/dist/src/tools/argo/ArgoListTool.js +133 -0
  148. package/dist/src/tools/argo/ArgoListTool.js.map +1 -0
  149. package/dist/src/tools/argo/ArgoLogsTool.d.ts +10 -0
  150. package/dist/src/tools/argo/ArgoLogsTool.d.ts.map +1 -0
  151. package/dist/src/tools/argo/ArgoLogsTool.js +117 -0
  152. package/dist/src/tools/argo/ArgoLogsTool.js.map +1 -0
  153. package/dist/src/tools/argo/BaseTool.d.ts +60 -0
  154. package/dist/src/tools/argo/BaseTool.d.ts.map +1 -0
  155. package/dist/src/tools/argo/BaseTool.js +51 -0
  156. package/dist/src/tools/argo/BaseTool.js.map +1 -0
  157. package/dist/src/tools/argo/index.d.ts +7 -0
  158. package/dist/src/tools/argo/index.d.ts.map +1 -0
  159. package/dist/src/tools/argo/index.js +6 -0
  160. package/dist/src/tools/argo/index.js.map +1 -0
  161. package/dist/src/tools/argocd/ArgoCDAppGetTool.d.ts +10 -0
  162. package/dist/src/tools/argocd/ArgoCDAppGetTool.d.ts.map +1 -0
  163. package/dist/src/tools/argocd/ArgoCDAppGetTool.js +97 -0
  164. package/dist/src/tools/argocd/ArgoCDAppGetTool.js.map +1 -0
  165. package/dist/src/tools/argocd/ArgoCDAppHistoryTool.d.ts +10 -0
  166. package/dist/src/tools/argocd/ArgoCDAppHistoryTool.d.ts.map +1 -0
  167. package/dist/src/tools/argocd/ArgoCDAppHistoryTool.js +80 -0
  168. package/dist/src/tools/argocd/ArgoCDAppHistoryTool.js.map +1 -0
  169. package/dist/src/tools/argocd/ArgoCDAppListTool.d.ts +10 -0
  170. package/dist/src/tools/argocd/ArgoCDAppListTool.d.ts.map +1 -0
  171. package/dist/src/tools/argocd/ArgoCDAppListTool.js +128 -0
  172. package/dist/src/tools/argocd/ArgoCDAppListTool.js.map +1 -0
  173. package/dist/src/tools/argocd/ArgoCDAppLogsTool.d.ts +10 -0
  174. package/dist/src/tools/argocd/ArgoCDAppLogsTool.d.ts.map +1 -0
  175. package/dist/src/tools/argocd/ArgoCDAppLogsTool.js +143 -0
  176. package/dist/src/tools/argocd/ArgoCDAppLogsTool.js.map +1 -0
  177. package/dist/src/tools/argocd/ArgoCDAppResourcesTool.d.ts +10 -0
  178. package/dist/src/tools/argocd/ArgoCDAppResourcesTool.d.ts.map +1 -0
  179. package/dist/src/tools/argocd/ArgoCDAppResourcesTool.js +123 -0
  180. package/dist/src/tools/argocd/ArgoCDAppResourcesTool.js.map +1 -0
  181. package/dist/src/tools/argocd/BaseTool.d.ts +71 -0
  182. package/dist/src/tools/argocd/BaseTool.d.ts.map +1 -0
  183. package/dist/src/tools/argocd/BaseTool.js +62 -0
  184. package/dist/src/tools/argocd/BaseTool.js.map +1 -0
  185. package/dist/src/tools/argocd/index.d.ts +8 -0
  186. package/dist/src/tools/argocd/index.d.ts.map +1 -0
  187. package/dist/src/tools/argocd/index.js +7 -0
  188. package/dist/src/tools/argocd/index.js.map +1 -0
  189. package/dist/src/tools/helm/BaseTool.d.ts +51 -0
  190. package/dist/src/tools/helm/BaseTool.d.ts.map +1 -0
  191. package/dist/src/tools/helm/BaseTool.js +42 -0
  192. package/dist/src/tools/helm/BaseTool.js.map +1 -0
  193. package/dist/src/tools/helm/HelmGetHooksTool.d.ts +10 -0
  194. package/dist/src/tools/helm/HelmGetHooksTool.d.ts.map +1 -0
  195. package/dist/src/tools/helm/HelmGetHooksTool.js +38 -0
  196. package/dist/src/tools/helm/HelmGetHooksTool.js.map +1 -0
  197. package/dist/src/tools/helm/HelmGetManifestTool.d.ts +10 -0
  198. package/dist/src/tools/helm/HelmGetManifestTool.d.ts.map +1 -0
  199. package/dist/src/tools/helm/HelmGetManifestTool.js +38 -0
  200. package/dist/src/tools/helm/HelmGetManifestTool.js.map +1 -0
  201. package/dist/src/tools/helm/HelmGetNotesTool.d.ts +10 -0
  202. package/dist/src/tools/helm/HelmGetNotesTool.d.ts.map +1 -0
  203. package/dist/src/tools/helm/HelmGetNotesTool.js +38 -0
  204. package/dist/src/tools/helm/HelmGetNotesTool.js.map +1 -0
  205. package/dist/src/tools/helm/HelmGetResourcesTool.d.ts +13 -0
  206. package/dist/src/tools/helm/HelmGetResourcesTool.d.ts.map +1 -0
  207. package/dist/src/tools/helm/HelmGetResourcesTool.js +124 -0
  208. package/dist/src/tools/helm/HelmGetResourcesTool.js.map +1 -0
  209. package/dist/src/tools/helm/HelmGetValuesTool.d.ts +10 -0
  210. package/dist/src/tools/helm/HelmGetValuesTool.d.ts.map +1 -0
  211. package/dist/src/tools/helm/HelmGetValuesTool.js +54 -0
  212. package/dist/src/tools/helm/HelmGetValuesTool.js.map +1 -0
  213. package/dist/src/tools/helm/HelmHistoryTool.d.ts +10 -0
  214. package/dist/src/tools/helm/HelmHistoryTool.d.ts.map +1 -0
  215. package/dist/src/tools/helm/HelmHistoryTool.js +49 -0
  216. package/dist/src/tools/helm/HelmHistoryTool.js.map +1 -0
  217. package/dist/src/tools/helm/HelmListTool.d.ts +10 -0
  218. package/dist/src/tools/helm/HelmListTool.d.ts.map +1 -0
  219. package/dist/src/tools/helm/HelmListTool.js +107 -0
  220. package/dist/src/tools/helm/HelmListTool.js.map +1 -0
  221. package/dist/src/tools/helm/HelmListWithResourcesTool.d.ts +14 -0
  222. package/dist/src/tools/helm/HelmListWithResourcesTool.d.ts.map +1 -0
  223. package/dist/src/tools/helm/HelmListWithResourcesTool.js +175 -0
  224. package/dist/src/tools/helm/HelmListWithResourcesTool.js.map +1 -0
  225. package/dist/src/tools/helm/HelmStatusTool.d.ts +10 -0
  226. package/dist/src/tools/helm/HelmStatusTool.d.ts.map +1 -0
  227. package/dist/src/tools/helm/HelmStatusTool.js +54 -0
  228. package/dist/src/tools/helm/HelmStatusTool.js.map +1 -0
  229. package/dist/src/tools/helm/index.d.ts +12 -0
  230. package/dist/src/tools/helm/index.d.ts.map +1 -0
  231. package/dist/src/tools/helm/index.js +11 -0
  232. package/dist/src/tools/helm/index.js.map +1 -0
  233. package/dist/src/tools/kubernetes/BaseTool.d.ts +48 -0
  234. package/dist/src/tools/kubernetes/BaseTool.d.ts.map +1 -0
  235. package/dist/src/tools/kubernetes/BaseTool.js +55 -0
  236. package/dist/src/tools/kubernetes/BaseTool.js.map +1 -0
  237. package/dist/src/tools/kubernetes/GetConfigMapTool.d.ts +11 -0
  238. package/dist/src/tools/kubernetes/GetConfigMapTool.d.ts.map +1 -0
  239. package/dist/src/tools/kubernetes/GetConfigMapTool.js +28 -0
  240. package/dist/src/tools/kubernetes/GetConfigMapTool.js.map +1 -0
  241. package/dist/src/tools/kubernetes/GetContainerLogsTool.d.ts +15 -0
  242. package/dist/src/tools/kubernetes/GetContainerLogsTool.d.ts.map +1 -0
  243. package/dist/src/tools/kubernetes/GetContainerLogsTool.js +119 -0
  244. package/dist/src/tools/kubernetes/GetContainerLogsTool.js.map +1 -0
  245. package/dist/src/tools/kubernetes/GetDeploymentsTool.d.ts +11 -0
  246. package/dist/src/tools/kubernetes/GetDeploymentsTool.d.ts.map +1 -0
  247. package/dist/src/tools/kubernetes/GetDeploymentsTool.js +30 -0
  248. package/dist/src/tools/kubernetes/GetDeploymentsTool.js.map +1 -0
  249. package/dist/src/tools/kubernetes/GetEventsTool.d.ts +11 -0
  250. package/dist/src/tools/kubernetes/GetEventsTool.d.ts.map +1 -0
  251. package/dist/src/tools/kubernetes/GetEventsTool.js +123 -0
  252. package/dist/src/tools/kubernetes/GetEventsTool.js.map +1 -0
  253. package/dist/src/tools/kubernetes/GetIngressTool.d.ts +11 -0
  254. package/dist/src/tools/kubernetes/GetIngressTool.d.ts.map +1 -0
  255. package/dist/src/tools/kubernetes/GetIngressTool.js +35 -0
  256. package/dist/src/tools/kubernetes/GetIngressTool.js.map +1 -0
  257. package/dist/src/tools/kubernetes/GetMetricsTool.d.ts +11 -0
  258. package/dist/src/tools/kubernetes/GetMetricsTool.d.ts.map +1 -0
  259. package/dist/src/tools/kubernetes/GetMetricsTool.js +40 -0
  260. package/dist/src/tools/kubernetes/GetMetricsTool.js.map +1 -0
  261. package/dist/src/tools/kubernetes/GetNamespacesTool.d.ts +11 -0
  262. package/dist/src/tools/kubernetes/GetNamespacesTool.d.ts.map +1 -0
  263. package/dist/src/tools/kubernetes/GetNamespacesTool.js +33 -0
  264. package/dist/src/tools/kubernetes/GetNamespacesTool.js.map +1 -0
  265. package/dist/src/tools/kubernetes/GetPersistentVolumeClaimsTool.d.ts +11 -0
  266. package/dist/src/tools/kubernetes/GetPersistentVolumeClaimsTool.d.ts.map +1 -0
  267. package/dist/src/tools/kubernetes/GetPersistentVolumeClaimsTool.js +92 -0
  268. package/dist/src/tools/kubernetes/GetPersistentVolumeClaimsTool.js.map +1 -0
  269. package/dist/src/tools/kubernetes/GetPersistentVolumesTool.d.ts +11 -0
  270. package/dist/src/tools/kubernetes/GetPersistentVolumesTool.d.ts.map +1 -0
  271. package/dist/src/tools/kubernetes/GetPersistentVolumesTool.js +88 -0
  272. package/dist/src/tools/kubernetes/GetPersistentVolumesTool.js.map +1 -0
  273. package/dist/src/tools/kubernetes/GetPodMetricsTool.d.ts +11 -0
  274. package/dist/src/tools/kubernetes/GetPodMetricsTool.d.ts.map +1 -0
  275. package/dist/src/tools/kubernetes/GetPodMetricsTool.js +100 -0
  276. package/dist/src/tools/kubernetes/GetPodMetricsTool.js.map +1 -0
  277. package/dist/src/tools/kubernetes/GetPodsTool.d.ts +11 -0
  278. package/dist/src/tools/kubernetes/GetPodsTool.d.ts.map +1 -0
  279. package/dist/src/tools/kubernetes/GetPodsTool.js +35 -0
  280. package/dist/src/tools/kubernetes/GetPodsTool.js.map +1 -0
  281. package/dist/src/tools/kubernetes/GetResourceTool.d.ts +12 -0
  282. package/dist/src/tools/kubernetes/GetResourceTool.d.ts.map +1 -0
  283. package/dist/src/tools/kubernetes/GetResourceTool.js +79 -0
  284. package/dist/src/tools/kubernetes/GetResourceTool.js.map +1 -0
  285. package/dist/src/tools/kubernetes/GetSecretsTool.d.ts +11 -0
  286. package/dist/src/tools/kubernetes/GetSecretsTool.d.ts.map +1 -0
  287. package/dist/src/tools/kubernetes/GetSecretsTool.js +49 -0
  288. package/dist/src/tools/kubernetes/GetSecretsTool.js.map +1 -0
  289. package/dist/src/tools/kubernetes/GetServicesTool.d.ts +11 -0
  290. package/dist/src/tools/kubernetes/GetServicesTool.d.ts.map +1 -0
  291. package/dist/src/tools/kubernetes/GetServicesTool.js +35 -0
  292. package/dist/src/tools/kubernetes/GetServicesTool.js.map +1 -0
  293. package/dist/src/tools/kubernetes/index.d.ts +17 -0
  294. package/dist/src/tools/kubernetes/index.d.ts.map +1 -0
  295. package/dist/src/tools/kubernetes/index.js +16 -0
  296. package/dist/src/tools/kubernetes/index.js.map +1 -0
  297. package/dist/src/utils/CliUtils.d.ts +24 -0
  298. package/dist/src/utils/CliUtils.d.ts.map +1 -0
  299. package/dist/src/utils/CliUtils.js +159 -0
  300. package/dist/src/utils/CliUtils.js.map +1 -0
  301. package/dist/tests/__mocks__/@kubernetes/client-node.d.ts +34 -0
  302. package/dist/tests/__mocks__/@kubernetes/client-node.d.ts.map +1 -0
  303. package/dist/tests/__mocks__/@kubernetes/client-node.js +20 -0
  304. package/dist/tests/__mocks__/@kubernetes/client-node.js.map +1 -0
  305. package/dist/tests/argo/ArgoGetTool.test.d.ts +2 -0
  306. package/dist/tests/argo/ArgoGetTool.test.d.ts.map +1 -0
  307. package/dist/tests/argo/ArgoGetTool.test.js +141 -0
  308. package/dist/tests/argo/ArgoGetTool.test.js.map +1 -0
  309. package/dist/tests/helm/BaseTool.test.d.ts +2 -0
  310. package/dist/tests/helm/BaseTool.test.d.ts.map +1 -0
  311. package/dist/tests/helm/BaseTool.test.js +241 -0
  312. package/dist/tests/helm/BaseTool.test.js.map +1 -0
  313. package/dist/tests/index.test.d.ts +2 -0
  314. package/dist/tests/index.test.d.ts.map +1 -0
  315. package/dist/tests/index.test.js +7 -0
  316. package/dist/tests/index.test.js.map +1 -0
  317. package/dist/tests/kubernetes/ConfigMapOperations.test.d.ts +2 -0
  318. package/dist/tests/kubernetes/ConfigMapOperations.test.d.ts.map +1 -0
  319. package/dist/tests/kubernetes/ConfigMapOperations.test.js +460 -0
  320. package/dist/tests/kubernetes/ConfigMapOperations.test.js.map +1 -0
  321. package/dist/tests/kubernetes/ConnectionManager.test.d.ts +2 -0
  322. package/dist/tests/kubernetes/ConnectionManager.test.d.ts.map +1 -0
  323. package/dist/tests/kubernetes/ConnectionManager.test.js +97 -0
  324. package/dist/tests/kubernetes/ConnectionManager.test.js.map +1 -0
  325. package/dist/tests/kubernetes/ConnectionPool.test.d.ts +2 -0
  326. package/dist/tests/kubernetes/ConnectionPool.test.d.ts.map +1 -0
  327. package/dist/tests/kubernetes/ConnectionPool.test.js +328 -0
  328. package/dist/tests/kubernetes/ConnectionPool.test.js.map +1 -0
  329. package/dist/tests/kubernetes/DeploymentOperations.test.d.ts +2 -0
  330. package/dist/tests/kubernetes/DeploymentOperations.test.d.ts.map +1 -0
  331. package/dist/tests/kubernetes/DeploymentOperations.test.js +196 -0
  332. package/dist/tests/kubernetes/DeploymentOperations.test.js.map +1 -0
  333. package/dist/tests/kubernetes/ErrorHandling.test.d.ts +2 -0
  334. package/dist/tests/kubernetes/ErrorHandling.test.d.ts.map +1 -0
  335. package/dist/tests/kubernetes/ErrorHandling.test.js +329 -0
  336. package/dist/tests/kubernetes/ErrorHandling.test.js.map +1 -0
  337. package/dist/tests/kubernetes/GetPodMetricsTool.test.d.ts +2 -0
  338. package/dist/tests/kubernetes/GetPodMetricsTool.test.d.ts.map +1 -0
  339. package/dist/tests/kubernetes/GetPodMetricsTool.test.js +150 -0
  340. package/dist/tests/kubernetes/GetPodMetricsTool.test.js.map +1 -0
  341. package/dist/tests/kubernetes/KubernetesClient.test.d.ts +2 -0
  342. package/dist/tests/kubernetes/KubernetesClient.test.d.ts.map +1 -0
  343. package/dist/tests/kubernetes/KubernetesClient.test.js +371 -0
  344. package/dist/tests/kubernetes/KubernetesClient.test.js.map +1 -0
  345. package/dist/tests/kubernetes/MetricOperations.parsing.test.d.ts +2 -0
  346. package/dist/tests/kubernetes/MetricOperations.parsing.test.d.ts.map +1 -0
  347. package/dist/tests/kubernetes/MetricOperations.parsing.test.js +49 -0
  348. package/dist/tests/kubernetes/MetricOperations.parsing.test.js.map +1 -0
  349. package/dist/tests/kubernetes/NamespaceOperations.test.d.ts +2 -0
  350. package/dist/tests/kubernetes/NamespaceOperations.test.d.ts.map +1 -0
  351. package/dist/tests/kubernetes/NamespaceOperations.test.js +115 -0
  352. package/dist/tests/kubernetes/NamespaceOperations.test.js.map +1 -0
  353. package/dist/tests/kubernetes/PodOperations.test.d.ts +2 -0
  354. package/dist/tests/kubernetes/PodOperations.test.d.ts.map +1 -0
  355. package/dist/tests/kubernetes/PodOperations.test.js +209 -0
  356. package/dist/tests/kubernetes/PodOperations.test.js.map +1 -0
  357. package/dist/tests/kubernetes/RetryStrategy.test.d.ts +2 -0
  358. package/dist/tests/kubernetes/RetryStrategy.test.d.ts.map +1 -0
  359. package/dist/tests/kubernetes/RetryStrategy.test.js +419 -0
  360. package/dist/tests/kubernetes/RetryStrategy.test.js.map +1 -0
  361. package/dist/tests/kubernetes/ServiceOperations.test.d.ts +2 -0
  362. package/dist/tests/kubernetes/ServiceOperations.test.d.ts.map +1 -0
  363. package/dist/tests/kubernetes/ServiceOperations.test.js +159 -0
  364. package/dist/tests/kubernetes/ServiceOperations.test.js.map +1 -0
  365. package/dist/tests/plugins/KubernetesToolsPlugin.test.d.ts +2 -0
  366. package/dist/tests/plugins/KubernetesToolsPlugin.test.d.ts.map +1 -0
  367. package/dist/tests/plugins/KubernetesToolsPlugin.test.js +153 -0
  368. package/dist/tests/plugins/KubernetesToolsPlugin.test.js.map +1 -0
  369. package/dist/tests/server/MCPServer.integration.test.d.ts +2 -0
  370. package/dist/tests/server/MCPServer.integration.test.d.ts.map +1 -0
  371. package/dist/tests/server/MCPServer.integration.test.js +244 -0
  372. package/dist/tests/server/MCPServer.integration.test.js.map +1 -0
  373. package/dist/tests/server/MCPServer.test.d.ts +2 -0
  374. package/dist/tests/server/MCPServer.test.d.ts.map +1 -0
  375. package/dist/tests/server/MCPServer.test.js +257 -0
  376. package/dist/tests/server/MCPServer.test.js.map +1 -0
  377. package/package.json +85 -0
  378. package/src/cli/cli.ts +25 -0
  379. package/src/cli/run-command.js +391 -0
  380. package/src/index.ts +46 -0
  381. package/src/kubernetes/BaseResourceOperations.ts +286 -0
  382. package/src/kubernetes/CircuitBreaker.ts +485 -0
  383. package/src/kubernetes/ConnectionManager.ts +114 -0
  384. package/src/kubernetes/ConnectionPool.ts +551 -0
  385. package/src/kubernetes/ErrorHandler.ts +436 -0
  386. package/src/kubernetes/ErrorHandling.ts +401 -0
  387. package/src/kubernetes/KubernetesClient.ts +500 -0
  388. package/src/kubernetes/README.md +349 -0
  389. package/src/kubernetes/ResourceOperations.ts +116 -0
  390. package/src/kubernetes/RetryStrategy.ts +365 -0
  391. package/src/kubernetes/RetryableOperation.ts +313 -0
  392. package/src/kubernetes/docs/ResourceOperations.md +606 -0
  393. package/src/kubernetes/index.ts +116 -0
  394. package/src/kubernetes/resources/ConfigMapOperations.ts +367 -0
  395. package/src/kubernetes/resources/CustomResourceOperations.ts +392 -0
  396. package/src/kubernetes/resources/DeploymentOperations.ts +294 -0
  397. package/src/kubernetes/resources/IngressOperations.ts +277 -0
  398. package/src/kubernetes/resources/MetricOperations.ts +1159 -0
  399. package/src/kubernetes/resources/NamespaceOperations.ts +129 -0
  400. package/src/kubernetes/resources/PersistentVolumeClaimOperations.ts +516 -0
  401. package/src/kubernetes/resources/PersistentVolumeOperations.ts +438 -0
  402. package/src/kubernetes/resources/PodOperations.ts +417 -0
  403. package/src/kubernetes/resources/SecretOperations.ts +304 -0
  404. package/src/kubernetes/resources/ServiceOperations.ts +283 -0
  405. package/src/kubernetes/utils/ResourceUtils.ts +553 -0
  406. package/src/plugins/ArgoCDToolsPlugin.ts +189 -0
  407. package/src/plugins/ArgoToolsPlugin.ts +180 -0
  408. package/src/plugins/HelmToolsPlugin.ts +180 -0
  409. package/src/plugins/KubernetesToolsPlugin.ts +198 -0
  410. package/src/plugins/SamplePlugin.ts +60 -0
  411. package/src/plugins/index.ts +4 -0
  412. package/src/server/MCPServer.ts +500 -0
  413. package/src/tools/argo/ArgoCronListTool.ts +78 -0
  414. package/src/tools/argo/ArgoGetTool.ts +91 -0
  415. package/src/tools/argo/ArgoListTool.ts +143 -0
  416. package/src/tools/argo/ArgoLogsTool.ts +131 -0
  417. package/src/tools/argo/BaseTool.ts +69 -0
  418. package/src/tools/argo/index.ts +6 -0
  419. package/src/tools/argocd/ArgoCDAppGetTool.ts +109 -0
  420. package/src/tools/argocd/ArgoCDAppHistoryTool.ts +91 -0
  421. package/src/tools/argocd/ArgoCDAppListTool.ts +144 -0
  422. package/src/tools/argocd/ArgoCDAppLogsTool.ts +162 -0
  423. package/src/tools/argocd/ArgoCDAppResourcesTool.ts +139 -0
  424. package/src/tools/argocd/BaseTool.ts +83 -0
  425. package/src/tools/argocd/index.ts +7 -0
  426. package/src/tools/helm/BaseTool.ts +60 -0
  427. package/src/tools/helm/HelmGetHooksTool.ts +44 -0
  428. package/src/tools/helm/HelmGetManifestTool.ts +44 -0
  429. package/src/tools/helm/HelmGetNotesTool.ts +44 -0
  430. package/src/tools/helm/HelmGetResourcesTool.ts +140 -0
  431. package/src/tools/helm/HelmGetValuesTool.ts +62 -0
  432. package/src/tools/helm/HelmHistoryTool.ts +56 -0
  433. package/src/tools/helm/HelmListTool.ts +107 -0
  434. package/src/tools/helm/HelmListWithResourcesTool.ts +194 -0
  435. package/src/tools/helm/HelmStatusTool.ts +62 -0
  436. package/src/tools/helm/index.ts +11 -0
  437. package/src/tools/kubernetes/BaseTool.ts +76 -0
  438. package/src/tools/kubernetes/GetConfigMapTool.ts +33 -0
  439. package/src/tools/kubernetes/GetContainerLogsTool.ts +140 -0
  440. package/src/tools/kubernetes/GetDeploymentsTool.ts +33 -0
  441. package/src/tools/kubernetes/GetEventsTool.ts +133 -0
  442. package/src/tools/kubernetes/GetIngressTool.ts +39 -0
  443. package/src/tools/kubernetes/GetMetricsTool.ts +53 -0
  444. package/src/tools/kubernetes/GetNamespacesTool.ts +36 -0
  445. package/src/tools/kubernetes/GetPersistentVolumeClaimsTool.ts +113 -0
  446. package/src/tools/kubernetes/GetPersistentVolumesTool.ts +107 -0
  447. package/src/tools/kubernetes/GetPodMetricsTool.ts +113 -0
  448. package/src/tools/kubernetes/GetPodsTool.ts +39 -0
  449. package/src/tools/kubernetes/GetResourceTool.ts +90 -0
  450. package/src/tools/kubernetes/GetSecretsTool.ts +55 -0
  451. package/src/tools/kubernetes/GetServicesTool.ts +39 -0
  452. package/src/tools/kubernetes/index.ts +16 -0
  453. package/src/utils/CliUtils.ts +207 -0
  454. package/tsconfig.json +36 -0
@@ -0,0 +1,1159 @@
1
+ import { KubernetesClient } from '../KubernetesClient.js';
2
+ import { V1ServicePort, V1ServiceList, V1PodList, V1Pod, V1Node } from '@kubernetes/client-node';
3
+ import * as http from 'http'; // Import http module
4
+ import * as https from 'https'; // Import https module
5
+ import { URL } from 'url'; // Import URL for parsing
6
+ import { Logger } from 'winston';
7
+
8
+ // Based on metrics.k8s.io/v1beta1
9
+ interface K8sObjectMeta {
10
+ name: string;
11
+ namespace?: string;
12
+ selfLink?: string;
13
+ creationTimestamp?: string;
14
+ // Add other metadata fields if needed
15
+ }
16
+
17
+ interface ResourceUsage {
18
+ cpu: string; // e.g., "9559630n" (nanocores) or "0.5" (cores)
19
+ memory: string; // e.g., "22244Ki" (kibibytes)
20
+ // Add other resources like ephemeral-storage if needed
21
+ }
22
+
23
+ export interface ContainerMetrics {
24
+ name: string;
25
+ usage: ResourceUsage;
26
+ }
27
+
28
+ export interface PodMetrics {
29
+ kind: 'PodMetrics';
30
+ apiVersion: 'metrics.k8s.io/v1beta1';
31
+ metadata: K8sObjectMeta;
32
+ timestamp: string; // ISO 8601 timestamp
33
+ window: string; // e.g., "30s"
34
+ containers: ContainerMetrics[];
35
+ }
36
+
37
+ export interface NodeMetrics {
38
+ kind: 'NodeMetrics';
39
+ apiVersion: 'metrics.k8s.io/v1beta1';
40
+ metadata: K8sObjectMeta;
41
+ timestamp: string; // ISO 8601 timestamp
42
+ window: string; // e.g., "30s"
43
+ usage: ResourceUsage;
44
+ }
45
+
46
+ // For lists of metrics
47
+ export interface NodeMetricsList {
48
+ kind: 'NodeMetricsList';
49
+ apiVersion: 'metrics.k8s.io/v1beta1';
50
+ metadata: { selfLink?: string; resourceVersion?: string };
51
+ items: NodeMetrics[];
52
+ }
53
+
54
+ export interface PodMetricsList {
55
+ kind: 'PodMetricsList';
56
+ apiVersion: 'metrics.k8s.io/v1beta1';
57
+ metadata: { selfLink?: string; resourceVersion?: string };
58
+ items: PodMetrics[];
59
+ }
60
+
61
+ // Prometheus-related constants
62
+ const PROMETHEUS_ANNOTATION_SCRAPE = 'prometheus.io/scrape';
63
+ const PROMETHEUS_ANNOTATION_PATH = 'prometheus.io/path';
64
+ const PROMETHEUS_ANNOTATION_PORT = 'prometheus.io/port';
65
+
66
+ // Define an interface for a discovered Prometheus target
67
+ export interface PrometheusTarget {
68
+ url: string; // Full URL, e.g., http://service-ip:port/metrics-path
69
+ serviceName: string;
70
+ namespace: string;
71
+ // Potentially add more metadata here later, like scrape interval from annotations
72
+ }
73
+
74
+ // Normalized Metric Structures
75
+ export interface NormalizedResourceUsage {
76
+ cpuCores?: number; // CPU in cores (e.g., 0.5 for half a core)
77
+ memoryBytes?: number; // Memory in bytes
78
+ }
79
+
80
+ export interface NormalizedContainerMetric {
81
+ name: string;
82
+ usage: NormalizedResourceUsage;
83
+ }
84
+
85
+ export interface NormalizedPodMetric {
86
+ name: string;
87
+ namespace: string;
88
+ uid?: string;
89
+ nodeName?: string; // From Pod spec, not metrics directly
90
+ timestamp: string;
91
+ window: string;
92
+ usage: NormalizedResourceUsage;
93
+ containers: NormalizedContainerMetric[];
94
+ // For merged Prometheus metrics
95
+ customMetrics?: NormalizedCustomMetric[];
96
+ }
97
+
98
+ export interface NormalizedNodeMetric {
99
+ name: string;
100
+ uid?: string;
101
+ timestamp: string;
102
+ window: string;
103
+ usage: NormalizedResourceUsage;
104
+ // For merged Prometheus metrics
105
+ customMetrics?: NormalizedCustomMetric[];
106
+ }
107
+
108
+ export interface NormalizedCustomMetric {
109
+ name: string; // The PromQL metric name
110
+ value: number;
111
+ labels?: Record<string, string>; // Prometheus labels
112
+ type?: 'gauge' | 'counter' | 'histogram' | 'summary' | 'untyped'; // Optional: Prometheus metric type
113
+ }
114
+
115
+ export class MetricOperations {
116
+ private k8sClient: KubernetesClient;
117
+ private logger?: Logger;
118
+ private static readonly METRICS_API_GROUP = 'metrics.k8s.io';
119
+ private static readonly METRICS_API_VERSION = 'v1beta1';
120
+ private discoveredPrometheusTargets: PrometheusTarget[] = [];
121
+
122
+ constructor(k8sClient: KubernetesClient, logger?: Logger) {
123
+ this.k8sClient = k8sClient;
124
+ this.logger = logger;
125
+ this.logger?.debug?.('MetricOperations initialized');
126
+ }
127
+
128
+ /**
129
+ * Fetches metrics for all nodes.
130
+ * @returns A promise that resolves to a list of node metrics.
131
+ */
132
+ public async getNodeMetrics(): Promise<NodeMetricsList | null> {
133
+ this.logger?.debug?.('Fetching node metrics using CustomObjectsApi');
134
+ try {
135
+ const response = await this.k8sClient.customObjects.listClusterCustomObject({
136
+ group: MetricOperations.METRICS_API_GROUP,
137
+ version: MetricOperations.METRICS_API_VERSION,
138
+ plural: 'nodes',
139
+ });
140
+ return response.body as NodeMetricsList;
141
+ } catch (error: any) {
142
+ this.logger?.error('Error fetching node metrics:', error.message);
143
+ if (error.body) {
144
+ this.logger?.error('Error body:', JSON.stringify(error.body, null, 2));
145
+ }
146
+ return null;
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Fetches metrics for a specific node.
152
+ * @param nodeName The name of the node.
153
+ * @returns A promise that resolves to the node's metrics.
154
+ */
155
+ public async getNodeMetricsByName(nodeName: string): Promise<NodeMetrics | null> {
156
+ this.logger?.debug?.(`Fetching metrics for node: ${nodeName} using CustomObjectsApi`);
157
+ try {
158
+ const response = await this.k8sClient.customObjects.getClusterCustomObject({
159
+ group: MetricOperations.METRICS_API_GROUP,
160
+ version: MetricOperations.METRICS_API_VERSION,
161
+ plural: 'nodes',
162
+ name: nodeName,
163
+ });
164
+ return response.body as NodeMetrics;
165
+ } catch (error: any) {
166
+ this.logger?.error(`Error fetching metrics for node ${nodeName}:`, error.message);
167
+ if (error.body) {
168
+ this.logger?.error('Error body:', JSON.stringify(error.body, null, 2));
169
+ }
170
+ return null;
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Helper: List all node names in the cluster
176
+ */
177
+ private async listNodes(): Promise<string[]> {
178
+ try {
179
+ const nodes = await this.k8sClient.core.listNode();
180
+ return (nodes.items as V1Node[])
181
+ .map((n: V1Node) => n.metadata?.name)
182
+ .filter(Boolean) as string[];
183
+ } catch (e) {
184
+ this.logger?.error('Failed to list nodes for kubelet summary fallback:', e);
185
+ return [];
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Helper: Convert kubelet summary pod stats to PodMetrics format
191
+ */
192
+ private convertSummaryPodToPodMetrics(pod: any): PodMetrics {
193
+ // Kubelet summary pod object: https://github.com/kubernetes/kubernetes/blob/master/pkg/kubelet/apis/stats/v1alpha1/types.go
194
+ // We'll map the fields as best as possible
195
+ const containers: ContainerMetrics[] = (pod.containers || []).map((c: any) => ({
196
+ name: c.name,
197
+ usage: {
198
+ cpu: c.cpu?.usageNanoCores ? `${c.cpu.usageNanoCores}n` : '0',
199
+ memory: c.memory?.usageBytes ? `${c.memory.usageBytes}` : '0',
200
+ },
201
+ }));
202
+ return {
203
+ kind: 'PodMetrics',
204
+ apiVersion: 'metrics.k8s.io/v1beta1',
205
+ metadata: {
206
+ name: pod.podRef.name,
207
+ namespace: pod.podRef.namespace,
208
+ // selfLink and creationTimestamp not available
209
+ },
210
+ timestamp: pod.startTime || new Date().toISOString(),
211
+ window: '30s', // Not available in summary, use default
212
+ containers,
213
+ };
214
+ }
215
+
216
+ /**
217
+ * Helper: Fetch pod metrics from kubelet summary API for all nodes
218
+ */
219
+ private async getPodMetricsFromKubeletSummary(namespace?: string): Promise<PodMetrics[]> {
220
+ const nodeNames = await this.listNodes();
221
+ const allPodMetrics: PodMetrics[] = [];
222
+ for (const nodeName of nodeNames) {
223
+ try {
224
+ // Use the Kubernetes API proxy to access the kubelet summary endpoint
225
+ const summary = await this.k8sClient.getRaw<any>(
226
+ `/api/v1/nodes/${nodeName}/proxy/stats/summary`,
227
+ );
228
+ if (summary && summary.pods) {
229
+ for (const pod of summary.pods) {
230
+ if (!namespace || pod.podRef.namespace === namespace) {
231
+ allPodMetrics.push(this.convertSummaryPodToPodMetrics(pod));
232
+ }
233
+ }
234
+ }
235
+ } catch (e) {
236
+ this.logger?.warn(`Failed to fetch kubelet summary for node ${nodeName}: ${e}`);
237
+ }
238
+ }
239
+ return allPodMetrics;
240
+ }
241
+
242
+ /**
243
+ * Helper: Fetch pod metrics from metrics.k8s.io API (original implementation)
244
+ */
245
+ private async getPodMetricsFromMetricsApi(namespace?: string): Promise<PodMetricsList | null> {
246
+ this.logger?.debug?.(
247
+ `Fetching pod metrics for namespace: ${namespace || 'all'} using CustomObjectsApi`,
248
+ );
249
+ try {
250
+ let response;
251
+ if (namespace) {
252
+ response = await this.k8sClient.customObjects.listNamespacedCustomObject({
253
+ group: MetricOperations.METRICS_API_GROUP,
254
+ version: MetricOperations.METRICS_API_VERSION,
255
+ namespace: namespace,
256
+ plural: 'pods',
257
+ });
258
+ } else {
259
+ this.logger?.debug?.(
260
+ 'Fetching pod metrics for all namespaces using listClusterCustomObject.',
261
+ );
262
+ response = await this.k8sClient.customObjects.listClusterCustomObject({
263
+ group: MetricOperations.METRICS_API_GROUP,
264
+ version: MetricOperations.METRICS_API_VERSION,
265
+ plural: 'pods',
266
+ });
267
+ }
268
+ return response.body as PodMetricsList;
269
+ } catch (error: any) {
270
+ this.logger?.error('Error fetching pod metrics from metrics.k8s.io:', error.message);
271
+ if (error.body) {
272
+ this.logger?.error('Error body:', JSON.stringify(error.body, null, 2));
273
+ }
274
+ return null;
275
+ }
276
+ }
277
+
278
+ /**
279
+ * Fetches metrics for all pods in a given namespace, or all namespaces if none is specified.
280
+ * Always tries both metrics.k8s.io and kubelet summary API, merging results.
281
+ * @param namespace Optional namespace to filter pods.
282
+ * @returns A promise that resolves to a list of pod metrics.
283
+ */
284
+ public async getPodMetrics(namespace?: string): Promise<PodMetricsList | null> {
285
+ // Attempt 1: metrics.k8s.io API
286
+ const metricsApi = await this.getPodMetricsFromMetricsApi(namespace);
287
+ if (metricsApi && metricsApi.items && metricsApi.items.length > 0) {
288
+ // Primary source succeeded – return as‑is, no merging with kubelet
289
+ return metricsApi;
290
+ }
291
+
292
+ // Attempt 2 (fallback): Kubelet summary API
293
+ const kubeletMetrics = await this.getPodMetricsFromKubeletSummary(namespace);
294
+ if (kubeletMetrics && kubeletMetrics.length > 0) {
295
+ return {
296
+ kind: 'PodMetricsList',
297
+ apiVersion: 'metrics.k8s.io/v1beta1',
298
+ metadata: {},
299
+ items: kubeletMetrics,
300
+ };
301
+ }
302
+
303
+ // No data from either source
304
+ return null;
305
+ }
306
+
307
+ /**
308
+ * Fetches metrics for a specific pod in a given namespace.
309
+ * Uses the namespace-wide getPodMetrics method and filters for the specific pod.
310
+ * This approach is more reliable than direct pod metric API calls.
311
+ * @param podName The name of the pod.
312
+ * @param namespace The namespace of the pod.
313
+ * @returns A promise that resolves to the pod's metrics.
314
+ */
315
+ public async getPodMetricsByName(podName: string, namespace: string): Promise<PodMetrics | null> {
316
+ this.logger?.debug?.(`Fetching metrics for pod: ${podName} in namespace ${namespace}`);
317
+
318
+ try {
319
+ // Use the robust getPodMetrics method which handles all fallbacks
320
+ const podMetricsList = await this.getPodMetrics(namespace);
321
+
322
+ if (podMetricsList && podMetricsList.items && podMetricsList.items.length > 0) {
323
+ // Find the specific pod in the list
324
+ const foundPod = podMetricsList.items.find(
325
+ (pod) =>
326
+ pod.metadata.name === podName &&
327
+ (pod.metadata.namespace === namespace || pod.metadata.namespace === undefined),
328
+ );
329
+
330
+ if (foundPod) {
331
+ this.logger?.debug?.(
332
+ `Successfully found metrics for pod ${podName} in namespace ${namespace}`,
333
+ );
334
+ return foundPod;
335
+ } else {
336
+ this.logger?.debug?.(
337
+ `Pod ${podName} not found in metrics list for namespace ${namespace}`,
338
+ );
339
+ this.logger?.debug?.(
340
+ `Available pods in namespace: ${podMetricsList.items.map((p) => p.metadata.name).join(', ')}`,
341
+ );
342
+ }
343
+ } else {
344
+ this.logger?.debug?.(`No pod metrics available in namespace ${namespace}`);
345
+ }
346
+ } catch (error: any) {
347
+ this.logger?.error(
348
+ `Error fetching metrics for pod ${podName} in namespace ${namespace}: ${error.message}`,
349
+ );
350
+ }
351
+
352
+ return null;
353
+ }
354
+
355
+ /**
356
+ * Discovers Prometheus targets by scanning services for specific annotations.
357
+ * @param namespace Optional namespace to scan. If not provided, scans all namespaces.
358
+ */
359
+ public async discoverPrometheusTargets(namespace?: string): Promise<PrometheusTarget[]> {
360
+ this.logger?.debug?.(
361
+ `Starting Prometheus target discovery in namespace: ${namespace || 'all'}`,
362
+ );
363
+ const newTargets: PrometheusTarget[] = [];
364
+
365
+ try {
366
+ let serviceList: V1ServiceList; // serviceList will directly be V1ServiceList
367
+ if (namespace) {
368
+ // Assuming the method directly returns V1ServiceList
369
+ serviceList = await this.k8sClient.core.listNamespacedService({ namespace });
370
+ } else {
371
+ serviceList = await this.k8sClient.core.listServiceForAllNamespaces();
372
+ }
373
+
374
+ // Now serviceList is V1ServiceList, so access .items directly.
375
+ if (serviceList && serviceList.items) {
376
+ for (const service of serviceList.items) {
377
+ const metadata = service.metadata;
378
+ const annotations = metadata?.annotations;
379
+
380
+ if (annotations && annotations[PROMETHEUS_ANNOTATION_SCRAPE] === 'true') {
381
+ const serviceName = metadata?.name;
382
+ const serviceNamespace = metadata?.namespace;
383
+ const clusterIP = service.spec?.clusterIP;
384
+
385
+ if (!serviceName || !serviceNamespace || !clusterIP || clusterIP === 'None') {
386
+ this.logger?.debug?.(
387
+ `Skipping Prometheus target for service ${serviceName || 'unknown'} in ${serviceNamespace || 'unknown'} due to missing name, namespace, or ClusterIP.`,
388
+ );
389
+ continue;
390
+ }
391
+
392
+ const path = annotations[PROMETHEUS_ANNOTATION_PATH] || '/metrics';
393
+ let portString = annotations[PROMETHEUS_ANNOTATION_PORT];
394
+
395
+ if (!portString && service.spec?.ports && service.spec.ports.length > 0) {
396
+ const httpPort = service.spec.ports.find(
397
+ (p: V1ServicePort) => p.name === 'http' || p.name === 'metrics',
398
+ );
399
+ portString = httpPort ? String(httpPort.port) : String(service.spec.ports[0].port);
400
+ }
401
+
402
+ if (!portString) {
403
+ this.logger?.warn(
404
+ `Could not determine port for Prometheus target ${serviceName} in ${serviceNamespace}. Skipping.`,
405
+ );
406
+ continue;
407
+ }
408
+
409
+ const targetUrl = `http://${clusterIP}:${portString}${path.startsWith('/') ? path : '/' + path}`;
410
+ const newTarget: PrometheusTarget = {
411
+ url: targetUrl,
412
+ serviceName: serviceName,
413
+ namespace: serviceNamespace,
414
+ };
415
+ newTargets.push(newTarget);
416
+ this.logger?.debug?.(
417
+ `Discovered Prometheus target: ${newTarget.url} from service ${serviceNamespace}/${serviceName}`,
418
+ );
419
+ }
420
+ }
421
+ }
422
+ } catch (error: any) {
423
+ this.logger?.error(`Error discovering Prometheus targets: ${error.message}`, error);
424
+ // Depending on desired behavior, might re-throw or return empty/partial list
425
+ }
426
+
427
+ this.discoveredPrometheusTargets = newTargets; // Replace or merge as needed
428
+ this.logger?.debug?.(
429
+ `Prometheus target discovery finished. Found ${newTargets.length} targets.`,
430
+ );
431
+ return this.discoveredPrometheusTargets;
432
+ }
433
+
434
+ /**
435
+ * Queries a single Prometheus target.
436
+ * @param target The Prometheus target to query.
437
+ * @param promqlQuery The PromQL query string.
438
+ * @returns A promise that resolves to the parsed JSON response from Prometheus.
439
+ */
440
+ public async queryPrometheusTarget(
441
+ target: PrometheusTarget,
442
+ promqlQuery: string,
443
+ ): Promise<any | null> {
444
+ const rawQueryUrl = `${target.url.replace(/\/$/, '')}/api/v1/query?query=${encodeURIComponent(promqlQuery)}`;
445
+ this.logger?.debug?.(`Querying Prometheus target: ${rawQueryUrl}`);
446
+
447
+ return new Promise((resolve, _reject) => {
448
+ const parsedUrl = new URL(rawQueryUrl);
449
+ const options = {
450
+ method: 'GET',
451
+ headers: {
452
+ Accept: 'application/json',
453
+ },
454
+ rejectUnauthorized: false,
455
+ };
456
+
457
+ const client = parsedUrl.protocol === 'https:' ? https : http;
458
+
459
+ const req = client.request(parsedUrl, options, (res) => {
460
+ let rawData = '';
461
+ res.setEncoding('utf8');
462
+
463
+ res.on('data', (chunk) => {
464
+ rawData += chunk;
465
+ });
466
+
467
+ res.on('end', () => {
468
+ if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
469
+ try {
470
+ const parsedData = JSON.parse(rawData);
471
+ this.logger?.debug?.(
472
+ `Successfully queried Prometheus target ${target.url} for query: ${promqlQuery}`,
473
+ );
474
+ resolve(parsedData);
475
+ } catch (parseError: any) {
476
+ this.logger?.error(
477
+ `Error parsing JSON response from ${target.url}: ${parseError.message}`,
478
+ parseError,
479
+ );
480
+ resolve(null); // Or reject, depending on desired error handling for parsing issues
481
+ }
482
+ } else {
483
+ this.logger?.error(
484
+ `Error querying Prometheus target ${target.url}: ${res.statusCode} ${res.statusMessage}`,
485
+ );
486
+ this.logger?.error(`Prometheus error response body: ${rawData}`);
487
+ resolve(null); // Or reject for HTTP errors
488
+ }
489
+ });
490
+ });
491
+
492
+ req.on('error', (error: any) => {
493
+ this.logger?.error(
494
+ `Exception querying Prometheus target ${target.url}: ${error.message}`,
495
+ error,
496
+ );
497
+ resolve(null); // Or reject for request errors
498
+ });
499
+
500
+ req.end();
501
+ });
502
+ }
503
+
504
+ /**
505
+ * Fetches metrics from all discovered Prometheus targets for a given query.
506
+ * This is a high-level method that would orchestrate discovery and querying.
507
+ * @param promqlQuery The PromQL query to execute on all targets.
508
+ * @returns A promise that resolves to an array of results from each target.
509
+ */
510
+ public async getPrometheusMetrics(promqlQuery: string): Promise<any[]> {
511
+ if (this.discoveredPrometheusTargets.length === 0) {
512
+ this.logger?.debug?.('No Prometheus targets discovered. Attempting discovery now.');
513
+ await this.discoverPrometheusTargets(); // Discover if not already done
514
+ if (this.discoveredPrometheusTargets.length === 0) {
515
+ this.logger?.warn(
516
+ 'Still no Prometheus targets found after discovery. Cannot query metrics.',
517
+ );
518
+ return [];
519
+ }
520
+ }
521
+
522
+ const allResults: any[] = [];
523
+ for (const target of this.discoveredPrometheusTargets) {
524
+ const result = await this.queryPrometheusTarget(target, promqlQuery);
525
+ if (result) {
526
+ allResults.push({ target: target.url, data: result });
527
+ }
528
+ }
529
+ return allResults;
530
+ }
531
+
532
+ // Helper function to parse K8s resource strings like "100m" or "128974848n" for CPU
533
+ // and "64Mi" or "128974848" for memory into numerical values (cores and bytes).
534
+ private parseK8sCpuString(cpuString?: string): number | undefined {
535
+ if (!cpuString) return undefined;
536
+ let value: number;
537
+ if (cpuString.endsWith('n')) {
538
+ // nanocores
539
+ value = parseFloat(cpuString) / 1e9;
540
+ } else if (cpuString.endsWith('u')) {
541
+ // microcores
542
+ value = parseFloat(cpuString) / 1e6;
543
+ } else if (cpuString.endsWith('m')) {
544
+ // millicores
545
+ value = parseFloat(cpuString) / 1e3;
546
+ } else {
547
+ value = parseFloat(cpuString); // Assuming cores
548
+ }
549
+ return isNaN(value) ? undefined : value; // Return undefined if parsing failed
550
+ }
551
+
552
+ private parseK8sMemoryString(memoryString?: string): number | undefined {
553
+ if (!memoryString) return undefined;
554
+ const suffixes: Record<string, number> = {
555
+ Ki: 1024,
556
+ Mi: 1024 ** 2,
557
+ Gi: 1024 ** 3,
558
+ Ti: 1024 ** 4,
559
+ Pi: 1024 ** 5,
560
+ Ei: 1024 ** 6,
561
+ K: 1000,
562
+ M: 1000 ** 2,
563
+ G: 1000 ** 3,
564
+ T: 1000 ** 4,
565
+ P: 1000 ** 5,
566
+ E: 1000 ** 6,
567
+ };
568
+ const match = memoryString.match(/^(\d+\.?\d*|\.\d+)([KMGTPE]i?)?$/); // Allow decimals
569
+ if (!match) {
570
+ const numericValue = parseFloat(memoryString);
571
+ return isNaN(numericValue) ? undefined : numericValue; // Assuming bytes if no suffix and numeric
572
+ }
573
+
574
+ const value = parseFloat(match[1]);
575
+ if (isNaN(value)) return undefined; // Parsing of numeric part failed
576
+
577
+ const suffix = match[2];
578
+
579
+ if (suffix && suffixes[suffix]) {
580
+ return value * suffixes[suffix];
581
+ }
582
+ return value; // Bytes
583
+ }
584
+
585
+ /**
586
+ * Normalizes and merges metrics from different sources.
587
+ * This is a placeholder and will need actual implementation logic.
588
+ * @param nodeMetricsRaw Raw node metrics from Metrics Server.
589
+ * @param podMetricsRaw Raw pod metrics from Metrics Server.
590
+ * @param prometheusMetricsRaw Raw metrics from Prometheus (array of results from getPrometheusMetrics).
591
+ * @param allPodsRaw Optional: Full pod list for enrichment
592
+ * @returns An object containing lists of normalized node and pod metrics.
593
+ */
594
+ public async normalizeAndMergeMetrics(
595
+ nodeMetricsRaw: NodeMetricsList | null,
596
+ podMetricsRaw: PodMetricsList | null,
597
+ prometheusMetricsRaw: any[], // This will be an array of Prometheus query results
598
+ allPodsRaw?: V1PodList | null, // Optional: Full pod list for enrichment
599
+ ): Promise<{
600
+ nodesMetrics: NormalizedNodeMetric[];
601
+ podsMetrics: NormalizedPodMetric[];
602
+ error?: string;
603
+ }> {
604
+ this.logger?.debug?.('Starting metrics normalization and merging...');
605
+ const normalizedNodes: NormalizedNodeMetric[] = [];
606
+ const normalizedPods: NormalizedPodMetric[] = [];
607
+
608
+ try {
609
+ // Add a try-catch block for the whole normalization process
610
+ // Create a map for quick pod spec lookup if allPodsRaw is provided
611
+ const podSpecMap = new Map<string, V1Pod>();
612
+ if (allPodsRaw && allPodsRaw.items) {
613
+ for (const pod of allPodsRaw.items) {
614
+ if (pod.metadata?.uid) {
615
+ podSpecMap.set(pod.metadata.uid, pod);
616
+ }
617
+ }
618
+ }
619
+
620
+ // Normalize NodeMetrics from Metrics Server
621
+ if (nodeMetricsRaw && Array.isArray(nodeMetricsRaw.items)) {
622
+ for (const rawNode of nodeMetricsRaw.items) {
623
+ normalizedNodes.push({
624
+ name: rawNode.metadata.name,
625
+ uid: rawNode.metadata.name, // Use name as fallback UID
626
+ timestamp: rawNode.timestamp,
627
+ window: rawNode.window,
628
+ usage: {
629
+ cpuCores: this.parseK8sCpuString(rawNode.usage.cpu),
630
+ memoryBytes: this.parseK8sMemoryString(rawNode.usage.memory),
631
+ },
632
+ customMetrics: [], // Initialize for Prometheus metrics
633
+ });
634
+ }
635
+ }
636
+
637
+ // Normalize PodMetrics from Metrics Server
638
+ if (podMetricsRaw && Array.isArray(podMetricsRaw.items)) {
639
+ for (const rawPod of podMetricsRaw.items) {
640
+ const containers: NormalizedContainerMetric[] = rawPod.containers.map((c) => ({
641
+ name: c.name,
642
+ usage: {
643
+ cpuCores: this.parseK8sCpuString(c.usage.cpu),
644
+ memoryBytes: this.parseK8sMemoryString(c.usage.memory),
645
+ },
646
+ }));
647
+
648
+ // Sum container usage for pod-level usage if not directly available or to ensure consistency
649
+ const podUsage: NormalizedResourceUsage = containers.reduce(
650
+ (acc, curr) => {
651
+ return {
652
+ cpuCores: (acc.cpuCores || 0) + (curr.usage.cpuCores || 0),
653
+ memoryBytes: (acc.memoryBytes || 0) + (curr.usage.memoryBytes || 0),
654
+ };
655
+ },
656
+ { cpuCores: 0, memoryBytes: 0 },
657
+ );
658
+
659
+ // Use name and namespace as fallback UID
660
+ const podUID = `${rawPod.metadata.namespace || 'default'}:${rawPod.metadata.name}`;
661
+ let nodeNameFromSpec: string | undefined = undefined;
662
+
663
+ if (podUID) {
664
+ // Try to find matching pod spec by UID if available
665
+ const podSpec = podSpecMap.get(podUID);
666
+ if (podSpec) {
667
+ nodeNameFromSpec = podSpec.spec?.nodeName;
668
+ } else {
669
+ // Fallback: try to match by name and namespace if UID from metrics isn't reliable
670
+ if (
671
+ allPodsRaw &&
672
+ allPodsRaw.items &&
673
+ rawPod.metadata.name &&
674
+ rawPod.metadata.namespace
675
+ ) {
676
+ const fallbackPodSpec = allPodsRaw.items.find(
677
+ (p) =>
678
+ p.metadata?.name === rawPod.metadata.name &&
679
+ p.metadata?.namespace === rawPod.metadata.namespace,
680
+ );
681
+ if (fallbackPodSpec) {
682
+ nodeNameFromSpec = fallbackPodSpec.spec?.nodeName;
683
+ }
684
+ }
685
+ }
686
+ }
687
+
688
+ normalizedPods.push({
689
+ name: rawPod.metadata.name,
690
+ namespace: rawPod.metadata.namespace || 'unknown',
691
+ uid: podUID,
692
+ nodeName: nodeNameFromSpec,
693
+ timestamp: rawPod.timestamp,
694
+ window: rawPod.window,
695
+ usage: podUsage,
696
+ containers: containers,
697
+ customMetrics: [], // Initialize for Prometheus metrics
698
+ });
699
+ }
700
+ }
701
+
702
+ // TODO: Implement merging of Prometheus metrics
703
+ // This will involve iterating prometheusMetricsRaw, parsing PromQL results,
704
+ // and matching them to the correct normalizedNodes or normalizedPods based on labels (e.g., node name, pod name, namespace).
705
+ this.logger?.debug?.(`Prometheus metrics to merge: ${prometheusMetricsRaw.length} results.`);
706
+ for (const promResult of prometheusMetricsRaw) {
707
+ if (
708
+ promResult &&
709
+ promResult.status === 'success' &&
710
+ promResult.data &&
711
+ promResult.data.result
712
+ ) {
713
+ for (const metric of promResult.data.result) {
714
+ // --- Map Prometheus resultType to accepted metric kinds -----------------
715
+ const metricTypeMap: Record<string, NormalizedCustomMetric['type']> = {
716
+ gauge: 'gauge',
717
+ counter: 'counter',
718
+ histogram: 'histogram',
719
+ summary: 'summary',
720
+ untyped: 'untyped',
721
+ vector: 'gauge',
722
+ matrix: 'gauge',
723
+ scalar: 'gauge',
724
+ string: 'untyped',
725
+ };
726
+ const mappedType =
727
+ metricTypeMap[(promResult.data.resultType as string) ?? ''] ?? 'untyped';
728
+
729
+ const metricName = metric.metric.__name__ || 'unknown_metric';
730
+ const valueParsed = parseFloat(metric.value[1]); // [timestamp, value]
731
+ if (Number.isNaN(valueParsed)) {
732
+ // Skip this metric if the value isn't a finite number
733
+ continue;
734
+ }
735
+ const value = valueParsed;
736
+ const labels = { ...metric.metric };
737
+ delete labels.__name__;
738
+
739
+ const customMetric: NormalizedCustomMetric = {
740
+ name: metricName,
741
+ value: value,
742
+ labels: labels,
743
+ type: mappedType,
744
+ };
745
+
746
+ // Example: Attempt to match to a node
747
+ if (labels.node) {
748
+ const node = normalizedNodes.find((n) => n.name === labels.node);
749
+ if (node) {
750
+ node.customMetrics?.push(customMetric);
751
+ }
752
+ } else if (labels.pod_name && labels.namespace) {
753
+ // Or a pod
754
+ const pod = normalizedPods.find(
755
+ (p) => p.name === labels.pod_name && p.namespace === labels.namespace,
756
+ );
757
+ if (pod) {
758
+ pod.customMetrics?.push(customMetric);
759
+ }
760
+ } else if (labels.pod && labels.namespace) {
761
+ // k8s_pod_name from cAdvisor via Prometheus
762
+ const pod = normalizedPods.find(
763
+ (p) => p.name === labels.pod && p.namespace === labels.namespace,
764
+ );
765
+ if (pod) {
766
+ pod.customMetrics?.push(customMetric);
767
+ }
768
+ }
769
+ // Add more matching logic as needed based on common Prometheus label conventions for K8s
770
+ }
771
+ }
772
+ }
773
+
774
+ this.logger?.debug?.('Metrics normalization and merging finished.');
775
+ return { nodesMetrics: normalizedNodes, podsMetrics: normalizedPods };
776
+ } catch (error: any) {
777
+ this.logger?.error(
778
+ `Critical error during metrics normalization and merging: ${error.message}`,
779
+ error,
780
+ );
781
+ // Depending on desired behavior, might re-throw or return empty/partial normalized lists
782
+ // For now, return empty lists to indicate failure but not crash the caller.
783
+ return { nodesMetrics: [], podsMetrics: [], error: error.message };
784
+ }
785
+ }
786
+
787
+ /**
788
+ * Fetches all relevant metrics (Metrics Server, Prometheus), optionally enriches with pod specs,
789
+ * and returns them in a normalized and merged format.
790
+ *
791
+ * This is a high-level convenience method.
792
+ *
793
+ * @param prometheusQueries An array of PromQL queries to execute against discovered Prometheus targets.
794
+ * @param fetchPodSpecs If true, fetches all pod specifications to enrich normalized pod metrics with nodeName. Defaults to true.
795
+ * @param discoveryNamespace Optional namespace to restrict Prometheus discovery and pod spec fetching.
796
+ * @returns A promise that resolves to an object containing lists of normalized node and pod metrics.
797
+ */
798
+ public async getAllNormalizedMetrics(
799
+ prometheusQueries: string[],
800
+ fetchPodSpecs: boolean = true,
801
+ discoveryNamespace?: string,
802
+ ): Promise<{
803
+ nodesMetrics: NormalizedNodeMetric[];
804
+ podsMetrics: NormalizedPodMetric[];
805
+ error?: string;
806
+ }> {
807
+ this.logger?.debug?.(
808
+ 'Starting high-level metric gathering using sequential sources (Metrics API ➔ Prometheus ➔ Kubelet)...',
809
+ );
810
+
811
+ /* -----------------------------------------------------------
812
+ * 0) Optionally fetch full Pod specs for later enrichment
813
+ * -------------------------------------------------------- */
814
+ let allPodsRaw: V1PodList | null = null;
815
+ if (fetchPodSpecs) {
816
+ try {
817
+ this.logger?.debug?.('Fetching all pod specs for enrichment...');
818
+ if (discoveryNamespace) {
819
+ if (typeof this.k8sClient.core.listNamespacedPod === 'function') {
820
+ allPodsRaw = await this.k8sClient.core.listNamespacedPod({
821
+ namespace: discoveryNamespace,
822
+ });
823
+ } else {
824
+ this.logger?.warn(
825
+ 'this.k8sClient.core.listNamespacedPod is not a function. Skipping pod spec fetch.',
826
+ );
827
+ }
828
+ } else {
829
+ if (typeof this.k8sClient.core.listPodForAllNamespaces === 'function') {
830
+ allPodsRaw = await this.k8sClient.core.listPodForAllNamespaces();
831
+ } else {
832
+ this.logger?.warn(
833
+ 'this.k8sClient.core.listPodForAllNamespaces is not a function. Skipping pod spec fetch.',
834
+ );
835
+ }
836
+ }
837
+ this.logger?.debug?.(`Fetched ${allPodsRaw?.items?.length || 0} pod specs.`);
838
+ } catch (error: any) {
839
+ this.logger?.error(`Error fetching pod specs: ${error.message}`, error);
840
+ }
841
+ }
842
+
843
+ /* -----------------------------------------------------------
844
+ * 1) Metrics Server (/apis/metrics.k8s.io/v1beta1)
845
+ * -------------------------------------------------------- */
846
+ let nodeMetricsRaw = null;
847
+ let podMetricsRaw = null;
848
+
849
+ // Try to get node metrics directly via customObjects API
850
+ try {
851
+ this.logger?.debug?.('Attempting to fetch node metrics from metrics.k8s.io API...');
852
+ nodeMetricsRaw = await this.getNodeMetrics();
853
+ if (nodeMetricsRaw) {
854
+ this.logger?.debug?.(
855
+ `Successfully fetched metrics for ${nodeMetricsRaw?.items?.length || 0} nodes.`,
856
+ );
857
+ } else {
858
+ this.logger?.warn('Node metrics API returned null or undefined response.');
859
+ }
860
+ } catch (error: any) {
861
+ this.logger?.warn(`Error fetching node metrics from metrics.k8s.io API: ${error.message}`);
862
+
863
+ // Try direct API endpoint access for Rancher Desktop and similar environments
864
+ try {
865
+ this.logger?.debug?.('Attempting direct API endpoint access for node metrics...');
866
+ const directMetrics = await this.k8sClient.getRaw<any>(
867
+ '/apis/metrics.k8s.io/v1beta1/nodes',
868
+ );
869
+ if (directMetrics && directMetrics.items) {
870
+ this.logger?.debug?.(
871
+ `Successfully fetched metrics for ${directMetrics.items.length} nodes via direct API access.`,
872
+ );
873
+ nodeMetricsRaw = directMetrics;
874
+ }
875
+ } catch (directError: any) {
876
+ this.logger?.warn(`Direct API access for node metrics failed: ${directError.message}`);
877
+ }
878
+ }
879
+
880
+ // Try to get pod metrics via customObjects API
881
+ try {
882
+ this.logger?.debug?.('Attempting to fetch pod metrics from metrics.k8s.io API...');
883
+ podMetricsRaw = await this.getPodMetricsFromMetricsApi(discoveryNamespace);
884
+ if (podMetricsRaw) {
885
+ this.logger?.debug?.(
886
+ `Successfully fetched metrics for ${podMetricsRaw?.items?.length || 0} pods.`,
887
+ );
888
+ } else {
889
+ this.logger?.warn('Pod metrics API returned null or undefined response.');
890
+ }
891
+ } catch (error: any) {
892
+ this.logger?.warn(`Error fetching pod metrics from metrics.k8s.io API: ${error.message}`);
893
+
894
+ // Try direct API endpoint access for Rancher Desktop and similar environments
895
+ try {
896
+ this.logger?.debug?.('Attempting direct API endpoint access for pod metrics...');
897
+ const apiPath = discoveryNamespace
898
+ ? `/apis/metrics.k8s.io/v1beta1/namespaces/${discoveryNamespace}/pods`
899
+ : '/apis/metrics.k8s.io/v1beta1/pods';
900
+ const directMetrics = await this.k8sClient.getRaw<any>(apiPath);
901
+ if (directMetrics && directMetrics.items) {
902
+ this.logger?.debug?.(
903
+ `Successfully fetched metrics for ${directMetrics.items.length} pods via direct API access.`,
904
+ );
905
+ podMetricsRaw = directMetrics;
906
+ }
907
+ } catch (directError: any) {
908
+ this.logger?.warn(`Direct API access for pod metrics failed: ${directError.message}`);
909
+ }
910
+ }
911
+
912
+ const metricsApiHasData =
913
+ (nodeMetricsRaw?.items?.length ?? 0) > 0 || (podMetricsRaw?.items?.length ?? 0) > 0;
914
+
915
+ if (metricsApiHasData) {
916
+ this.logger?.debug?.('Returning metrics gathered from metrics.k8s.io API.');
917
+ return this.normalizeAndMergeMetrics(nodeMetricsRaw, podMetricsRaw, [], allPodsRaw);
918
+ }
919
+
920
+ /* -----------------------------------------------------------
921
+ * 2) Prometheus (if Metrics Server unavailable)
922
+ * -------------------------------------------------------- */
923
+ await this.discoverPrometheusTargets(discoveryNamespace);
924
+
925
+ const prometheusMetricsRaw: any[] = [];
926
+ for (const query of prometheusQueries) {
927
+ const results = await this.getPrometheusMetrics(query);
928
+ if (Array.isArray(results)) {
929
+ prometheusMetricsRaw.push(...results);
930
+ }
931
+ }
932
+
933
+ if (prometheusMetricsRaw.length > 0) {
934
+ this.logger?.debug?.('Returning metrics gathered from Prometheus targets.');
935
+ return this.normalizeAndMergeMetrics(null, null, prometheusMetricsRaw, allPodsRaw);
936
+ }
937
+
938
+ /* -----------------------------------------------------------
939
+ * 3) Kubelet /stats/summary (final fallback)
940
+ * -------------------------------------------------------- */
941
+ const kubeletPodMetrics = await this.getPodMetricsFromKubeletSummary(discoveryNamespace);
942
+ const podMetricsList: PodMetricsList | null =
943
+ kubeletPodMetrics && kubeletPodMetrics.length > 0
944
+ ? {
945
+ kind: 'PodMetricsList',
946
+ apiVersion: 'metrics.k8s.io/v1beta1',
947
+ metadata: {},
948
+ items: kubeletPodMetrics,
949
+ }
950
+ : null;
951
+
952
+ if (podMetricsList) {
953
+ this.logger?.debug?.('Returning metrics gathered from kubelet summary fallback.');
954
+ } else {
955
+ this.logger?.warn('Failed to gather metrics from any source; returning empty lists.');
956
+ }
957
+
958
+ return this.normalizeAndMergeMetrics(null, podMetricsList, [], allPodsRaw);
959
+ }
960
+
961
+ /**
962
+ * Get metrics with specific behavior for clean API calls.
963
+ * This method provides a more direct approach than getAllNormalizedMetrics for direct API access.
964
+ *
965
+ * @param prometheusQueries An array of PromQL queries to execute against discovered Prometheus targets.
966
+ * @param fetchPodSpecs If true, fetches all pod specifications to enrich normalized pod metrics with nodeName. Defaults to true.
967
+ * @param discoveryNamespace Optional namespace to restrict Prometheus discovery and pod spec fetching.
968
+ * @returns A promise that resolves to an object containing lists of normalized node and pod metrics.
969
+ */
970
+ public async getMetricsWithOptions(
971
+ prometheusQueries: string[] = [],
972
+ fetchPodSpecs: boolean = true,
973
+ discoveryNamespace?: string,
974
+ nodeApiPath: string = '/apis/metrics.k8s.io/v1beta1/nodes',
975
+ podApiPath: string = '/apis/metrics.k8s.io/v1beta1/pods',
976
+ ): Promise<{
977
+ normalizedNodes?: NormalizedNodeMetric[];
978
+ normalizedPods?: NormalizedPodMetric[];
979
+ error?: string;
980
+ }> {
981
+ // Try direct API calls first for better performance
982
+ try {
983
+ let nodeMetricsRaw = null;
984
+ let podMetricsRaw = null;
985
+ let allPodsRaw = null;
986
+ let errors: string[] = [];
987
+
988
+ // Get node metrics via direct API call
989
+ try {
990
+ nodeMetricsRaw = await this.k8sClient.getRaw<any>(nodeApiPath);
991
+ this.logger?.debug?.(`Successfully fetched node metrics from ${nodeApiPath}`);
992
+ } catch (error: any) {
993
+ const errorMsg = `Failed to get node metrics via API ${nodeApiPath}: ${error.message}`;
994
+ errors.push(errorMsg);
995
+ this.logger?.warn?.(errorMsg);
996
+ }
997
+
998
+ // Get pod metrics via direct API call
999
+ const podMetricsPath = discoveryNamespace
1000
+ ? `/apis/metrics.k8s.io/v1beta1/namespaces/${discoveryNamespace}/pods`
1001
+ : podApiPath;
1002
+ try {
1003
+ podMetricsRaw = await this.k8sClient.getRaw<any>(podMetricsPath);
1004
+ this.logger?.debug?.(`Successfully fetched pod metrics from ${podMetricsPath}`);
1005
+ } catch (error: any) {
1006
+ const errorMsg = `Failed to get pod metrics via API ${podMetricsPath}: ${error.message}`;
1007
+ errors.push(errorMsg);
1008
+ this.logger?.warn?.(errorMsg);
1009
+ }
1010
+
1011
+ // Get pod specs for enrichment if requested
1012
+ if (fetchPodSpecs) {
1013
+ try {
1014
+ if (discoveryNamespace) {
1015
+ allPodsRaw = await this.k8sClient.core.listNamespacedPod({
1016
+ namespace: discoveryNamespace,
1017
+ });
1018
+ } else {
1019
+ allPodsRaw = await this.k8sClient.core.listPodForAllNamespaces();
1020
+ }
1021
+ this.logger?.debug?.(`Successfully fetched pod specs for enrichment`);
1022
+ } catch (error: any) {
1023
+ const errorMsg = `Failed to get pod specs for enrichment: ${error.message}`;
1024
+ errors.push(errorMsg);
1025
+ this.logger?.warn?.(errorMsg);
1026
+ }
1027
+ }
1028
+
1029
+ // If we got metrics, normalize and return them
1030
+ if (
1031
+ (nodeMetricsRaw && nodeMetricsRaw.items && nodeMetricsRaw.items.length > 0) ||
1032
+ (podMetricsRaw && podMetricsRaw.items && podMetricsRaw.items.length > 0)
1033
+ ) {
1034
+ try {
1035
+ const normalized = await this.normalizeAndMergeMetrics(
1036
+ nodeMetricsRaw,
1037
+ podMetricsRaw,
1038
+ [],
1039
+ allPodsRaw,
1040
+ );
1041
+
1042
+ // Include partial errors if any occurred during API calls
1043
+ const combinedError =
1044
+ errors.length > 0
1045
+ ? normalized.error
1046
+ ? `${normalized.error}; Additional errors: ${errors.join('; ')}`
1047
+ : errors.join('; ')
1048
+ : normalized.error;
1049
+
1050
+ return {
1051
+ normalizedNodes: normalized.nodesMetrics,
1052
+ normalizedPods: normalized.podsMetrics,
1053
+ error: combinedError,
1054
+ };
1055
+ } catch (normalizationError: any) {
1056
+ this.logger?.error?.(`Error during metrics normalization: ${normalizationError.message}`);
1057
+ return {
1058
+ normalizedNodes: [],
1059
+ normalizedPods: [],
1060
+ error: `Normalization failed: ${normalizationError.message}${errors.length > 0 ? `; API errors: ${errors.join('; ')}` : ''}`,
1061
+ };
1062
+ }
1063
+ }
1064
+
1065
+ // If no direct metrics available, log collected errors and try fallback
1066
+ if (errors.length > 0) {
1067
+ this.logger?.warn?.(
1068
+ `Direct API calls failed, attempting fallback. Errors: ${errors.join('; ')}`,
1069
+ );
1070
+ }
1071
+
1072
+ // Fallback: try the high-level getAllNormalizedMetrics method
1073
+ try {
1074
+ const safeDiscoveryNamespace =
1075
+ typeof discoveryNamespace === 'string' ? discoveryNamespace : undefined;
1076
+ const metrics = await this.getAllNormalizedMetrics(
1077
+ prometheusQueries,
1078
+ fetchPodSpecs,
1079
+ safeDiscoveryNamespace,
1080
+ );
1081
+
1082
+ // Include errors from direct API attempts if fallback succeeds
1083
+ const combinedError =
1084
+ errors.length > 0
1085
+ ? metrics.error
1086
+ ? `${metrics.error}; Direct API errors: ${errors.join('; ')}`
1087
+ : `Direct API errors (fallback used): ${errors.join('; ')}`
1088
+ : metrics.error;
1089
+
1090
+ return {
1091
+ normalizedNodes: metrics.nodesMetrics,
1092
+ normalizedPods: metrics.podsMetrics,
1093
+ error: combinedError,
1094
+ };
1095
+ } catch (fallbackError: any) {
1096
+ this.logger?.error?.(`Fallback method also failed: ${fallbackError.message}`);
1097
+ return {
1098
+ normalizedNodes: [],
1099
+ normalizedPods: [],
1100
+ error: `All methods failed. Direct API errors: ${errors.join('; ')}; Fallback error: ${fallbackError.message}`,
1101
+ };
1102
+ }
1103
+ } catch (error: any) {
1104
+ this.logger?.error?.(`Critical error in getMetricsWithOptions: ${error.message}`);
1105
+ return {
1106
+ normalizedNodes: [],
1107
+ normalizedPods: [],
1108
+ error: `Critical error: ${error.message}`,
1109
+ };
1110
+ }
1111
+ }
1112
+
1113
+ /**
1114
+ * Parse CPU value to nanocores for consistent summing
1115
+ * Public method for use by tools that need raw nanocores values
1116
+ */
1117
+ public parseCpuValueToNanocores(cpuString: string): number {
1118
+ if (!cpuString) return 0;
1119
+
1120
+ // Handle different CPU units
1121
+ if (cpuString.endsWith('n')) {
1122
+ return parseInt(cpuString.slice(0, -1), 10);
1123
+ } else if (cpuString.endsWith('u')) {
1124
+ return parseInt(cpuString.slice(0, -1), 10) * 1000;
1125
+ } else if (cpuString.endsWith('m')) {
1126
+ return parseInt(cpuString.slice(0, -1), 10) * 1000000;
1127
+ } else {
1128
+ // Assume cores, convert to nanocores
1129
+ return parseFloat(cpuString) * 1000000000;
1130
+ }
1131
+ }
1132
+
1133
+ /**
1134
+ * Parse memory value to bytes for consistent summing
1135
+ * Public method for use by tools that need raw bytes values
1136
+ */
1137
+ public parseMemoryValueToBytes(memoryString: string): number {
1138
+ if (!memoryString) return 0;
1139
+
1140
+ const value = parseInt(memoryString.replace(/[^\d]/g, ''), 10);
1141
+
1142
+ if (memoryString.includes('Ki')) {
1143
+ return value * 1024;
1144
+ } else if (memoryString.includes('Mi')) {
1145
+ return value * 1024 * 1024;
1146
+ } else if (memoryString.includes('Gi')) {
1147
+ return value * 1024 * 1024 * 1024;
1148
+ } else if (memoryString.includes('Ti')) {
1149
+ return value * 1024 * 1024 * 1024 * 1024;
1150
+ } else {
1151
+ // Assume bytes
1152
+ return value;
1153
+ }
1154
+ }
1155
+
1156
+ // TODO: Implement data normalization and merging
1157
+ // TODO: Implement caching and throttling
1158
+ // TODO: Integrate with AnalysisEngine
1159
+ }