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