Compare commits

...

17 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
6c7b7c4751 Address code review feedback
- Fixed analyzer name in README.md to use consistent 'CustomResource' name
- Improved TestCRDAnalyzer_NilClientConfig to be more explicit about expected behavior
- Added clear comments about what the test verifies

Co-authored-by: AlexsJones <1235925+AlexsJones@users.noreply.github.com>
2026-02-06 07:32:08 +00:00
copilot-swe-agent[bot]
03bd9a8387 Add CRD analyzer documentation and examples
- Created comprehensive documentation in docs/CRD_ANALYZER.md
- Added example configuration file in examples/crd_analyzer_config.yaml
- Updated README.md to list the new customResourceAnalyzer
- Documentation includes use cases for cert-manager, ArgoCD, Kafka, Prometheus
- Added troubleshooting section and best practices

Co-authored-by: AlexsJones <1235925+AlexsJones@users.noreply.github.com>
2026-02-06 07:27:34 +00:00
copilot-swe-agent[bot]
dfdcf9edd2 Add Generic CRD Analyzer implementation with tests
- Added CRDAnalyzerConfig types to pkg/common/types.go for configuration
- Implemented CRD analyzer in pkg/analyzer/crd.go with support for:
  - Discovery of all installed CRDs via apiextensions API
  - Generic health checks based on common patterns (.status.conditions, .status.phase, etc.)
  - Configurable per-CRD health checks via YAML config
  - Detection of stuck resources (deletionTimestamp with finalizers)
- Registered CRDAnalyzer in additionalAnalyzerMap
- Added comprehensive unit tests in pkg/analyzer/crd_test.go
- All tests passing

Co-authored-by: AlexsJones <1235925+AlexsJones@users.noreply.github.com>
2026-02-06 07:25:29 +00:00
copilot-swe-agent[bot]
2253625f40 Initial plan 2026-02-06 07:15:24 +00:00
kk573
c80b2e2c34 fix: use MaxCompletionTokens instead of deprecated MaxTokens for OpenAI (#1604)
The OpenAI API deprecated 'max_tokens' parameter in favor of
'max_completion_tokens' for newer models (o1, gpt-4o, etc.).

This change fixes the error:
'Unsupported parameter: max_tokens is not supported with this model.
Use max_completion_tokens instead.'

Refs: https://platform.openai.com/docs/api-reference/chat/create#chat-create-max_tokens

Signed-off-by: Evgenii Kuzakov <evgeniy.kuzakov@csssr.com>
Co-authored-by: Evgenii Kuzakov <evgeniy.kuzakov@csssr.com>
2026-01-27 17:46:51 +00:00
lif
867bce1907 feat: add Groq as LLM provider (#1600)
Add Groq as a new AI backend provider. Groq provides an OpenAI-compatible
API, so this implementation reuses the existing OpenAI client library
with Groq's API endpoint.

Closes #1269

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: majiayu000 <1835304752@qq.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 12:05:17 +00:00
Alex Jones
f5fb2a7e12 feat: multiple security fixes. Prometheus: v0.302.1 → v0.306.0 (#1597)
Kubernetes client-go: v0.32.2 → v0.32.3 (security patches)
gRPC Gateway: v2.25.1 → v2.26.3
Prometheus Sigv4: v0.1.1 → v0.2.0
OpenTelemetry: Multiple v1.34.0 → v1.36.0 updates
gRPC: v1.70.0 → v1.73.0
Docker SDK: v27.4.1 → v28.3.0
Cloud/Auth SDKs: Multiple security updates

Signed-off-by: AlexsJones <alexsimonjones@gmail.com>
2025-12-22 13:56:02 +00:00
renovate[bot]
a303ffa21c chore(deps): update actions/setup-go digest to 40f1582 (#1593)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-22 08:37:11 +00:00
Alex Jones
21369c5c09 chore: util tests (#1594)
* chore: incremental test coverage

Signed-off-by: AlexsJones <alexsimonjones@gmail.com>

* chore: incremental test coverage

Signed-off-by: AlexsJones <alexsimonjones@gmail.com>

* chore: fixed some security stuff

Signed-off-by: AlexsJones <alexsimonjones@gmail.com>

* chore: fixing linting issues

Signed-off-by: AlexsJones <alexsimonjones@gmail.com>

* chore: fixing linting issues

Signed-off-by: AlexsJones <alexsimonjones@gmail.com>

* chore: fixing linting issues

Signed-off-by: AlexsJones <alexsimonjones@gmail.com>

---------

Signed-off-by: AlexsJones <alexsimonjones@gmail.com>
2025-12-22 08:24:18 +00:00
renovate[bot]
40ffcbec6b chore(deps): update actions/checkout digest to 93cb6ef (#1592)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-18 16:45:32 +00:00
renovate[bot]
7fe3bdbd95 fix(deps): update module gopkg.in/yaml.v2 to v3 (#1550)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-18 16:08:12 +00:00
github-actions[bot]
e7b7a5db47 chore(main): release 0.4.27 (#1590)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-18 13:52:08 +00:00
Alex Jones
5480051230 feat: mcp v2 (#1589)
* feat: bringing in 9 new mcp capabilities

Signed-off-by: AlexsJones <alexsimonjones@gmail.com>

* feat: bringing in 9 new mcp capabilities

Signed-off-by: AlexsJones <alexsimonjones@gmail.com>

* chore: fixed linting issue

Signed-off-by: AlexsJones <alexsimonjones@gmail.com>

---------

Signed-off-by: AlexsJones <alexsimonjones@gmail.com>
2025-12-18 13:11:10 +00:00
github-actions[bot]
ee6f58443b chore(main): release 0.4.26 (#1584)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-10-16 08:31:09 +01:00
Alex Jones
f1d2e306f3 chore: missing filter arg on serve (#1583)
Signed-off-by: AlexsJones <alexsimonjones@gmail.com>
2025-10-16 08:27:39 +01:00
github-actions[bot]
83c5d67084 chore(main): release 0.4.25 (#1576)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-03 20:09:29 +01:00
Alex Jones
291e42dc4b feat: fix to broken inference (#1575)
Signed-off-by: Alex <alexsimonjones@gmail.com>
2025-09-03 20:08:44 +01:00
32 changed files with 3078 additions and 270 deletions

View File

@@ -36,7 +36,7 @@ jobs:
steps:
- name: Check out code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
- name: Extract branch name
id: extract_branch
@@ -68,7 +68,7 @@ jobs:
steps:
- name: Check out code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
- name: Docker meta
id: meta

View File

@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
- name: golangci-lint
uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8

View File

@@ -23,7 +23,7 @@ jobs:
# Release-please creates a PR that tracks all changes
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
- uses: google-github-actions/release-please-action@e4dc86ba9405554aeba3c6bb2d169500e7d3b4ee # v4.1.1
id: release
@@ -55,11 +55,11 @@ jobs:
docker-images: true
swap-storage: true
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with:
go-version: '1.22'
- name: Download Syft
@@ -91,7 +91,7 @@ jobs:
IMAGE_NAME: k8sgpt
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
with:
submodules: recursive

View File

@@ -15,10 +15,10 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with:
go-version: ${{ env.GO_VERSION }}

View File

@@ -1 +1 @@
{".":"0.4.24"}
{".":"0.4.27"}

View File

@@ -1,5 +1,26 @@
# Changelog
## [0.4.27](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.4.26...v0.4.27) (2025-12-18)
### Features
* mcp v2 ([#1589](https://github.com/k8sgpt-ai/k8sgpt/issues/1589)) ([5480051](https://github.com/k8sgpt-ai/k8sgpt/commit/5480051230ce83b89c0382abd7992c7ecc4a85b8))
## [0.4.26](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.4.25...v0.4.26) (2025-10-16)
### Other
* missing filter arg on serve ([#1583](https://github.com/k8sgpt-ai/k8sgpt/issues/1583)) ([f1d2e30](https://github.com/k8sgpt-ai/k8sgpt/commit/f1d2e306f32eb1e01a2788174084be29a7fa1282))
## [0.4.25](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.4.24...v0.4.25) (2025-09-03)
### Features
* fix to broken inference ([#1575](https://github.com/k8sgpt-ai/k8sgpt/issues/1575)) ([291e42d](https://github.com/k8sgpt-ai/k8sgpt/commit/291e42dc4b81ffb0672c21fbb325ddebc5d531a3))
## [0.4.24](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.4.23...v0.4.24) (2025-08-18)

482
MCP.md Normal file
View File

@@ -0,0 +1,482 @@
# K8sGPT Model Context Protocol (MCP) Server
K8sGPT provides a Model Context Protocol (MCP) server that exposes Kubernetes cluster operations as standardized tools, resources, and prompts for AI assistants like Claude, ChatGPT, and other MCP-compatible clients.
## Table of Contents
- [What is MCP?](#what-is-mcp)
- [Quick Start](#quick-start)
- [Server Modes](#server-modes)
- [Available Tools](#available-tools)
- [Available Resources](#available-resources)
- [Available Prompts](#available-prompts)
- [Usage Examples](#usage-examples)
- [Integration with AI Assistants](#integration-with-ai-assistants)
- [HTTP API Reference](#http-api-reference)
## What is MCP?
The Model Context Protocol (MCP) is an open standard that enables AI assistants to securely connect to external data sources and tools. K8sGPT's MCP server exposes Kubernetes operations through this standardized interface, allowing AI assistants to:
- Analyze cluster health and issues
- Query Kubernetes resources
- Access pod logs and events
- Get troubleshooting guidance
- Manage analyzer filters
## Quick Start
### Start the MCP Server
**Stdio mode (for local AI assistants):**
```bash
k8sgpt serve --mcp
```
**HTTP mode (for network access):**
```bash
k8sgpt serve --mcp --mcp-http --mcp-port 8089
```
### Test with curl
```bash
curl -X POST http://localhost:8089/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}'
```
## Server Modes
### Stdio Mode (Default)
Used by local AI assistants like Claude Desktop:
```bash
k8sgpt serve --mcp
```
Configure in your MCP client (e.g., Claude Desktop's `claude_desktop_config.json`):
```json
{
"mcpServers": {
"k8sgpt": {
"command": "k8sgpt",
"args": ["serve", "--mcp"]
}
}
}
```
### HTTP Mode
Used for network access and webhooks:
```bash
k8sgpt serve --mcp --mcp-http --mcp-port 8089
```
The server runs in stateless mode, so no session management is required. Each request is independent.
## Available Tools
The MCP server exposes 12 tools for Kubernetes operations:
### Cluster Analysis
**analyze**
- Analyze Kubernetes resources for issues and problems
- Parameters:
- `namespace` (optional): Namespace to analyze
- `explain` (optional): Get AI explanations for issues
- `filters` (optional): Comma-separated list of analyzers to use
**cluster-info**
- Get Kubernetes cluster information and version
### Resource Management
**list-resources**
- List Kubernetes resources of a specific type
- Parameters:
- `resourceType` (required): Type of resource (pods, deployments, services, nodes, jobs, cronjobs, statefulsets, daemonsets, replicasets, configmaps, secrets, ingresses, pvcs, pvs)
- `namespace` (optional): Namespace to query
- `labelSelector` (optional): Label selector for filtering
**get-resource**
- Get detailed information about a specific Kubernetes resource
- Parameters:
- `resourceType` (required): Type of resource
- `name` (required): Resource name
- `namespace` (optional): Namespace
**list-namespaces**
- List all namespaces in the cluster
### Debugging and Troubleshooting
**get-logs**
- Get logs from a pod container
- Parameters:
- `podName` (required): Name of the pod
- `namespace` (optional): Namespace
- `container` (optional): Container name
- `tail` (optional): Number of lines to show
- `previous` (optional): Show logs from previous container instance
- `sinceSeconds` (optional): Show logs from last N seconds
**list-events**
- List Kubernetes events for debugging
- Parameters:
- `namespace` (optional): Namespace to query
- `involvedObjectName` (optional): Filter by object name
- `involvedObjectKind` (optional): Filter by object kind
### Analyzer Management
**list-filters**
- List all available and active analyzers/filters
**add-filters**
- Add filters to enable specific analyzers
- Parameters:
- `filters` (required): Comma-separated list of analyzer names
**remove-filters**
- Remove filters to disable specific analyzers
- Parameters:
- `filters` (required): Comma-separated list of analyzer names
### Integrations
**list-integrations**
- List available integrations (Prometheus, AWS, Keda, Kyverno, etc.)
### Configuration
**config**
- Configure K8sGPT settings including custom analyzers and cache
## Available Resources
Resources provide read-only access to cluster information:
**cluster-info**
- URI: `cluster-info`
- Get information about the Kubernetes cluster
**namespaces**
- URI: `namespaces`
- List all namespaces in the cluster
**active-filters**
- URI: `active-filters`
- Get currently active analyzers/filters
## Available Prompts
Prompts provide guided troubleshooting workflows:
**troubleshoot-pod**
- Interactive pod debugging workflow
- Arguments:
- `podName` (required): Name of the pod to troubleshoot
- `namespace` (required): Namespace of the pod
**troubleshoot-deployment**
- Interactive deployment debugging workflow
- Arguments:
- `deploymentName` (required): Name of the deployment
- `namespace` (required): Namespace of the deployment
**troubleshoot-cluster**
- General cluster troubleshooting workflow
## Usage Examples
### Example 1: Analyze a Namespace
```bash
curl -X POST http://localhost:8089/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "analyze",
"arguments": {
"namespace": "production",
"explain": "true"
}
}
}'
```
### Example 2: List Pods
```bash
curl -X POST http://localhost:8089/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "list-resources",
"arguments": {
"resourceType": "pods",
"namespace": "default"
}
}
}'
```
### Example 3: Get Pod Logs
```bash
curl -X POST http://localhost:8089/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "get-logs",
"arguments": {
"podName": "nginx-abc123",
"namespace": "default",
"tail": "100"
}
}
}'
```
### Example 4: Access a Resource
```bash
curl -X POST http://localhost:8089/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 4,
"method": "resources/read",
"params": {
"uri": "namespaces"
}
}'
```
### Example 5: Get a Troubleshooting Prompt
```bash
curl -X POST http://localhost:8089/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 5,
"method": "prompts/get",
"params": {
"name": "troubleshoot-pod",
"arguments": {
"podName": "nginx-abc123",
"namespace": "default"
}
}
}'
```
## Integration with AI Assistants
### Claude Desktop
Add to `claude_desktop_config.json`:
```json
{
"mcpServers": {
"k8sgpt": {
"command": "k8sgpt",
"args": ["serve", "--mcp"]
}
}
}
```
Restart Claude Desktop and you'll see k8sgpt tools available in the tool selector.
### Custom MCP Clients
Any MCP-compatible client can connect to the k8sgpt server. For HTTP-based clients:
1. Start the server: `k8sgpt serve --mcp --mcp-http --mcp-port 8089`
2. Connect to: `http://localhost:8089/mcp`
3. Use standard MCP protocol methods: `tools/list`, `tools/call`, `resources/read`, `prompts/get`
## HTTP API Reference
### Endpoint
```
POST http://localhost:8089/mcp
Content-Type: application/json
```
### Request Format
All requests follow the JSON-RPC 2.0 format:
```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "method_name",
"params": {
...
}
}
```
### Discovery Methods
**List Tools**
```json
{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}
```
**List Resources**
```json
{"jsonrpc": "2.0", "id": 2, "method": "resources/list"}
```
**List Prompts**
```json
{"jsonrpc": "2.0", "id": 3, "method": "prompts/list"}
```
### Tool Invocation
```json
{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "tool_name",
"arguments": {
"arg1": "value1",
"arg2": "value2"
}
}
}
```
### Resource Access
```json
{
"jsonrpc": "2.0",
"id": 5,
"method": "resources/read",
"params": {
"uri": "resource_uri"
}
}
```
### Prompt Access
```json
{
"jsonrpc": "2.0",
"id": 6,
"method": "prompts/get",
"params": {
"name": "prompt_name",
"arguments": {
"arg1": "value1"
}
}
}
```
### Response Format
Successful responses:
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
...
}
}
```
Error responses:
```json
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32600,
"message": "Error description"
}
}
```
## Advanced Configuration
### Custom Port
```bash
k8sgpt serve --mcp --mcp-http --mcp-port 9000
```
### With Specific Backend
```bash
k8sgpt serve --mcp --backend openai
```
### With Kubeconfig
```bash
k8sgpt serve --mcp --kubeconfig ~/.kube/config
```
## Troubleshooting
### Connection Issues
Verify the server is running:
```bash
curl http://localhost:8089/mcp
```
### Permission Issues
Ensure your kubeconfig has appropriate cluster access:
```bash
kubectl cluster-info
```
### Tool Errors
List available tools to verify names:
```bash
curl -X POST http://localhost:8089/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}'
```
## Learn More
- [MCP Specification](https://modelcontextprotocol.io/)
- [K8sGPT Documentation](https://docs.k8sgpt.ai/)
- [MCP Go Library](https://github.com/mark3labs/mcp-go)

View File

@@ -34,6 +34,7 @@ _Out of the box integration with OpenAI, Azure, Cohere, Amazon Bedrock, Google G
- [Examples](#examples)
- [LLM AI Backends](#llm-ai-backends)
- [Key Features](#key-features)
- [Model Context Protocol (MCP)](#model-context-protocol-mcp)
- [Documentation](#documentation)
- [Contributing](#contributing)
- [Community](#community)
@@ -62,7 +63,7 @@ brew install k8sgpt
<!---x-release-please-start-version-->
```
sudo rpm -ivh https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.24/k8sgpt_386.rpm
sudo rpm -ivh https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.27/k8sgpt_386.rpm
```
<!---x-release-please-end-->
@@ -70,7 +71,7 @@ brew install k8sgpt
<!---x-release-please-start-version-->
```
sudo rpm -ivh https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.24/k8sgpt_amd64.rpm
sudo rpm -ivh https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.27/k8sgpt_amd64.rpm
```
<!---x-release-please-end-->
</details>
@@ -83,7 +84,7 @@ brew install k8sgpt
<!---x-release-please-start-version-->
```
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.24/k8sgpt_386.deb
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.27/k8sgpt_386.deb
sudo dpkg -i k8sgpt_386.deb
```
@@ -94,7 +95,7 @@ sudo dpkg -i k8sgpt_386.deb
<!---x-release-please-start-version-->
```
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.24/k8sgpt_amd64.deb
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.27/k8sgpt_amd64.deb
sudo dpkg -i k8sgpt_amd64.deb
```
@@ -109,7 +110,7 @@ sudo dpkg -i k8sgpt_amd64.deb
<!---x-release-please-start-version-->
```
wget https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.24/k8sgpt_386.apk
wget https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.27/k8sgpt_386.apk
apk add --allow-untrusted k8sgpt_386.apk
```
<!---x-release-please-end-->
@@ -118,7 +119,7 @@ sudo dpkg -i k8sgpt_amd64.deb
<!---x-release-please-start-version-->
```
wget https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.24/k8sgpt_amd64.apk
wget https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.27/k8sgpt_amd64.apk
apk add --allow-untrusted k8sgpt_amd64.apk
```
<!---x-release-please-end-->
@@ -278,6 +279,7 @@ you will be able to write your own analyzers.
- [x] OperatorGroup
- [x] InstallPlan
- [x] Subscription
- [x] **CustomResource** - Generic analyzer for any CRD (cert-manager, ArgoCD, Kafka, etc.) [Documentation](docs/CRD_ANALYZER.md)
## Examples
@@ -699,7 +701,30 @@ k8sgpt custom-analyzer remove --names "my-custom-analyzer,my-custom-analyzer-2"
```
</details>
## Model Context Protocol (MCP)
K8sGPT provides a Model Context Protocol server that exposes Kubernetes operations as standardized tools for AI assistants like Claude, ChatGPT, and other MCP-compatible clients.
**Start the MCP server:**
Stdio mode (for local AI assistants):
```bash
k8sgpt serve --mcp
```
HTTP mode (for network access):
```bash
k8sgpt serve --mcp --mcp-http --mcp-port 8089
```
**Features:**
- 12 tools for cluster analysis, resource management, and debugging
- 3 resources for cluster information access
- 3 interactive troubleshooting prompts
- Stateless HTTP mode for one-off invocations
- Full integration with Claude Desktop and other MCP clients
**Learn more:** See [MCP.md](MCP.md) for complete documentation, usage examples, and integration guides.
## Documentation
Find our official documentation available [here](https://docs.k8sgpt.ai)

View File

@@ -75,6 +75,9 @@ K8sGPT supports a variety of AI/LLM providers (backends). Some providers have a
- **Supported Models:**
- ibm/granite-13b-chat-v2
### Groq
- **Model:** User-configurable (any model supported by Groq, e.g., `llama-3.3-70b-versatile`, `mixtral-8x7b-32768`)
---
For more details on configuring each provider and model, refer to the official K8sGPT documentation and the provider's own documentation.

View File

@@ -41,6 +41,8 @@ var (
enableMCP bool
mcpPort string
mcpHTTP bool
// filters can be injected into the server (repeatable flag)
filters []string
)
var ServeCmd = &cobra.Command{
@@ -208,6 +210,7 @@ var ServeCmd = &cobra.Command{
EnableHttp: enableHttp,
Token: aiProvider.Password,
Logger: logger,
Filters: filters,
}
go func() {
if err := server.ServeMetrics(); err != nil {
@@ -237,4 +240,6 @@ func init() {
ServeCmd.Flags().BoolVarP(&enableMCP, "mcp", "", false, "Enable Mission Control Protocol server")
ServeCmd.Flags().StringVarP(&mcpPort, "mcp-port", "", "8089", "Port to run the MCP server on")
ServeCmd.Flags().BoolVarP(&mcpHTTP, "mcp-http", "", false, "Enable HTTP mode for MCP server")
// allow injecting filters into the running server (repeatable)
ServeCmd.Flags().StringSliceVar(&filters, "filter", []string{}, "Filter to apply (can be specified multiple times)")
}

View File

@@ -9,7 +9,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
FROM golang:1.23-alpine3.19 AS builder
FROM golang:1.24-alpine3.23 AS builder
ENV CGO_ENABLED=0
ARG VERSION

252
docs/CRD_ANALYZER.md Normal file
View File

@@ -0,0 +1,252 @@
# Generic CRD Analyzer Configuration Examples
The Generic CRD Analyzer enables K8sGPT to automatically analyze custom resources from any installed CRD in your Kubernetes cluster. This provides observability for operator-managed resources like cert-manager, ArgoCD, Kafka, and more.
## Basic Configuration
The CRD analyzer is configured via the K8sGPT configuration file (typically `~/.config/k8sgpt/k8sgpt.yaml`). Here's a minimal example:
```yaml
crd_analyzer:
enabled: true
```
With this basic configuration, the analyzer will:
- Discover all CRDs installed in your cluster
- Apply generic health checks based on common Kubernetes patterns
- Report issues with resources that have unhealthy status conditions
## Configuration Options
### Complete Example
```yaml
crd_analyzer:
enabled: true
include:
- name: certificates.cert-manager.io
statusPath: ".status.conditions"
readyCondition:
type: "Ready"
expectedStatus: "True"
- name: applications.argoproj.io
statusPath: ".status.health.status"
expectedValue: "Healthy"
- name: kafkas.kafka.strimzi.io
readyCondition:
type: "Ready"
expectedStatus: "True"
exclude:
- name: kafkatopics.kafka.strimzi.io
- name: servicemonitors.monitoring.coreos.com
```
### Configuration Fields
#### `enabled` (boolean)
- **Default**: `false`
- **Description**: Master switch to enable/disable the CRD analyzer
- **Example**: `enabled: true`
#### `include` (array)
- **Description**: List of CRDs with custom health check configurations
- **Fields**:
- `name` (string, required): The full CRD name (e.g., `certificates.cert-manager.io`)
- `statusPath` (string, optional): JSONPath to the status field to check (e.g., `.status.health.status`)
- `readyCondition` (object, optional): Configuration for checking a Ready-style condition
- `type` (string): The condition type to check (e.g., `"Ready"`)
- `expectedStatus` (string): Expected status value (e.g., `"True"`)
- `expectedValue` (string, optional): Expected value at the statusPath (requires `statusPath`)
#### `exclude` (array)
- **Description**: List of CRDs to skip during analysis
- **Fields**:
- `name` (string): The full CRD name to exclude
## Use Cases
### 1. cert-manager Certificate Analysis
Detect certificates that are not ready or have issuance failures:
```yaml
crd_analyzer:
enabled: true
include:
- name: certificates.cert-manager.io
readyCondition:
type: "Ready"
expectedStatus: "True"
```
**Detected Issues:**
- Certificates with `Ready=False`
- Certificate renewal failures
- Invalid certificate configurations
### 2. ArgoCD Application Health
Monitor ArgoCD application sync and health status:
```yaml
crd_analyzer:
enabled: true
include:
- name: applications.argoproj.io
statusPath: ".status.health.status"
expectedValue: "Healthy"
```
**Detected Issues:**
- Applications in `Degraded` state
- Sync failures
- Missing resources
### 3. Kafka Operator Resources
Check Kafka cluster health with Strimzi operator:
```yaml
crd_analyzer:
enabled: true
include:
- name: kafkas.kafka.strimzi.io
readyCondition:
type: "Ready"
expectedStatus: "True"
exclude:
- name: kafkatopics.kafka.strimzi.io # Exclude topics to reduce noise
```
**Detected Issues:**
- Kafka clusters not ready
- Broker failures
- Configuration issues
### 4. Prometheus Operator
Monitor Prometheus instances:
```yaml
crd_analyzer:
enabled: true
include:
- name: prometheuses.monitoring.coreos.com
readyCondition:
type: "Available"
expectedStatus: "True"
```
**Detected Issues:**
- Prometheus instances not available
- Configuration reload failures
- Storage issues
## Generic Health Checks
When a CRD is not explicitly configured in the `include` list, the analyzer applies generic health checks:
### Supported Patterns
1. **status.conditions** - Standard Kubernetes conditions
- Flags `Ready` conditions with status != `"True"`
- Flags any condition type containing "failed" with status = `"True"`
2. **status.phase** - Phase-based resources
- Flags resources with phase = `"Failed"` or `"Error"`
3. **status.health.status** - ArgoCD-style health
- Flags resources with health status != `"Healthy"` (except `"Unknown"`)
4. **status.state** - State-based resources
- Flags resources with state = `"Failed"` or `"Error"`
5. **Deletion with Finalizers** - Stuck resources
- Flags resources with `deletionTimestamp` set but still having finalizers
## Running the Analyzer
### Enable in Configuration
Add the CRD analyzer to your active filters:
```bash
# Add CustomResource filter
k8sgpt filters add CustomResource
# List active filters to verify
k8sgpt filters list
```
### Run Analysis
```bash
# Basic analysis
k8sgpt analyze --explain
# With specific filter
k8sgpt analyze --explain --filter=CustomResource
# In a specific namespace
k8sgpt analyze --explain --filter=CustomResource --namespace=production
```
### Example Output
```
AI Provider: openai
0: CustomResource/Certificate(default/example-cert)
- Error: Condition Ready is False (reason: Failed): Certificate issuance failed
- Details: The certificate 'example-cert' in namespace 'default' failed to issue.
The Let's Encrypt challenge validation failed due to DNS propagation issues.
Recommendation: Check DNS records and retry certificate issuance.
1: CustomResource/Application(argocd/my-app)
- Error: Health status is Degraded
- Details: The ArgoCD application 'my-app' is in a Degraded state.
This typically indicates that deployed resources are not healthy.
Recommendation: Check application logs and pod status.
```
## Best Practices
### 1. Start with Generic Checks
Begin with just `enabled: true` to see what issues are detected across all CRDs.
### 2. Add Specific Configurations Gradually
Add custom configurations for critical CRDs that need specialized health checks.
### 3. Use Exclusions to Reduce Noise
Exclude CRDs that generate false positives or are less critical.
### 4. Combine with Other Analyzers
Use the CRD analyzer alongside built-in analyzers for comprehensive cluster observability.
### 5. Monitor Performance
If you have many CRDs, the analysis may take longer. Use exclusions to optimize.
## Troubleshooting
### Analyzer Not Running
- Verify `enabled: true` is set in configuration
- Check that `CustomResource` is in active filters: `k8sgpt filters list`
- Ensure configuration file is in the correct location
### No Issues Detected
- Verify CRDs are actually installed: `kubectl get crds`
- Check if custom resources exist: `kubectl get <crd-name> --all-namespaces`
- Review generic health check patterns - your CRDs may use different status fields
### Too Many False Positives
- Add specific configurations for problematic CRDs in the `include` section
- Use the `exclude` list to skip noisy CRDs
- Review the status patterns your CRDs use and configure accordingly
### Configuration Not Applied
- Restart K8sGPT after configuration changes
- Verify YAML syntax is correct
- Check K8sGPT logs for configuration parsing errors

View File

@@ -0,0 +1,45 @@
# Example K8sGPT Configuration with CRD Analyzer
# Place this file at ~/.config/k8sgpt/k8sgpt.yaml
# CRD Analyzer Configuration
crd_analyzer:
enabled: true
# Specific CRD configurations with custom health checks
include:
# cert-manager certificates
- name: certificates.cert-manager.io
readyCondition:
type: "Ready"
expectedStatus: "True"
# ArgoCD applications
- name: applications.argoproj.io
statusPath: ".status.health.status"
expectedValue: "Healthy"
# Strimzi Kafka clusters
- name: kafkas.kafka.strimzi.io
readyCondition:
type: "Ready"
expectedStatus: "True"
# Prometheus instances
- name: prometheuses.monitoring.coreos.com
readyCondition:
type: "Available"
expectedStatus: "True"
# CRDs to skip during analysis
exclude:
- name: kafkatopics.kafka.strimzi.io
- name: servicemonitors.monitoring.coreos.com
- name: podmonitors.monitoring.coreos.com
- name: prometheusrules.monitoring.coreos.com
# Other K8sGPT configuration...
# ai:
# providers:
# - name: openai
# model: gpt-4
# # ... other AI config

135
go.mod
View File

@@ -1,23 +1,25 @@
module github.com/k8sgpt-ai/k8sgpt
go 1.23.3
go 1.24.1
toolchain go1.24.11
require (
github.com/fatih/color v1.18.0
github.com/kedacore/keda/v2 v2.16.0
github.com/magiconair/properties v1.8.9
github.com/mittwald/go-helm-client v0.12.14
github.com/ollama/ollama v0.5.1
github.com/ollama/ollama v0.13.4
github.com/sashabaranov/go-openai v1.36.0
github.com/schollz/progressbar/v3 v3.17.1
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.10.0
golang.org/x/term v0.30.0
golang.org/x/term v0.33.0
helm.sh/helm/v3 v3.17.4
k8s.io/api v0.32.2
k8s.io/apimachinery v0.32.2
k8s.io/client-go v0.32.2
k8s.io/api v0.32.3
k8s.io/apimachinery v0.32.3
k8s.io/client-go v0.32.3
k8s.io/kubectl v0.32.2 // indirect
)
@@ -30,9 +32,9 @@ require (
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc-ecosystem/gateway/v2 v2.24.0-20241118152629-1379a5a1889d.1
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.5.1-20241118152629-1379a5a1889d.1
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.35.2-20241118152629-1379a5a1889d.1
cloud.google.com/go/storage v1.48.0
cloud.google.com/go/storage v1.50.0
cloud.google.com/go/vertexai v0.13.2
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.5.0
github.com/IBM/watsonx-go v1.0.1
github.com/agiledragon/gomonkey/v2 v2.13.0
@@ -41,18 +43,19 @@ require (
github.com/aws/aws-sdk-go-v2/config v1.29.14
github.com/aws/aws-sdk-go-v2/service/bedrock v1.33.0
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.30.0
github.com/aws/smithy-go v1.22.2
github.com/cohere-ai/cohere-go/v2 v2.12.2
github.com/go-logr/zapr v1.3.0
github.com/google/generative-ai-go v0.19.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3
github.com/hupe1980/go-huggingface v0.0.15
github.com/kyverno/policy-reporter-kyverno-plugin v1.6.4
github.com/mark3labs/mcp-go v0.36.0
github.com/olekukonko/tablewriter v0.0.5
github.com/oracle/oci-go-sdk/v65 v65.79.0
github.com/prometheus/prometheus v0.302.1
github.com/prometheus/prometheus v0.306.0
github.com/pterm/pterm v0.12.80
google.golang.org/api v0.218.0
google.golang.org/api v0.239.0
gopkg.in/yaml.v2 v2.4.0
sigs.k8s.io/controller-runtime v0.19.3
sigs.k8s.io/gateway-api v1.2.1
@@ -62,23 +65,23 @@ require (
atomicgo.dev/cursor v0.2.0 // indirect
atomicgo.dev/keyboard v0.2.9 // indirect
atomicgo.dev/schedule v0.1.0 // indirect
cel.dev/expr v0.19.0 // indirect
cloud.google.com/go v0.116.0 // indirect
cel.dev/expr v0.23.0 // indirect
cloud.google.com/go v0.120.0 // indirect
cloud.google.com/go/ai v0.8.0 // indirect
cloud.google.com/go/aiplatform v1.69.0 // indirect
cloud.google.com/go/auth v0.14.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
cloud.google.com/go/compute/metadata v0.6.0 // indirect
cloud.google.com/go/iam v1.2.2 // indirect
cloud.google.com/go/longrunning v0.6.2 // indirect
cloud.google.com/go/monitoring v1.21.2 // indirect
cloud.google.com/go/aiplatform v1.85.0 // indirect
cloud.google.com/go/auth v0.16.2 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.7.0 // indirect
cloud.google.com/go/iam v1.5.2 // indirect
cloud.google.com/go/longrunning v0.6.7 // indirect
cloud.google.com/go/monitoring v1.24.2 // indirect
dario.cat/mergo v1.0.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0 // indirect
github.com/Microsoft/hcsshim v0.12.4 // indirect
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect
@@ -92,33 +95,30 @@ require (
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // indirect
github.com/aws/smithy-go v1.22.2 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f // indirect
github.com/containerd/console v1.0.4 // indirect
github.com/containerd/continuity v0.4.3 // indirect
github.com/containerd/errdefs v0.3.0 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v0.2.1 // indirect
github.com/creack/pty v1.1.21 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
github.com/envoyproxy/go-control-plane v0.13.1 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
github.com/expr-lang/expr v1.17.2 // indirect
github.com/expr-lang/expr v1.17.7 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
github.com/gofrs/flock v0.12.1 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/googleapis/gax-go/v2 v2.14.2 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect
@@ -132,30 +132,31 @@ require (
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/prometheus/sigv4 v0.1.1 // indirect
github.com/prometheus/sigv4 v0.2.0 // indirect
github.com/sagikazarmark/locafero v0.6.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/segmentio/fasthash v1.0.3 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/sony/gobreaker v0.5.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
go.opencensus.io v0.24.0 // indirect
github.com/zeebo/errs v1.4.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.32.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.35.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
knative.dev/pkg v0.0.0-20241026180704-25f6002b00f3 // indirect
@@ -174,12 +175,12 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chai2010/gettext-go v1.0.3 // indirect
github.com/containerd/containerd v1.7.24 // indirect
github.com/containerd/containerd v1.7.29 // indirect
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/cli v26.1.4+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v27.4.1+incompatible // indirect
github.com/docker/docker v28.3.0+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.2 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-metrics v0.0.1 // indirect
@@ -199,7 +200,7 @@ require (
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/gnostic v0.7.0
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.6.0 // indirect
@@ -214,7 +215,7 @@ require (
github.com/jmoiron/sqlx v1.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/lib/pq v1.10.9 // indirect
@@ -241,10 +242,10 @@ require (
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.21.0-rc.0
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/client_golang v1.23.0-rc.1
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.65.1-0.20250703115700-7f8b2a0d32d3 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/robfig/cron/v3 v3.0.1
github.com/rubenv/sql-migrate v1.7.1 // indirect
@@ -259,20 +260,20 @@ require (
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect
golang.org/x/net v0.38.0
golang.org/x/oauth2 v0.25.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/time v0.9.0 // indirect
google.golang.org/grpc v1.70.0
google.golang.org/protobuf v1.36.4 // indirect
golang.org/x/crypto v0.40.0 // indirect
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect
golang.org/x/net v0.42.0
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.36.0 // indirect
golang.org/x/text v0.27.0 // indirect
golang.org/x/time v0.12.0 // indirect
google.golang.org/grpc v1.73.0
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
k8s.io/apiextensions-apiserver v0.32.2

336
go.sum
View File

@@ -16,8 +16,8 @@ buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.5.1-20241118152629-1379a5a1889d.1 h
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.5.1-20241118152629-1379a5a1889d.1/go.mod h1:rlbkTkVN2P3aNR0U/7N5d9/uvNW8/dzHwtJDfPzh2vc=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.35.2-20241118152629-1379a5a1889d.1 h1:Z+fW0kWryP6LdjP+z+d1/WT4tObrq890aye4aPIh6hM=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.35.2-20241118152629-1379a5a1889d.1/go.mod h1:dqopmdpTDT6p9kPTxVCgR8WDnNb1SjZjwzaNj/kRbps=
cel.dev/expr v0.19.0 h1:lXuo+nDhpyJSpWxpPVi5cPUwzKb+dsdOiw6IreM5yt0=
cel.dev/expr v0.19.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cel.dev/expr v0.23.0 h1:wUb94w6OYQS4uXraxo9U+wUAs9jT47Xvl4iPgAwM2ss=
cel.dev/expr v0.23.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
@@ -56,8 +56,8 @@ cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRY
cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM=
cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I=
cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY=
cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA=
cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q=
cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4=
cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw=
cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E=
@@ -73,8 +73,8 @@ cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9j
cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ=
cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k=
cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw=
cloud.google.com/go/aiplatform v1.69.0 h1:XvBzK8e6/6ufbi/i129Vmn/gVqFwbNPmRQ89K+MGlgc=
cloud.google.com/go/aiplatform v1.69.0/go.mod h1:nUsIqzS3khlnWvpjfJbP+2+h+VrFyYsTm7RNCAViiY8=
cloud.google.com/go/aiplatform v1.85.0 h1:80/GqdP8Tovaaw9Qr6fYZNDvwJeA9rLk8mYkqBJNIJQ=
cloud.google.com/go/aiplatform v1.85.0/go.mod h1:S4DIKz3TFLSt7ooF2aCRdAqsUR4v/YDXUoHqn5P0EFc=
cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI=
cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4=
cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M=
@@ -123,10 +123,10 @@ cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVo
cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo=
cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0=
cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E=
cloud.google.com/go/auth v0.14.0 h1:A5C4dKV/Spdvxcl0ggWwWEzzP7AZMJSEIgrkngwhGYM=
cloud.google.com/go/auth v0.14.0/go.mod h1:CYsoRL1PdiDuqeQpZE0bP2pnPrGqFcOkI0nldEQis+A=
cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M=
cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc=
cloud.google.com/go/auth v0.16.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4=
cloud.google.com/go/auth v0.16.2/go.mod h1:sRBas2Y1fB1vZTdurouM0AzuYQBMZinrUYL8EufhtEA=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0=
cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8=
cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8=
@@ -205,8 +205,8 @@ cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZ
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU=
cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo=
cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY=
cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck=
cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w=
@@ -340,8 +340,8 @@ cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGE
cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY=
cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY=
cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0=
cloud.google.com/go/iam v1.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA=
cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY=
cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8=
cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE=
cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc=
cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A=
cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk=
@@ -371,13 +371,13 @@ cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6
cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo=
cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw=
cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M=
cloud.google.com/go/logging v1.12.0 h1:ex1igYcGFd4S/RZWOCU51StlIEuey5bjqwH9ZYjHibk=
cloud.google.com/go/logging v1.12.0/go.mod h1:wwYBt5HlYP1InnrtYI0wtwttpVU1rifnMT7RejksUAM=
cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc=
cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA=
cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE=
cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc=
cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo=
cloud.google.com/go/longrunning v0.6.2 h1:xjDfh1pQcWPEvnfjZmwjKQEcHnpz6lHjfy7Fo0MK+hc=
cloud.google.com/go/longrunning v0.6.2/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI=
cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=
cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY=
cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE=
cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM=
cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA=
@@ -401,8 +401,8 @@ cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhI
cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4=
cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w=
cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw=
cloud.google.com/go/monitoring v1.21.2 h1:FChwVtClH19E7pJ+e0xUhJPGksctZNVOk2UhMmblmdU=
cloud.google.com/go/monitoring v1.21.2/go.mod h1:hS3pXvaG8KgWTSz+dAdyzPrGUYmi2Q+WFX8g2hqVEZU=
cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM=
cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U=
cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA=
cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o=
cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM=
@@ -566,8 +566,8 @@ cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeL
cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s=
cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y=
cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4=
cloud.google.com/go/storage v1.48.0 h1:FhBDHACbVtdPx7S/AbcKujPWiHvfO6F8OXGgCEbB2+o=
cloud.google.com/go/storage v1.48.0/go.mod h1:aFoDYNMAjv67lp+xcuZqjUKv/ctmplzQ3wJgodA7b+M=
cloud.google.com/go/storage v1.50.0 h1:3TbVkzTooBvnZsk7WaAQfOsNrdoM8QHusXA1cpk6QJs=
cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv482poyafY=
cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w=
cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I=
cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4=
@@ -587,8 +587,8 @@ cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg
cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y=
cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA=
cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk=
cloud.google.com/go/trace v1.11.2 h1:4ZmaBdL8Ng/ajrgKqY5jfvzqMXbrDcBsUGXOT9aqTtI=
cloud.google.com/go/trace v1.11.2/go.mod h1:bn7OwXd4pd5rFuAnTrzBuoZ4ax2XQeG3qNgYmfCy0Io=
cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4=
cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI=
cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs=
cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg=
cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0=
@@ -644,14 +644,14 @@ gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zum
git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 h1:1mvYtZfWQAnwNah/C+Z+Jb9rQH95LPE2vlmMuWAHJk8=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1/go.mod h1:75I/mXtme1JyWFtz8GocPHVFyH421IBoZErnO16dd0k=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1 h1:Bk5uOhSAenHyR5P61D/NzeQCv+4fEVV8mOkJ82NqpWw=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1/go.mod h1:QZ4pw3or1WPmRBxf0cHd1tknzrT54WPBOQoGutCPvSU=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.7.0 h1:LkHbJbgF3YyvC53aqYGR+wWQDn2Rdp9AQdGndf9QvY4=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.7.0/go.mod h1:QyiQdW4f4/BIfB8ZutZ2s+28RAgfa/pT+zS++ZHyM1I=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4 v4.3.0 h1:bXwSugBiSbgtz7rOtbfGf+woewp4f06orW9OP5BjHLA=
@@ -664,8 +664,8 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ=
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
@@ -674,14 +674,14 @@ github.com/Code-Hex/go-generics-cache v1.5.1 h1:6vhZGc5M7Y/YD8cIUcY8kcuQLB4cHR7U
github.com/Code-Hex/go-generics-cache v1.5.1/go.mod h1:qxcC9kRVrct9rHeiYpFWSoW1vxyillCVzX13KZG8dl4=
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 h1:UQ0AhxogsIRZDkElkblfnwjc3IaltCm2HUMvezQaL7s=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1/go.mod h1:jyqM3eLpJ3IbIFDTKVz2rF9T/xWGW0rIriGwnz8l9Tk=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.48.1 h1:oTX4vsorBZo/Zdum6OKPA4o7544hm6smoRv1QjpTwGo=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.48.1/go.mod h1:0wEl7vrAD8mehJyohS9HZy+WyEOaQO2mJx86Cvh93kM=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 h1:8nn+rsCvTq9axyEh382S0PFLBeaFwNsT43IrPWzctRU=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1/go.mod h1:viRWSEhtMZqz1rhwmOVKkWl6SwmVowfL9O2YR5gI2PE=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0 h1:5IT7xOdq17MtcdtL/vtl6mGfzhaq4m4vpollPRmlsBQ=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0/go.mod h1:ZV4VOm0/eHR06JLrXWe09068dHpr3TRpY9Uo7T+anuA=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.50.0 h1:nNMpRpnkWDAaqcpxMJvxa/Ud98gjbYwayJY4/9bdjiU=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.50.0/go.mod h1:SZiPHWGOOk3bl8tkevxkoiwPgsIl6CwrWcbwjfHZpdM=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0 h1:ig/FpDD2JofP/NExKQUbn7uOSZzJAQqogfqluZK4ed4=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0=
github.com/IBM/watsonx-go v1.0.1 h1:Juj90I8ZpJWR/Oq4ISJzQhMJwI6q+DAaZy+f/8W7KDA=
github.com/IBM/watsonx-go v1.0.1/go.mod h1:8lzvpe/158JkrzvcoIcIj6OdNty5iC9co5nQHfkhRtM=
github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk=
@@ -791,7 +791,6 @@ github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXe
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -818,8 +817,8 @@ github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI=
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k=
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/cohere-ai/cohere-go/v2 v2.12.2 h1:8WJqqcCe3q6TB1CdhgzJOgRO2ouno8xcYcOoeWtI8Pk=
github.com/cohere-ai/cohere-go/v2 v2.12.2/go.mod h1:MuiJkCxlR18BDV2qQPbz2Yb/OCVphT1y6nD2zYaKeR0=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
@@ -828,12 +827,12 @@ github.com/containerd/cgroups/v3 v3.0.2/go.mod h1:JUgITrzdFqp42uI2ryGA+ge0ap/nxz
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/containerd/containerd v1.7.24 h1:zxszGrGjrra1yYJW/6rhm9cJ1ZQ8rkKBR48brqsa7nA=
github.com/containerd/containerd v1.7.24/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw=
github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8=
github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
github.com/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4=
github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/containerd v1.7.29 h1:90fWABQsaN9mJhGkoVnuzEY+o1XDPbg9BTC9QTAHnuE=
github.com/containerd/containerd v1.7.29/go.mod h1:azUkWcOvHrWvaiUjSQH0fjzuHIwSPg1WL5PshGP4Szs=
github.com/containerd/continuity v0.4.4 h1:/fNVfTJ7wIl/YPMHjf+5H32uFhl63JucB34PlCpMKII=
github.com/containerd/continuity v0.4.4/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
@@ -850,8 +849,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/digitalocean/godo v1.132.0 h1:n0x6+ZkwbyQBtIU1wwBhv26EINqHg0wWQiBXlwYg/HQ=
github.com/digitalocean/godo v1.132.0/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc=
github.com/digitalocean/godo v1.157.0 h1:ReELaS6FxXNf8gryUiVH0wmyUmZN8/NCmBX4gXd3F0o=
github.com/digitalocean/godo v1.157.0/go.mod h1:tYeiWY5ZXVpU48YaFv0M5irUFHXGorZpDNm7zzdWMzM=
github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc=
github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
@@ -888,8 +887,12 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34=
github.com/envoyproxy/go-control-plane v0.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE=
github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw=
github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M=
github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA=
github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A=
github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw=
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo=
github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
@@ -901,8 +904,8 @@ github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0
github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4=
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc=
github.com/expr-lang/expr v1.17.2 h1:o0A99O/Px+/DTjEnQiodAgOIK9PPxL8DtXhBRKC+Iso=
github.com/expr-lang/expr v1.17.2/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
github.com/expr-lang/expr v1.17.7 h1:Q0xY/e/2aCIp8g9s/LGvMDCC5PxYlvHgDZRQ4y16JX8=
github.com/expr-lang/expr v1.17.7/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
@@ -931,6 +934,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs=
github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw=
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U=
github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk=
@@ -951,8 +956,8 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
github.com/go-resty/resty/v2 v2.16.3 h1:zacNT7lt4b8M/io2Ahj6yPypL7bqx9n1iprfQuodV+E=
github.com/go-resty/resty/v2 v2.16.3/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
@@ -1038,8 +1043,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -1068,8 +1073,8 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18=
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
@@ -1084,8 +1089,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
@@ -1097,8 +1102,8 @@ github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqE
github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY=
github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8=
github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0=
github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w=
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
@@ -1106,8 +1111,8 @@ github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
github.com/gophercloud/gophercloud v1.14.1 h1:DTCNaTVGl8/cFu58O1JwWgis9gtISAFONqpMKNg/Vpw=
github.com/gophercloud/gophercloud/v2 v2.4.0 h1:XhP5tVEH3ni66NSNK1+0iSO6kaGPH/6srtx6Cr+8eCg=
github.com/gophercloud/gophercloud/v2 v2.4.0/go.mod h1:uJWNpTgJPSl2gyzJqcU/pIAhFUWvIkp8eE8M15n9rs4=
github.com/gophercloud/gophercloud/v2 v2.7.0 h1:o0m4kgVcPgHlcXiWAjoVxGd8QCmvM5VU+YM71pFbn0E=
github.com/gophercloud/gophercloud/v2 v2.7.0/go.mod h1:Ki/ILhYZr/5EPebrPL9Ej+tUg4lqx71/YH2JWVeU+Qk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
@@ -1124,10 +1129,10 @@ github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:Fecb
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
github.com/hashicorp/consul/api v1.31.0 h1:32BUNLembeSRek0G/ZAM6WNfdEwYdYo8oQ4+JoqGkNQ=
github.com/hashicorp/consul/api v1.31.0/go.mod h1:2ZGIiXM3A610NmDULmCHd/aqBJj8CkMfOhswhOafxRg=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
github.com/hashicorp/consul/api v1.32.0 h1:5wp5u780Gri7c4OedGEPzmlUEzi0g2KyiPphSr6zjVg=
github.com/hashicorp/consul/api v1.32.0/go.mod h1:Z8YgY0eVPukT/17ejW+l+C7zJmKwgPHtjU1q16v/Y40=
github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A=
github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -1155,8 +1160,8 @@ github.com/hashicorp/nomad/api v0.0.0-20241218080744-e3ac00f30eec h1:+YBzb977Vrm
github.com/hashicorp/nomad/api v0.0.0-20241218080744-e3ac00f30eec/go.mod h1:svtxn6QnrQ69P23VvIWMR34tg3vmwLz4UdUzm1dSCgE=
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
github.com/hetznercloud/hcloud-go/v2 v2.18.0 h1:BemrVGeWI8Kn/pvaC1jBsHZxQMnRqOydS7Ju4BERB4Q=
github.com/hetznercloud/hcloud-go/v2 v2.18.0/go.mod h1:r5RTzv+qi8IbLcDIskTzxkFIji7Ovc8yNgepQR9M+UA=
github.com/hetznercloud/hcloud-go/v2 v2.21.1 h1:IH3liW8/cCRjfJ4cyqYvw3s1ek+KWP8dl1roa0lD8JM=
github.com/hetznercloud/hcloud-go/v2 v2.21.1/go.mod h1:XOaYycZJ3XKMVWzmqQ24/+1V7ormJHmPdck/kxrNnQA=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/hupe1980/go-huggingface v0.0.15 h1:tTWmUGGunC/BYz4hrwS8SSVtMYVYjceG2uhL8HxeXvw=
@@ -1170,8 +1175,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/ionos-cloud/sdk-go/v6 v6.3.2 h1:2mUmrZZz6cPyT9IRX0T8fBLc/7XU/eTxP2Y5tS7/09k=
github.com/ionos-cloud/sdk-go/v6 v6.3.2/go.mod h1:SXrO9OGyWjd2rZhAhEpdYN6VUAODzzqRdqA9BCviQtI=
github.com/ionos-cloud/sdk-go/v6 v6.3.4 h1:jTvGl4LOF8v8OYoEIBNVwbFoqSGAFqn6vGE7sp7/BqQ=
github.com/ionos-cloud/sdk-go/v6 v6.3.4/go.mod h1:wCVwNJ/21W29FWFUv+fNawOTMlFoP1dS3L+ZuztFW48=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
@@ -1195,14 +1200,14 @@ github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kedacore/keda/v2 v2.16.0 h1:0ZoqAeGHORh0B/BOBLDf6fRVvgc5ATeuQCgEm6bNViM=
github.com/kedacore/keda/v2 v2.16.0/go.mod h1:17Yth2jUQvi5KZGGIRmL4ZlwFZY/0FDxIG5jSa+IjpY=
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs=
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw=
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
@@ -1234,8 +1239,8 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/linode/linodego v1.46.0 h1:+uOG4SD2MIrhbrLrvOD5HrbdLN3D19Wgn3MgdUNQjeU=
github.com/linode/linodego v1.46.0/go.mod h1:vyklQRzZUWhFVBZdYx4dcYJU/gG9yKB9VUcUs6ub0Lk=
github.com/linode/linodego v1.52.2 h1:N9ozU27To1LMSrDd8WvJZ5STSz1eGYdyLnxhAR/dIZg=
github.com/linode/linodego v1.52.2/go.mod h1:bI949fZaVchjWyKIA08hNyvAcV6BAS+PM2op3p7PAWA=
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
@@ -1257,11 +1262,12 @@ github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE=
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE=
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=
github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
@@ -1308,8 +1314,8 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/ollama/ollama v0.5.1 h1:Ug4y/5UZZoTgetMklZslAlEdaCnYEX9qZJ/aTsM4+xc=
github.com/ollama/ollama v0.5.1/go.mod h1:wrgnDTdogU9yeFOj/Jc8BpRBJrWu+Ox4eGyHxqiaQDc=
github.com/ollama/ollama v0.13.4 h1:COb7S3+mvXkAHG7vxqeD7uhAPJ/UCAn7OeGkiBBCo98=
github.com/ollama/ollama v0.13.4/go.mod h1:2VxohsKICsmUCrBjowf+luTXYiXn2Q70Cnvv5Urbzkw=
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
@@ -1320,8 +1326,8 @@ github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQ
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/oracle/oci-go-sdk/v65 v65.79.0 h1:Tv9L1XTKWkdXtSViMbP+dA93WunquvW++/2s5pOvOgU=
github.com/oracle/oci-go-sdk/v65 v65.79.0/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0=
github.com/ovh/go-ovh v1.6.0 h1:ixLOwxQdzYDx296sXcgS35TOPEahJkpjMGtzPadCjQI=
github.com/ovh/go-ovh v1.6.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c=
github.com/ovh/go-ovh v1.9.0 h1:6K8VoL3BYjVV3In9tPJUdT7qMx9h0GExN9EXx1r2kKE=
github.com/ovh/go-ovh v1.9.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
@@ -1351,27 +1357,27 @@ github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjz
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_golang v1.21.0-rc.0 h1:bR+RxBlwcr4q8hXkgSOA/J18j6n0/qH0Gb0DH+8c+RY=
github.com/prometheus/client_golang v1.21.0-rc.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
github.com/prometheus/client_golang v1.23.0-rc.1 h1:Is/nGODd8OsJlNQSybeYBwY/B6aHrN7+QwVUYutHSgw=
github.com/prometheus/client_golang v1.23.0-rc.1/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/common v0.65.1-0.20250703115700-7f8b2a0d32d3 h1:R/zO7ombSHCI8bjQusgCMSL+cE669w5/R2upq5WlPD0=
github.com/prometheus/common v0.65.1-0.20250703115700-7f8b2a0d32d3/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/prometheus v0.302.1 h1:xqVdrwrB4WNpdgJqxsz5loqFWNUZitsK8myqLuSZ6Ag=
github.com/prometheus/prometheus v0.302.1/go.mod h1:YcyCoTbUR/TM8rY3Aoeqr0AWTu/pu1Ehh+trpX3eRzg=
github.com/prometheus/sigv4 v0.1.1 h1:UJxjOqVcXctZlwDjpUpZ2OiMWJdFijgSofwLzO1Xk0Q=
github.com/prometheus/sigv4 v0.1.1/go.mod h1:RAmWVKqx0bwi0Qm4lrKMXFM0nhpesBcenfCtz9qRyH8=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/prometheus/prometheus v0.306.0 h1:Q0Pvz/ZKS6vVWCa1VSgNyNJlEe8hxdRlKklFg7SRhNw=
github.com/prometheus/prometheus v0.306.0/go.mod h1:7hMSGyZHt0dcmZ5r4kFPJ/vxPQU99N5/BGwSPDxeZrQ=
github.com/prometheus/sigv4 v0.2.0 h1:qDFKnHYFswJxdzGeRP63c4HlH3Vbn1Yf/Ao2zabtVXk=
github.com/prometheus/sigv4 v0.2.0/go.mod h1:D04rqmAaPPEUkjRQxGqjoxdyJuyCh6E0M18fZr0zBiE=
github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI=
github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg=
github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE=
@@ -1381,8 +1387,8 @@ github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5b
github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s=
github.com/pterm/pterm v0.12.80 h1:mM55B+GnKUnLMUSqhdINe4s6tOuVQIetQ3my8JGyAIg=
github.com/pterm/pterm v0.12.80/go.mod h1:c6DeF9bSnOSeFPZlfs4ZRAFcf5SCoTwvwQ5xaKGQlHo=
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI=
github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
@@ -1407,8 +1413,8 @@ github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6g
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sashabaranov/go-openai v1.36.0 h1:fcSrn8uGuorzPWCBp8L0aCR95Zjb/Dd+ZSML0YZy9EI=
github.com/sashabaranov/go-openai v1.36.0/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30 h1:yoKAVkEVwAqbGbR8n87rHQ1dulL25rKloGadb3vm770=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30/go.mod h1:sH0u6fq6x4R5M7WxkoQFY/o7UaiItec0o1LinLCJNq8=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33 h1:KhF0WejiUTDbL5X55nXowP7zNopwpowa6qaMAWyIE+0=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33/go.mod h1:792k1RTU+5JeMXm35/e2Wgp71qPH/DmDoZrRc+EFZDk=
github.com/schollz/progressbar/v3 v3.17.1 h1:bI1MTaoQO+v5kzklBjYNRQLoVpe0zbyRZNK6DFkVC5U=
github.com/schollz/progressbar/v3 v3.17.1/go.mod h1:RzqpnsPQNjUyIgdglUjRLgD7sVnxN1wpmBMV+UiEbL4=
github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM=
@@ -1441,6 +1447,10 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE=
github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g=
github.com/stackitcloud/stackit-sdk-go/core v0.17.2 h1:jPyn+i8rkp2hM80+hOg0B/1EVRbMt778Tr5RWyK1m2E=
github.com/stackitcloud/stackit-sdk-go/core v0.17.2/go.mod h1:8KIw3czdNJ9sdil9QQimxjR6vHjeINFrRv0iZ67wfn0=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -1497,6 +1507,8 @@ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPS
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM=
github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
@@ -1509,24 +1521,24 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/detectors/gcp v1.32.0 h1:P78qWqkLSShicHmAzfECaTgvslqHxblNE9j62Ws1NK8=
go.opentelemetry.io/contrib/detectors/gcp v1.32.0/go.mod h1:TVqo0Sda4Cv8gCIixd7LuLwW4EylumVWfhjZJjDD4DU=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/contrib/detectors/gcp v1.35.0 h1:bGvFt68+KTiAKFlacHW6AhA56GF2rS0bdD3aJYEnmzA=
go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVLloR1uo5ZYAslM4O6OqUi1DA=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
@@ -1547,8 +1559,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1564,8 +1576,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA=
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4=
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@@ -1607,8 +1619,8 @@ golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1667,8 +1679,8 @@ golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1697,8 +1709,8 @@ golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri
golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec=
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1715,8 +1727,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1801,8 +1813,8 @@ golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -1811,8 +1823,8 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1829,16 +1841,16 @@ golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1902,8 +1914,8 @@ golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1979,8 +1991,8 @@ google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/
google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI=
google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0=
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
google.golang.org/api v0.218.0 h1:x6JCjEWeZ9PFCRe9z0FBrNwj7pB7DOAqT35N+IPnAUA=
google.golang.org/api v0.218.0/go.mod h1:5VGHBAkxrA/8EFjLVEYmMUJ8/8+gWWQ3s4cFH0FxG2M=
google.golang.org/api v0.239.0 h1:2hZKUnFZEy81eugPs4e2XzIJ5SOwQg0G82bpXD65Puo=
google.golang.org/api v0.239.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -2121,16 +2133,16 @@ google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOl
google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
google.golang.org/genproto v0.0.0-20230525234025-438c736192d0/go.mod h1:9ExIQyXL5hZrHzQceCwuSYwZZ5QZBazOcprJ5rgs3lY=
google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk=
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk=
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc=
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78=
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk=
google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8=
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA=
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -2170,8 +2182,8 @@ google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCD
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -2190,8 +2202,8 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -2228,18 +2240,18 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
k8s.io/api v0.32.2 h1:bZrMLEkgizC24G9eViHGOPbW+aRo9duEISRIJKfdJuw=
k8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y=
k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
k8s.io/apiextensions-apiserver v0.32.2 h1:2YMk285jWMk2188V2AERy5yDwBYrjgWYggscghPCvV4=
k8s.io/apiextensions-apiserver v0.32.2/go.mod h1:GPwf8sph7YlJT3H6aKUWtd0E+oyShk/YHWQHf/OOgCA=
k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ=
k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/apiserver v0.32.2 h1:WzyxAu4mvLkQxwD9hGa4ZfExo3yZZaYzoYvvVDlM6vw=
k8s.io/apiserver v0.32.2/go.mod h1:PEwREHiHNU2oFdte7BjzA1ZyjWjuckORLIK/wLV5goM=
k8s.io/cli-runtime v0.32.2 h1:aKQR4foh9qeyckKRkNXUccP9moxzffyndZAvr+IXMks=
k8s.io/cli-runtime v0.32.2/go.mod h1:a/JpeMztz3xDa7GCyyShcwe55p8pbcCVQxvqZnIwXN8=
k8s.io/client-go v0.32.2 h1:4dYCD4Nz+9RApM2b/3BtVvBHw54QjMFUl1OLcJG5yOA=
k8s.io/client-go v0.32.2/go.mod h1:fpZ4oJXclZ3r2nDOv+Ux3XcJutfrwjKTCHz2H3sww94=
k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU=
k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
k8s.io/component-base v0.32.2 h1:1aUL5Vdmu7qNo4ZsE+569PV5zFatM9hl+lb3dEea2zU=
k8s.io/component-base v0.32.2/go.mod h1:PXJ61Vx9Lg+P5mS8TLd7bCIr+eMJRQTyXe8KvkrvJq0=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=

View File

@@ -458,26 +458,25 @@ func (a *AmazonBedRockClient) Configure(config IAIConfig) error {
// Get the inference profile details
profile, err := a.getInferenceProfile(context.Background(), modelInput)
if err != nil {
// Instead of using a fallback model, throw an error
return fmt.Errorf("failed to get inference profile: %v", err)
} else {
// Extract the model ID from the inference profile
modelID, err := a.extractModelFromInferenceProfile(profile)
if err != nil {
return fmt.Errorf("failed to extract model ID from inference profile: %v", err)
}
// Find the model configuration for the extracted model ID
foundModel, err := a.getModelFromString(modelID)
if err != nil {
// Instead of using a fallback model, throw an error
return fmt.Errorf("failed to find model configuration for %s: %v", modelID, err)
}
a.model = foundModel
// Use the inference profile ARN as the model ID for API calls
a.model.Config.ModelName = modelInput
}
// Extract the model ID from the inference profile
modelID, err := a.extractModelFromInferenceProfile(profile)
if err != nil {
return fmt.Errorf("failed to extract model ID from inference profile: %v", err)
}
// Find the model configuration for the extracted model ID
foundModel, err := a.getModelFromString(modelID)
if err != nil {
// Instead of failing, use a generic config for completion/response
// But still warn user
return fmt.Errorf("failed to find model configuration for %s: %v", modelID, err)
}
// Use the found model config for completion/response, but set ModelName to the profile ARN
a.model = foundModel
a.model.Config.ModelName = modelInput
// Mark that we're using an inference profile
// (could add a field if needed)
} else {
// Regular model ID provided
foundModel, err := a.getModelFromString(modelInput)
@@ -562,7 +561,8 @@ func (a *AmazonBedRockClient) GetCompletion(ctx context.Context, prompt string)
supportedModels[i] = m.Name
}
if !bedrock_support.IsModelSupported(a.model.Config.ModelName, supportedModels) {
// Allow valid inference profile ARNs as supported models
if !bedrock_support.IsModelSupported(a.model.Config.ModelName, supportedModels) && !validateInferenceProfileArn(a.model.Config.ModelName) {
return "", fmt.Errorf("model '%s' is not supported.\nSupported models:\n%s", a.model.Config.ModelName, func() string {
s := ""
for _, m := range supportedModels {

102
pkg/ai/groq.go Normal file
View File

@@ -0,0 +1,102 @@
/*
Copyright 2023 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ai
import (
"context"
"errors"
"net/http"
"net/url"
"github.com/sashabaranov/go-openai"
)
const groqAIClientName = "groq"
// Default Groq API endpoint (OpenAI-compatible)
const groqAPIBaseURL = "https://api.groq.com/openai/v1"
type GroqClient struct {
nopCloser
client *openai.Client
model string
temperature float32
topP float32
}
func (c *GroqClient) Configure(config IAIConfig) error {
token := config.GetPassword()
defaultConfig := openai.DefaultConfig(token)
proxyEndpoint := config.GetProxyEndpoint()
baseURL := config.GetBaseURL()
if baseURL != "" {
defaultConfig.BaseURL = baseURL
} else {
defaultConfig.BaseURL = groqAPIBaseURL
}
transport := &http.Transport{}
if proxyEndpoint != "" {
proxyUrl, err := url.Parse(proxyEndpoint)
if err != nil {
return err
}
transport.Proxy = http.ProxyURL(proxyUrl)
}
customHeaders := config.GetCustomHeaders()
defaultConfig.HTTPClient = &http.Client{
Transport: &OpenAIHeaderTransport{
Origin: transport,
Headers: customHeaders,
},
}
client := openai.NewClientWithConfig(defaultConfig)
if client == nil {
return errors.New("error creating Groq client")
}
c.client = client
c.model = config.GetModel()
c.temperature = config.GetTemperature()
c.topP = config.GetTopP()
return nil
}
func (c *GroqClient) GetCompletion(ctx context.Context, prompt string) (string, error) {
resp, err := c.client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{
Model: c.model,
Messages: []openai.ChatCompletionMessage{
{
Role: "user",
Content: prompt,
},
},
Temperature: c.temperature,
MaxTokens: maxToken,
PresencePenalty: presencePenalty,
FrequencyPenalty: frequencyPenalty,
TopP: c.topP,
})
if err != nil {
return "", err
}
return resp.Choices[0].Message.Content, nil
}
func (c *GroqClient) GetName() string {
return groqAIClientName
}

View File

@@ -34,6 +34,7 @@ var (
&OCIGenAIClient{},
&CustomRestClient{},
&IBMWatsonxAIClient{},
&GroqClient{},
}
Backends = []string{
openAIClientName,
@@ -50,6 +51,7 @@ var (
ociClientName,
CustomRestClientName,
ibmWatsonxAIClientName,
groqAIClientName,
}
)

View File

@@ -95,7 +95,7 @@ func (c *OpenAIClient) GetCompletion(ctx context.Context, prompt string) (string
},
},
Temperature: c.temperature,
MaxTokens: maxToken,
MaxCompletionTokens: maxToken,
PresencePenalty: presencePenalty,
FrequencyPenalty: frequencyPenalty,
TopP: c.topP,

View File

@@ -64,6 +64,7 @@ var additionalAnalyzerMap = map[string]common.IAnalyzer{
"InstallPlan": InstallPlanAnalyzer{},
"CatalogSource": CatalogSourceAnalyzer{},
"OperatorGroup": OperatorGroupAnalyzer{},
"CustomResource": CRDAnalyzer{},
}
func ListFilters() ([]string, []string, []string) {

330
pkg/analyzer/crd.go Normal file
View File

@@ -0,0 +1,330 @@
/*
Copyright 2023 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyzer
import (
"fmt"
"strings"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/spf13/viper"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
)
type CRDAnalyzer struct{}
func (CRDAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
// Load CRD analyzer configuration
var config common.CRDAnalyzerConfig
if err := viper.UnmarshalKey("crd_analyzer", &config); err != nil {
// If no config or error, disable the analyzer
return nil, nil
}
if !config.Enabled {
return nil, nil
}
// Create apiextensions client to discover CRDs
apiExtClient, err := apiextensionsclientset.NewForConfig(a.Client.GetConfig())
if err != nil {
return nil, fmt.Errorf("failed to create apiextensions client: %w", err)
}
// List all CRDs in the cluster
crdList, err := apiExtClient.ApiextensionsV1().CustomResourceDefinitions().List(a.Context, metav1.ListOptions{})
if err != nil {
return nil, fmt.Errorf("failed to list CRDs: %w", err)
}
var results []common.Result
// Process each CRD
for _, crd := range crdList.Items {
// Check if CRD should be excluded
if shouldExcludeCRD(crd.Name, config.Exclude) {
continue
}
// Get the CRD configuration (if specified)
crdConfig := getCRDConfig(crd.Name, config.Include)
// Analyze resources for this CRD
crdResults, err := analyzeCRDResources(a, crd, crdConfig)
if err != nil {
// Log error but continue with other CRDs
continue
}
results = append(results, crdResults...)
}
return results, nil
}
// shouldExcludeCRD checks if a CRD should be excluded from analysis
func shouldExcludeCRD(crdName string, excludeList []common.CRDExcludeConfig) bool {
for _, exclude := range excludeList {
if exclude.Name == crdName {
return true
}
}
return false
}
// getCRDConfig returns the configuration for a specific CRD if it exists
func getCRDConfig(crdName string, includeList []common.CRDIncludeConfig) *common.CRDIncludeConfig {
for _, include := range includeList {
if include.Name == crdName {
return &include
}
}
return nil
}
// analyzeCRDResources analyzes all instances of a CRD
func analyzeCRDResources(a common.Analyzer, crd apiextensionsv1.CustomResourceDefinition, config *common.CRDIncludeConfig) ([]common.Result, error) {
if a.Client.GetDynamicClient() == nil {
return nil, fmt.Errorf("dynamic client is nil")
}
// Get the preferred version (typically the storage version)
var version string
for _, v := range crd.Spec.Versions {
if v.Storage {
version = v.Name
break
}
}
if version == "" && len(crd.Spec.Versions) > 0 {
version = crd.Spec.Versions[0].Name
}
// Construct GVR
gvr := schema.GroupVersionResource{
Group: crd.Spec.Group,
Version: version,
Resource: crd.Spec.Names.Plural,
}
// List resources
var list *unstructured.UnstructuredList
var err error
if crd.Spec.Scope == apiextensionsv1.NamespaceScoped {
if a.Namespace != "" {
list, err = a.Client.GetDynamicClient().Resource(gvr).Namespace(a.Namespace).List(a.Context, metav1.ListOptions{LabelSelector: a.LabelSelector})
} else {
list, err = a.Client.GetDynamicClient().Resource(gvr).Namespace(metav1.NamespaceAll).List(a.Context, metav1.ListOptions{LabelSelector: a.LabelSelector})
}
} else {
// Cluster-scoped
list, err = a.Client.GetDynamicClient().Resource(gvr).List(a.Context, metav1.ListOptions{LabelSelector: a.LabelSelector})
}
if err != nil {
return nil, err
}
var results []common.Result
// Analyze each resource instance
for _, item := range list.Items {
failures := analyzeResource(item, crd, config)
if len(failures) > 0 {
resourceName := item.GetName()
if item.GetNamespace() != "" {
resourceName = item.GetNamespace() + "/" + resourceName
}
results = append(results, common.Result{
Kind: crd.Spec.Names.Kind,
Name: resourceName,
Error: failures,
})
}
}
return results, nil
}
// analyzeResource analyzes a single CR instance for issues
func analyzeResource(item unstructured.Unstructured, crd apiextensionsv1.CustomResourceDefinition, config *common.CRDIncludeConfig) []common.Failure {
var failures []common.Failure
// Check for deletion with finalizers (resource stuck in deletion)
if item.GetDeletionTimestamp() != nil && len(item.GetFinalizers()) > 0 {
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Resource is being deleted but has finalizers: %v", item.GetFinalizers()),
})
}
// If custom config is provided, use it
if config != nil {
configFailures := analyzeWithConfig(item, config)
failures = append(failures, configFailures...)
return failures
}
// Otherwise, use generic health checks based on common patterns
genericFailures := analyzeGenericHealth(item)
failures = append(failures, genericFailures...)
return failures
}
// analyzeWithConfig analyzes a resource using custom configuration
func analyzeWithConfig(item unstructured.Unstructured, config *common.CRDIncludeConfig) []common.Failure {
var failures []common.Failure
// Check ReadyCondition if specified
if config.ReadyCondition != nil {
conditions, found, err := unstructured.NestedSlice(item.Object, "status", "conditions")
if !found || err != nil {
failures = append(failures, common.Failure{
Text: "Expected status.conditions not found",
})
return failures
}
ready := false
var conditionMessages []string
for _, cond := range conditions {
condMap, ok := cond.(map[string]interface{})
if !ok {
continue
}
condType, _, _ := unstructured.NestedString(condMap, "type")
status, _, _ := unstructured.NestedString(condMap, "status")
message, _, _ := unstructured.NestedString(condMap, "message")
if condType == config.ReadyCondition.Type {
if status == config.ReadyCondition.ExpectedStatus {
ready = true
} else {
conditionMessages = append(conditionMessages, fmt.Sprintf("%s=%s: %s", condType, status, message))
}
}
}
if !ready {
msg := fmt.Sprintf("Ready condition not met: expected %s=%s", config.ReadyCondition.Type, config.ReadyCondition.ExpectedStatus)
if len(conditionMessages) > 0 {
msg += "; " + strings.Join(conditionMessages, "; ")
}
failures = append(failures, common.Failure{
Text: msg,
})
}
}
// Check ExpectedValue if specified and StatusPath provided
if config.ExpectedValue != "" && config.StatusPath != "" {
pathParts := strings.Split(config.StatusPath, ".")
// Remove leading dot if present
if len(pathParts) > 0 && pathParts[0] == "" {
pathParts = pathParts[1:]
}
actualValue, found, err := unstructured.NestedString(item.Object, pathParts...)
if !found || err != nil {
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Expected field %s not found", config.StatusPath),
})
} else if actualValue != config.ExpectedValue {
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Field %s has value '%s', expected '%s'", config.StatusPath, actualValue, config.ExpectedValue),
})
}
}
return failures
}
// analyzeGenericHealth applies generic health checks based on common Kubernetes patterns
func analyzeGenericHealth(item unstructured.Unstructured) []common.Failure {
var failures []common.Failure
// Check for status.conditions (common pattern)
conditions, found, err := unstructured.NestedSlice(item.Object, "status", "conditions")
if found && err == nil && len(conditions) > 0 {
for _, cond := range conditions {
condMap, ok := cond.(map[string]interface{})
if !ok {
continue
}
condType, _, _ := unstructured.NestedString(condMap, "type")
status, _, _ := unstructured.NestedString(condMap, "status")
reason, _, _ := unstructured.NestedString(condMap, "reason")
message, _, _ := unstructured.NestedString(condMap, "message")
// Check for common failure patterns
if condType == "Ready" && status != "True" {
msg := fmt.Sprintf("Condition Ready is %s", status)
if reason != "" {
msg += fmt.Sprintf(" (reason: %s)", reason)
}
if message != "" {
msg += fmt.Sprintf(": %s", message)
}
failures = append(failures, common.Failure{Text: msg})
} else if strings.Contains(strings.ToLower(condType), "failed") && status == "True" {
msg := fmt.Sprintf("Condition %s is True", condType)
if message != "" {
msg += fmt.Sprintf(": %s", message)
}
failures = append(failures, common.Failure{Text: msg})
}
}
}
// Check for status.phase (common pattern)
phase, found, _ := unstructured.NestedString(item.Object, "status", "phase")
if found && phase != "" {
lowerPhase := strings.ToLower(phase)
if lowerPhase == "failed" || lowerPhase == "error" {
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Resource phase is %s", phase),
})
}
}
// Check for status.health.status (ArgoCD pattern)
healthStatus, found, _ := unstructured.NestedString(item.Object, "status", "health", "status")
if found && healthStatus != "" {
if healthStatus != "Healthy" && healthStatus != "Unknown" {
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Health status is %s", healthStatus),
})
}
}
// Check for status.state (common pattern)
state, found, _ := unstructured.NestedString(item.Object, "status", "state")
if found && state != "" {
lowerState := strings.ToLower(state)
if lowerState == "failed" || lowerState == "error" {
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Resource state is %s", state),
})
}
}
return failures
}

410
pkg/analyzer/crd_test.go Normal file
View File

@@ -0,0 +1,410 @@
/*
Copyright 2023 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyzer
import (
"context"
"strings"
"testing"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/spf13/viper"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/client-go/rest"
)
// TestCRDAnalyzer_Disabled tests that analyzer returns nil when disabled
func TestCRDAnalyzer_Disabled(t *testing.T) {
viper.Reset()
viper.Set("crd_analyzer", map[string]interface{}{
"enabled": false,
})
a := common.Analyzer{
Context: context.TODO(),
Client: &kubernetes.Client{},
}
res, err := (CRDAnalyzer{}).Analyze(a)
if err != nil {
t.Fatalf("Analyze error: %v", err)
}
if res != nil {
t.Fatalf("expected nil result when disabled, got %d results", len(res))
}
}
// TestCRDAnalyzer_NoConfig tests that analyzer returns nil when no config exists
func TestCRDAnalyzer_NoConfig(t *testing.T) {
viper.Reset()
a := common.Analyzer{
Context: context.TODO(),
Client: &kubernetes.Client{},
}
res, err := (CRDAnalyzer{}).Analyze(a)
if err != nil {
t.Fatalf("Analyze error: %v", err)
}
if res != nil {
t.Fatalf("expected nil result when no config, got %d results", len(res))
}
}
// TestAnalyzeGenericHealth_ReadyConditionFalse tests detection of Ready=False condition
func TestAnalyzeGenericHealth_ReadyConditionFalse(t *testing.T) {
item := unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "cert-manager.io/v1",
"kind": "Certificate",
"metadata": map[string]interface{}{
"name": "example-cert",
"namespace": "default",
},
"status": map[string]interface{}{
"conditions": []interface{}{
map[string]interface{}{
"type": "Ready",
"status": "False",
"reason": "Failed",
"message": "Certificate issuance failed",
},
},
},
},
}
failures := analyzeGenericHealth(item)
if len(failures) != 1 {
t.Fatalf("expected 1 failure, got %d", len(failures))
}
if !strings.Contains(failures[0].Text, "Ready is False") {
t.Errorf("expected 'Ready is False' in failure text, got: %s", failures[0].Text)
}
if !strings.Contains(failures[0].Text, "Failed") {
t.Errorf("expected 'Failed' reason in failure text, got: %s", failures[0].Text)
}
}
// TestAnalyzeGenericHealth_FailedPhase tests detection of Failed phase
func TestAnalyzeGenericHealth_FailedPhase(t *testing.T) {
item := unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "example.io/v1",
"kind": "CustomJob",
"metadata": map[string]interface{}{
"name": "failed-job",
"namespace": "default",
},
"status": map[string]interface{}{
"phase": "Failed",
},
},
}
failures := analyzeGenericHealth(item)
if len(failures) != 1 {
t.Fatalf("expected 1 failure, got %d", len(failures))
}
if !strings.Contains(failures[0].Text, "phase is Failed") {
t.Errorf("expected 'phase is Failed' in failure text, got: %s", failures[0].Text)
}
}
// TestAnalyzeGenericHealth_UnhealthyHealthStatus tests ArgoCD-style health status
func TestAnalyzeGenericHealth_UnhealthyHealthStatus(t *testing.T) {
item := unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "argoproj.io/v1alpha1",
"kind": "Application",
"metadata": map[string]interface{}{
"name": "my-app",
"namespace": "argocd",
},
"status": map[string]interface{}{
"health": map[string]interface{}{
"status": "Degraded",
},
},
},
}
failures := analyzeGenericHealth(item)
if len(failures) != 1 {
t.Fatalf("expected 1 failure, got %d", len(failures))
}
if !strings.Contains(failures[0].Text, "Health status is Degraded") {
t.Errorf("expected 'Health status is Degraded' in failure text, got: %s", failures[0].Text)
}
}
// TestAnalyzeGenericHealth_HealthyResource tests that healthy resources are not flagged
func TestAnalyzeGenericHealth_HealthyResource(t *testing.T) {
item := unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "cert-manager.io/v1",
"kind": "Certificate",
"metadata": map[string]interface{}{
"name": "healthy-cert",
"namespace": "default",
},
"status": map[string]interface{}{
"conditions": []interface{}{
map[string]interface{}{
"type": "Ready",
"status": "True",
},
},
},
},
}
failures := analyzeGenericHealth(item)
if len(failures) != 0 {
t.Fatalf("expected 0 failures for healthy resource, got %d", len(failures))
}
}
// TestAnalyzeResource_DeletionWithFinalizers tests detection of stuck deletion
func TestAnalyzeResource_DeletionWithFinalizers(t *testing.T) {
deletionTimestamp := metav1.Now()
item := unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "example.io/v1",
"kind": "CustomResource",
"metadata": map[string]interface{}{
"name": "stuck-resource",
"namespace": "default",
"deletionTimestamp": deletionTimestamp.Format("2006-01-02T15:04:05Z"),
"finalizers": []interface{}{"example.io/finalizer"},
},
},
}
item.SetDeletionTimestamp(&deletionTimestamp)
item.SetFinalizers([]string{"example.io/finalizer"})
crd := apiextensionsv1.CustomResourceDefinition{}
failures := analyzeResource(item, crd, nil)
if len(failures) != 1 {
t.Fatalf("expected 1 failure for stuck deletion, got %d", len(failures))
}
if !strings.Contains(failures[0].Text, "being deleted") {
t.Errorf("expected 'being deleted' in failure text, got: %s", failures[0].Text)
}
if !strings.Contains(failures[0].Text, "finalizers") {
t.Errorf("expected 'finalizers' in failure text, got: %s", failures[0].Text)
}
}
// TestAnalyzeWithConfig_ReadyConditionCheck tests custom ready condition checking
func TestAnalyzeWithConfig_ReadyConditionCheck(t *testing.T) {
item := unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "cert-manager.io/v1",
"kind": "Certificate",
"metadata": map[string]interface{}{
"name": "test-cert",
"namespace": "default",
},
"status": map[string]interface{}{
"conditions": []interface{}{
map[string]interface{}{
"type": "Ready",
"status": "False",
"message": "Certificate not issued",
},
},
},
},
}
config := &common.CRDIncludeConfig{
ReadyCondition: &common.CRDReadyCondition{
Type: "Ready",
ExpectedStatus: "True",
},
}
failures := analyzeWithConfig(item, config)
if len(failures) != 1 {
t.Fatalf("expected 1 failure, got %d", len(failures))
}
if !strings.Contains(failures[0].Text, "Ready condition not met") {
t.Errorf("expected 'Ready condition not met' in failure text, got: %s", failures[0].Text)
}
}
// TestAnalyzeWithConfig_ExpectedValueCheck tests custom status path value checking
func TestAnalyzeWithConfig_ExpectedValueCheck(t *testing.T) {
item := unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "argoproj.io/v1alpha1",
"kind": "Application",
"metadata": map[string]interface{}{
"name": "my-app",
"namespace": "argocd",
},
"status": map[string]interface{}{
"health": map[string]interface{}{
"status": "Degraded",
},
},
},
}
config := &common.CRDIncludeConfig{
StatusPath: "status.health.status",
ExpectedValue: "Healthy",
}
failures := analyzeWithConfig(item, config)
if len(failures) != 1 {
t.Fatalf("expected 1 failure, got %d", len(failures))
}
if !strings.Contains(failures[0].Text, "Degraded") {
t.Errorf("expected 'Degraded' in failure text, got: %s", failures[0].Text)
}
if !strings.Contains(failures[0].Text, "expected 'Healthy'") {
t.Errorf("expected 'expected Healthy' in failure text, got: %s", failures[0].Text)
}
}
// TestShouldExcludeCRD tests exclusion logic
func TestShouldExcludeCRD(t *testing.T) {
excludeList := []common.CRDExcludeConfig{
{Name: "kafkatopics.kafka.strimzi.io"},
{Name: "prometheuses.monitoring.coreos.com"},
}
if !shouldExcludeCRD("kafkatopics.kafka.strimzi.io", excludeList) {
t.Error("expected kafkatopics to be excluded")
}
if shouldExcludeCRD("certificates.cert-manager.io", excludeList) {
t.Error("expected certificates not to be excluded")
}
}
// TestGetCRDConfig tests configuration retrieval
func TestGetCRDConfig(t *testing.T) {
includeList := []common.CRDIncludeConfig{
{
Name: "certificates.cert-manager.io",
StatusPath: "status.conditions",
ReadyCondition: &common.CRDReadyCondition{
Type: "Ready",
ExpectedStatus: "True",
},
},
}
config := getCRDConfig("certificates.cert-manager.io", includeList)
if config == nil {
t.Fatal("expected config to be found")
}
if config.StatusPath != "status.conditions" {
t.Errorf("expected StatusPath 'status.conditions', got %s", config.StatusPath)
}
config = getCRDConfig("nonexistent.crd.io", includeList)
if config != nil {
t.Error("expected nil config for non-existent CRD")
}
}
// TestAnalyzeGenericHealth_MultipleConditionTypes tests handling multiple condition types
func TestAnalyzeGenericHealth_MultipleConditionTypes(t *testing.T) {
item := unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "example.io/v1",
"kind": "CustomResource",
"metadata": map[string]interface{}{
"name": "multi-cond",
"namespace": "default",
},
"status": map[string]interface{}{
"conditions": []interface{}{
map[string]interface{}{
"type": "Available",
"status": "True",
},
map[string]interface{}{
"type": "Ready",
"status": "False",
"reason": "Pending",
"message": "Waiting for dependencies",
},
},
},
},
}
failures := analyzeGenericHealth(item)
if len(failures) != 1 {
t.Fatalf("expected 1 failure (Ready=False), got %d", len(failures))
}
if !strings.Contains(failures[0].Text, "Ready is False") {
t.Errorf("expected 'Ready is False' in failure text, got: %s", failures[0].Text)
}
}
// TestAnalyzeGenericHealth_NoStatusFields tests resource without any status fields
func TestAnalyzeGenericHealth_NoStatusFields(t *testing.T) {
item := unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "example.io/v1",
"kind": "CustomResource",
"metadata": map[string]interface{}{
"name": "no-status",
"namespace": "default",
},
},
}
failures := analyzeGenericHealth(item)
if len(failures) != 0 {
t.Fatalf("expected 0 failures for resource without status, got %d", len(failures))
}
}
// TestCRDAnalyzer_NilClientConfig tests that the analyzer handles errors gracefully
func TestCRDAnalyzer_NilClientConfig(t *testing.T) {
viper.Reset()
viper.Set("crd_analyzer", map[string]interface{}{
"enabled": true,
})
// Create a client with a config that will cause an error when trying to create apiextensions client
a := common.Analyzer{
Context: context.TODO(),
Client: &kubernetes.Client{Config: &rest.Config{}},
}
// The analyzer should handle the error gracefully without panicking
results, err := (CRDAnalyzer{}).Analyze(a)
// We expect either an error or no results, but no panic
if err != nil {
// Error is expected in this case - that's fine
if results != nil {
t.Errorf("Expected nil results when error occurs, got %v", results)
}
}
// The important thing is that we didn't panic
}

60
pkg/cache/cache_test.go vendored Normal file
View File

@@ -0,0 +1,60 @@
package cache
import (
"os"
"testing"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
)
func TestNewReturnsExpectedCache(t *testing.T) {
require.IsType(t, &FileBasedCache{}, New("file"))
require.IsType(t, &AzureCache{}, New("azure"))
require.IsType(t, &GCSCache{}, New("gcs"))
require.IsType(t, &S3Cache{}, New("s3"))
require.IsType(t, &InterplexCache{}, New("interplex"))
// default fallback
require.IsType(t, &FileBasedCache{}, New("unknown"))
}
func TestNewCacheProvider_InterplexAndInvalid(t *testing.T) {
// valid: interplex
cp, err := NewCacheProvider("interplex", "", "", "localhost:1", "", "", "", false)
require.NoError(t, err)
require.Equal(t, "interplex", cp.CurrentCacheType)
require.Equal(t, "localhost:1", cp.Interplex.ConnectionString)
// invalid type
_, err = NewCacheProvider("not-a-type", "", "", "", "", "", "", false)
require.Error(t, err)
}
func TestAddRemoveRemoteCacheAndGet(t *testing.T) {
// isolate viper with temp config file
tmpFile, err := os.CreateTemp("", "k8sgpt-cache-config-*.yaml")
require.NoError(t, err)
defer func() {
_ = os.Remove(tmpFile.Name())
}()
viper.Reset()
viper.SetConfigFile(tmpFile.Name())
// add interplex remote cache
cp := CacheProvider{}
cp.CurrentCacheType = "interplex"
cp.Interplex.ConnectionString = "localhost:1"
require.NoError(t, AddRemoteCache(cp))
// read back via GetCacheConfiguration
c, err := GetCacheConfiguration()
require.NoError(t, err)
require.IsType(t, &InterplexCache{}, c)
// remove remote cache
require.NoError(t, RemoveRemoteCache())
// now default should be file-based
c2, err := GetCacheConfiguration()
require.NoError(t, err)
require.IsType(t, &FileBasedCache{}, c2)
}

77
pkg/cache/file_based_test.go vendored Normal file
View File

@@ -0,0 +1,77 @@
package cache
import (
"os"
"path/filepath"
"testing"
"github.com/adrg/xdg"
"github.com/stretchr/testify/require"
)
// withTempCacheHome sets XDG_CACHE_HOME to a temp dir for test isolation.
func withTempCacheHome(t *testing.T) func() {
t.Helper()
tmp, err := os.MkdirTemp("", "k8sgpt-cache-test-*")
require.NoError(t, err)
old := os.Getenv("XDG_CACHE_HOME")
require.NoError(t, os.Setenv("XDG_CACHE_HOME", tmp))
return func() {
_ = os.Setenv("XDG_CACHE_HOME", old)
_ = os.RemoveAll(tmp)
}
}
func TestFileBasedCache_BasicOps(t *testing.T) {
cleanup := withTempCacheHome(t)
defer cleanup()
c := &FileBasedCache{}
// Configure should be a no-op
require.NoError(t, c.Configure(CacheProvider{}))
require.Equal(t, "file", c.GetName())
require.False(t, c.IsCacheDisabled())
c.DisableCache()
require.True(t, c.IsCacheDisabled())
key := "testkey"
data := "hello"
// Store
require.NoError(t, c.Store(key, data))
// Exists
require.True(t, c.Exists(key))
// Load
got, err := c.Load(key)
require.NoError(t, err)
require.Equal(t, data, got)
// List should include our key file
items, err := c.List()
require.NoError(t, err)
// ensure at least one item and that one matches our key
found := false
for _, it := range items {
if it.Name == key {
found = true
break
}
}
require.True(t, found)
// Remove
require.NoError(t, c.Remove(key))
require.False(t, c.Exists(key))
}
func TestFileBasedCache_PathShape(t *testing.T) {
cleanup := withTempCacheHome(t)
defer cleanup()
// Verify xdg.CacheFile path shape (directory and filename)
p, err := xdg.CacheFile(filepath.Join("k8sgpt", "abc"))
require.NoError(t, err)
require.Equal(t, "abc", filepath.Base(p))
require.Contains(t, p, "k8sgpt")
}

View File

@@ -18,18 +18,31 @@ func TestInterplexCache(t *testing.T) {
}
// Mock GRPC server setup
errChan := make(chan error, 1)
go func() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
t.Fatalf("failed to listen: %v", err)
errChan <- err
return
}
s := grpc.NewServer()
rpc.RegisterCacheServiceServer(s, &mockCacheService{})
if err := s.Serve(lis); err != nil {
t.Fatalf("failed to serve: %v", err)
errChan <- err
return
}
}()
// Check if server startup failed
select {
case err := <-errChan:
if err != nil {
t.Fatalf("failed to start mock server: %v", err)
}
default:
// Server started successfully
}
t.Run("TestStore", func(t *testing.T) {
err := cache.Store("key1", "value1")
if err != nil {

View File

@@ -97,6 +97,32 @@ type Sensitive struct {
Masked string
}
// CRDAnalyzerConfig defines the configuration for the generic CRD analyzer
type CRDAnalyzerConfig struct {
Enabled bool `yaml:"enabled" json:"enabled"`
Include []CRDIncludeConfig `yaml:"include" json:"include"`
Exclude []CRDExcludeConfig `yaml:"exclude" json:"exclude"`
}
// CRDIncludeConfig defines configuration for a specific CRD to analyze
type CRDIncludeConfig struct {
Name string `yaml:"name" json:"name"`
StatusPath string `yaml:"statusPath" json:"statusPath"`
ReadyCondition *CRDReadyCondition `yaml:"readyCondition" json:"readyCondition"`
ExpectedValue string `yaml:"expectedValue" json:"expectedValue"`
}
// CRDReadyCondition defines the expected ready condition
type CRDReadyCondition struct {
Type string `yaml:"type" json:"type"`
ExpectedStatus string `yaml:"expectedStatus" json:"expectedStatus"`
}
// CRDExcludeConfig defines a CRD to exclude from analysis
type CRDExcludeConfig struct {
Name string `yaml:"name" json:"name"`
}
type (
SourceType string
AvailabilityMode string

41
pkg/custom/client_test.go Normal file
View File

@@ -0,0 +1,41 @@
package custom
import (
"context"
"testing"
schemav1 "buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go/schema/v1"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
)
// mockAnalyzerClient implements rpc.CustomAnalyzerServiceClient for testing
type mockAnalyzerClient struct{
resp *schemav1.RunResponse
err error
}
func (m *mockAnalyzerClient) Run(ctx context.Context, in *schemav1.RunRequest, opts ...grpc.CallOption) (*schemav1.RunResponse, error) {
return m.resp, m.err
}
func TestClientRunMapsResponse(t *testing.T) {
// prepare fake response
resp := &schemav1.RunResponse{
Result: &schemav1.Result{
Name: "AnalyzerA",
Kind: "Pod",
Details: "details",
ParentObject: "Deployment/foo",
},
}
cli := &Client{analyzerClient: &mockAnalyzerClient{resp: resp}}
got, err := cli.Run()
require.NoError(t, err)
require.Equal(t, "AnalyzerA", got.Name)
require.Equal(t, "Pod", got.Kind)
require.Equal(t, "details", got.Details)
require.Equal(t, "Deployment/foo", got.ParentObject)
require.Len(t, got.Error, 0)
}

View File

@@ -28,6 +28,7 @@ import (
"github.com/mark3labs/mcp-go/server"
"github.com/spf13/viper"
"go.uber.org/zap"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// K8sGptMCPServer represents an MCP server for k8sgpt
@@ -67,6 +68,8 @@ func NewMCPServer(port string, aiProvider *ai.AIProvider, useHTTP bool, logger *
// Create HTTP server with streamable transport
httpOpts := []server.StreamableHTTPOption{
server.WithLogger(&zapLoggerAdapter{logger: logger}),
// Enable stateless mode for one-off tool invocations without session management
server.WithStateLess(true),
}
httpServer := server.NewStreamableHTTPServer(mcpServer, httpOpts...)
@@ -218,6 +221,122 @@ func (s *K8sGptMCPServer) registerToolsAndResources() error {
),
)
s.server.AddTool(configTool, s.handleConfig)
// Register resource listing tools
listResourcesTool := mcp.NewTool("list-resources",
mcp.WithDescription("List Kubernetes resources of a specific type (pods, deployments, services, nodes, etc.)"),
mcp.WithString("resourceType",
mcp.Required(),
mcp.Description("Type of resource to list (e.g., pods, deployments, services, nodes, jobs, etc.)"),
),
mcp.WithString("namespace",
mcp.Description("Namespace to list resources from (empty for all or cluster-scoped resources)"),
),
mcp.WithString("labelSelector",
mcp.Description("Label selector to filter resources (e.g., 'app=myapp')"),
),
)
s.server.AddTool(listResourcesTool, s.handleListResources)
// Register get resource tool
getResourceTool := mcp.NewTool("get-resource",
mcp.WithDescription("Get detailed information about a specific Kubernetes resource"),
mcp.WithString("resourceType",
mcp.Required(),
mcp.Description("Type of resource (e.g., pod, deployment, service)"),
),
mcp.WithString("name",
mcp.Required(),
mcp.Description("Name of the resource"),
),
mcp.WithString("namespace",
mcp.Description("Namespace of the resource (required for namespaced resources)"),
),
)
s.server.AddTool(getResourceTool, s.handleGetResource)
// Register list namespaces tool
listNamespacesTool := mcp.NewTool("list-namespaces",
mcp.WithDescription("List all namespaces in the cluster"),
)
s.server.AddTool(listNamespacesTool, s.handleListNamespaces)
// Register list events tool
listEventsTool := mcp.NewTool("list-events",
mcp.WithDescription("List Kubernetes events for debugging and troubleshooting"),
mcp.WithString("namespace",
mcp.Description("Namespace to list events from (empty for all namespaces)"),
),
mcp.WithString("involvedObjectName",
mcp.Description("Filter events by involved object name (e.g., pod name)"),
),
mcp.WithString("involvedObjectKind",
mcp.Description("Filter events by involved object kind (e.g., Pod, Deployment)"),
),
mcp.WithNumber("limit",
mcp.Description("Maximum number of events to return (default: 100)"),
),
)
s.server.AddTool(listEventsTool, s.handleListEvents)
// Register get logs tool
getLogsTool := mcp.NewTool("get-logs",
mcp.WithDescription("Get logs from a pod container"),
mcp.WithString("podName",
mcp.Required(),
mcp.Description("Name of the pod"),
),
mcp.WithString("namespace",
mcp.Required(),
mcp.Description("Namespace of the pod"),
),
mcp.WithString("container",
mcp.Description("Container name (if pod has multiple containers)"),
),
mcp.WithBoolean("previous",
mcp.Description("Get logs from previous terminated container"),
),
mcp.WithNumber("tailLines",
mcp.Description("Number of lines from the end of logs (default: 100)"),
),
mcp.WithNumber("sinceSeconds",
mcp.Description("Return logs newer than this many seconds"),
),
)
s.server.AddTool(getLogsTool, s.handleGetLogs)
// Register filter management tools
listFiltersTool := mcp.NewTool("list-filters",
mcp.WithDescription("List all available and active analyzers/filters in k8sgpt"),
)
s.server.AddTool(listFiltersTool, s.handleListFilters)
addFiltersTool := mcp.NewTool("add-filters",
mcp.WithDescription("Add filters to enable specific analyzers"),
mcp.WithArray("filters",
mcp.Required(),
mcp.Description("List of filter names to add (e.g., ['Pod', 'Service', 'Deployment'])"),
mcp.WithStringItems(),
),
)
s.server.AddTool(addFiltersTool, s.handleAddFilters)
removeFiltersTool := mcp.NewTool("remove-filters",
mcp.WithDescription("Remove filters to disable specific analyzers"),
mcp.WithArray("filters",
mcp.Required(),
mcp.Description("List of filter names to remove"),
mcp.WithStringItems(),
),
)
s.server.AddTool(removeFiltersTool, s.handleRemoveFilters)
// Register integration management tools
listIntegrationsTool := mcp.NewTool("list-integrations",
mcp.WithDescription("List available integrations (Prometheus, AWS, Keda, Kyverno, etc.)"),
)
s.server.AddTool(listIntegrationsTool, s.handleListIntegrations)
return nil
}
@@ -453,7 +572,26 @@ func (s *K8sGptMCPServer) handleConfig(ctx context.Context, request mcp.CallTool
// registerPrompts registers the prompts for the MCP server
func (s *K8sGptMCPServer) registerPrompts() error {
// Register any prompts needed for the MCP server
// Register troubleshooting prompts
podTroubleshootPrompt := mcp.NewPrompt("troubleshoot-pod",
mcp.WithPromptDescription("Guide for troubleshooting pod issues in Kubernetes"),
mcp.WithArgument("podName"),
mcp.WithArgument("namespace"),
)
s.server.AddPrompt(podTroubleshootPrompt, s.getTroubleshootPodPrompt)
deploymentTroubleshootPrompt := mcp.NewPrompt("troubleshoot-deployment",
mcp.WithPromptDescription("Guide for troubleshooting deployment issues in Kubernetes"),
mcp.WithArgument("deploymentName"),
mcp.WithArgument("namespace"),
)
s.server.AddPrompt(deploymentTroubleshootPrompt, s.getTroubleshootDeploymentPrompt)
generalTroubleshootPrompt := mcp.NewPrompt("troubleshoot-cluster",
mcp.WithPromptDescription("General guide for troubleshooting Kubernetes cluster issues"),
)
s.server.AddPrompt(generalTroubleshootPrompt, s.getTroubleshootClusterPrompt)
return nil
}
@@ -463,8 +601,20 @@ func (s *K8sGptMCPServer) registerResources() error {
mcp.WithResourceDescription("Get information about the Kubernetes cluster"),
mcp.WithMIMEType("application/json"),
)
s.server.AddResource(clusterInfoResource, s.getClusterInfo)
namespacesResource := mcp.NewResource("namespaces", "namespaces",
mcp.WithResourceDescription("List all namespaces in the cluster"),
mcp.WithMIMEType("application/json"),
)
s.server.AddResource(namespacesResource, s.getNamespacesResource)
activeFiltersResource := mcp.NewResource("active-filters", "active-filters",
mcp.WithResourceDescription("Get currently active analyzers/filters"),
mcp.WithMIMEType("application/json"),
)
s.server.AddResource(activeFiltersResource, s.getActiveFiltersResource)
return nil
}
@@ -505,6 +655,72 @@ func (s *K8sGptMCPServer) getClusterInfo(ctx context.Context, request mcp.ReadRe
}, nil
}
func (s *K8sGptMCPServer) getNamespacesResource(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
client, err := kubernetes.NewClient("", "")
if err != nil {
return nil, fmt.Errorf("failed to create Kubernetes client: %v", err)
}
namespaces, err := client.Client.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
if err != nil {
return nil, fmt.Errorf("failed to list namespaces: %v", err)
}
// Extract just the namespace names
names := make([]string, 0, len(namespaces.Items))
for _, ns := range namespaces.Items {
names = append(names, ns.Name)
}
data, err := json.Marshal(map[string]interface{}{
"count": len(names),
"namespaces": names,
})
if err != nil {
return []mcp.ResourceContents{
&mcp.TextResourceContents{
URI: "namespaces",
MIMEType: "text/plain",
Text: "Failed to marshal namespaces",
},
}, nil
}
return []mcp.ResourceContents{
&mcp.TextResourceContents{
URI: "namespaces",
MIMEType: "application/json",
Text: string(data),
},
}, nil
}
func (s *K8sGptMCPServer) getActiveFiltersResource(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
activeFilters := viper.GetStringSlice("active_filters")
data, err := json.Marshal(map[string]interface{}{
"activeFilters": activeFilters,
"count": len(activeFilters),
})
if err != nil {
return []mcp.ResourceContents{
&mcp.TextResourceContents{
URI: "active-filters",
MIMEType: "text/plain",
Text: "Failed to marshal active filters",
},
}, nil
}
return []mcp.ResourceContents{
&mcp.TextResourceContents{
URI: "active-filters",
MIMEType: "application/json",
Text: string(data),
},
}, nil
}
// Close closes the MCP server and releases resources
func (s *K8sGptMCPServer) Close() error {
return nil

438
pkg/server/mcp_handlers.go Normal file
View File

@@ -0,0 +1,438 @@
/*
Copyright 2024 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package server
import (
"context"
"encoding/json"
"fmt"
"io"
"strings"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
"github.com/k8sgpt-ai/k8sgpt/pkg/integration"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/mark3labs/mcp-go/mcp"
"github.com/spf13/viper"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// handleListResources lists Kubernetes resources of a specific type
func (s *K8sGptMCPServer) handleListResources(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
var req struct {
ResourceType string `json:"resourceType"`
Namespace string `json:"namespace,omitempty"`
LabelSelector string `json:"labelSelector,omitempty"`
}
if err := request.BindArguments(&req); err != nil {
return mcp.NewToolResultErrorf("Failed to parse request arguments: %v", err), nil
}
client, err := kubernetes.NewClient("", "")
if err != nil {
return mcp.NewToolResultErrorf("Failed to create Kubernetes client: %v", err), nil
}
listOptions := metav1.ListOptions{}
if req.LabelSelector != "" {
listOptions.LabelSelector = req.LabelSelector
}
var result string
resourceType := strings.ToLower(req.ResourceType)
switch resourceType {
case "pod", "pods":
pods, err := client.Client.CoreV1().Pods(req.Namespace).List(ctx, listOptions)
if err != nil {
return mcp.NewToolResultErrorf("Failed to list pods: %v", err), nil
}
data, _ := json.MarshalIndent(pods.Items, "", " ")
result = string(data)
case "deployment", "deployments":
deps, err := client.Client.AppsV1().Deployments(req.Namespace).List(ctx, listOptions)
if err != nil {
return mcp.NewToolResultErrorf("Failed to list deployments: %v", err), nil
}
data, _ := json.MarshalIndent(deps.Items, "", " ")
result = string(data)
case "service", "services", "svc":
svcs, err := client.Client.CoreV1().Services(req.Namespace).List(ctx, listOptions)
if err != nil {
return mcp.NewToolResultErrorf("Failed to list services: %v", err), nil
}
data, _ := json.MarshalIndent(svcs.Items, "", " ")
result = string(data)
case "node", "nodes":
nodes, err := client.Client.CoreV1().Nodes().List(ctx, listOptions)
if err != nil {
return mcp.NewToolResultErrorf("Failed to list nodes: %v", err), nil
}
data, _ := json.MarshalIndent(nodes.Items, "", " ")
result = string(data)
case "job", "jobs":
jobs, err := client.Client.BatchV1().Jobs(req.Namespace).List(ctx, listOptions)
if err != nil {
return mcp.NewToolResultErrorf("Failed to list jobs: %v", err), nil
}
data, _ := json.MarshalIndent(jobs.Items, "", " ")
result = string(data)
case "cronjob", "cronjobs":
cronjobs, err := client.Client.BatchV1().CronJobs(req.Namespace).List(ctx, listOptions)
if err != nil {
return mcp.NewToolResultErrorf("Failed to list cronjobs: %v", err), nil
}
data, _ := json.MarshalIndent(cronjobs.Items, "", " ")
result = string(data)
case "statefulset", "statefulsets", "sts":
sts, err := client.Client.AppsV1().StatefulSets(req.Namespace).List(ctx, listOptions)
if err != nil {
return mcp.NewToolResultErrorf("Failed to list statefulsets: %v", err), nil
}
data, _ := json.MarshalIndent(sts.Items, "", " ")
result = string(data)
case "daemonset", "daemonsets", "ds":
ds, err := client.Client.AppsV1().DaemonSets(req.Namespace).List(ctx, listOptions)
if err != nil {
return mcp.NewToolResultErrorf("Failed to list daemonsets: %v", err), nil
}
data, _ := json.MarshalIndent(ds.Items, "", " ")
result = string(data)
case "replicaset", "replicasets", "rs":
rs, err := client.Client.AppsV1().ReplicaSets(req.Namespace).List(ctx, listOptions)
if err != nil {
return mcp.NewToolResultErrorf("Failed to list replicasets: %v", err), nil
}
data, _ := json.MarshalIndent(rs.Items, "", " ")
result = string(data)
case "configmap", "configmaps", "cm":
cms, err := client.Client.CoreV1().ConfigMaps(req.Namespace).List(ctx, listOptions)
if err != nil {
return mcp.NewToolResultErrorf("Failed to list configmaps: %v", err), nil
}
data, _ := json.MarshalIndent(cms.Items, "", " ")
result = string(data)
case "secret", "secrets":
secrets, err := client.Client.CoreV1().Secrets(req.Namespace).List(ctx, listOptions)
if err != nil {
return mcp.NewToolResultErrorf("Failed to list secrets: %v", err), nil
}
data, _ := json.MarshalIndent(secrets.Items, "", " ")
result = string(data)
case "ingress", "ingresses", "ing":
ingresses, err := client.Client.NetworkingV1().Ingresses(req.Namespace).List(ctx, listOptions)
if err != nil {
return mcp.NewToolResultErrorf("Failed to list ingresses: %v", err), nil
}
data, _ := json.MarshalIndent(ingresses.Items, "", " ")
result = string(data)
case "persistentvolumeclaim", "persistentvolumeclaims", "pvc":
pvcs, err := client.Client.CoreV1().PersistentVolumeClaims(req.Namespace).List(ctx, listOptions)
if err != nil {
return mcp.NewToolResultErrorf("Failed to list PVCs: %v", err), nil
}
data, _ := json.MarshalIndent(pvcs.Items, "", " ")
result = string(data)
case "persistentvolume", "persistentvolumes", "pv":
pvs, err := client.Client.CoreV1().PersistentVolumes().List(ctx, listOptions)
if err != nil {
return mcp.NewToolResultErrorf("Failed to list PVs: %v", err), nil
}
data, _ := json.MarshalIndent(pvs.Items, "", " ")
result = string(data)
default:
return mcp.NewToolResultErrorf("Unsupported resource type: %s. Supported types: pods, deployments, services, nodes, jobs, cronjobs, statefulsets, daemonsets, replicasets, configmaps, secrets, ingresses, pvc, pv", resourceType), nil
}
return mcp.NewToolResultText(result), nil
}
// handleGetResource gets detailed information about a specific resource
func (s *K8sGptMCPServer) handleGetResource(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
var req struct {
ResourceType string `json:"resourceType"`
Name string `json:"name"`
Namespace string `json:"namespace,omitempty"`
}
if err := request.BindArguments(&req); err != nil {
return mcp.NewToolResultErrorf("Failed to parse request arguments: %v", err), nil
}
client, err := kubernetes.NewClient("", "")
if err != nil {
return mcp.NewToolResultErrorf("Failed to create Kubernetes client: %v", err), nil
}
var result string
resourceType := strings.ToLower(req.ResourceType)
switch resourceType {
case "pod", "pods":
pod, err := client.Client.CoreV1().Pods(req.Namespace).Get(ctx, req.Name, metav1.GetOptions{})
if err != nil {
return mcp.NewToolResultErrorf("Failed to get pod: %v", err), nil
}
data, _ := json.MarshalIndent(pod, "", " ")
result = string(data)
case "deployment", "deployments":
dep, err := client.Client.AppsV1().Deployments(req.Namespace).Get(ctx, req.Name, metav1.GetOptions{})
if err != nil {
return mcp.NewToolResultErrorf("Failed to get deployment: %v", err), nil
}
data, _ := json.MarshalIndent(dep, "", " ")
result = string(data)
case "service", "services", "svc":
svc, err := client.Client.CoreV1().Services(req.Namespace).Get(ctx, req.Name, metav1.GetOptions{})
if err != nil {
return mcp.NewToolResultErrorf("Failed to get service: %v", err), nil
}
data, _ := json.MarshalIndent(svc, "", " ")
result = string(data)
case "node", "nodes":
node, err := client.Client.CoreV1().Nodes().Get(ctx, req.Name, metav1.GetOptions{})
if err != nil {
return mcp.NewToolResultErrorf("Failed to get node: %v", err), nil
}
data, _ := json.MarshalIndent(node, "", " ")
result = string(data)
default:
return mcp.NewToolResultErrorf("Unsupported resource type: %s", resourceType), nil
}
return mcp.NewToolResultText(result), nil
}
// handleListNamespaces lists all namespaces in the cluster
func (s *K8sGptMCPServer) handleListNamespaces(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
client, err := kubernetes.NewClient("", "")
if err != nil {
return mcp.NewToolResultErrorf("Failed to create Kubernetes client: %v", err), nil
}
namespaces, err := client.Client.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
if err != nil {
return mcp.NewToolResultErrorf("Failed to list namespaces: %v", err), nil
}
data, _ := json.MarshalIndent(namespaces.Items, "", " ")
return mcp.NewToolResultText(string(data)), nil
}
// handleListEvents lists Kubernetes events
func (s *K8sGptMCPServer) handleListEvents(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
var req struct {
Namespace string `json:"namespace,omitempty"`
InvolvedObjectName string `json:"involvedObjectName,omitempty"`
InvolvedObjectKind string `json:"involvedObjectKind,omitempty"`
Limit int64 `json:"limit,omitempty"`
}
if err := request.BindArguments(&req); err != nil {
return mcp.NewToolResultErrorf("Failed to parse request arguments: %v", err), nil
}
if req.Limit == 0 {
req.Limit = 100
}
client, err := kubernetes.NewClient("", "")
if err != nil {
return mcp.NewToolResultErrorf("Failed to create Kubernetes client: %v", err), nil
}
listOptions := metav1.ListOptions{
Limit: req.Limit,
}
events, err := client.Client.CoreV1().Events(req.Namespace).List(ctx, listOptions)
if err != nil {
return mcp.NewToolResultErrorf("Failed to list events: %v", err), nil
}
// Filter events if needed
filteredEvents := []corev1.Event{}
for _, event := range events.Items {
if req.InvolvedObjectName != "" && event.InvolvedObject.Name != req.InvolvedObjectName {
continue
}
if req.InvolvedObjectKind != "" && event.InvolvedObject.Kind != req.InvolvedObjectKind {
continue
}
filteredEvents = append(filteredEvents, event)
}
data, _ := json.MarshalIndent(filteredEvents, "", " ")
return mcp.NewToolResultText(string(data)), nil
}
// handleGetLogs retrieves logs from a pod container
func (s *K8sGptMCPServer) handleGetLogs(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
var req struct {
PodName string `json:"podName"`
Namespace string `json:"namespace"`
Container string `json:"container,omitempty"`
Previous bool `json:"previous,omitempty"`
TailLines int64 `json:"tailLines,omitempty"`
SinceSeconds int64 `json:"sinceSeconds,omitempty"`
}
if err := request.BindArguments(&req); err != nil {
return mcp.NewToolResultErrorf("Failed to parse request arguments: %v", err), nil
}
if req.TailLines == 0 {
req.TailLines = 100
}
client, err := kubernetes.NewClient("", "")
if err != nil {
return mcp.NewToolResultErrorf("Failed to create Kubernetes client: %v", err), nil
}
podLogOpts := &corev1.PodLogOptions{
Container: req.Container,
Previous: req.Previous,
TailLines: &req.TailLines,
}
if req.SinceSeconds > 0 {
podLogOpts.SinceSeconds = &req.SinceSeconds
}
logRequest := client.Client.CoreV1().Pods(req.Namespace).GetLogs(req.PodName, podLogOpts)
logStream, err := logRequest.Stream(ctx)
if err != nil {
return mcp.NewToolResultErrorf("Failed to get logs: %v", err), nil
}
defer func() {
_ = logStream.Close()
}()
logs, err := io.ReadAll(logStream)
if err != nil {
return mcp.NewToolResultErrorf("Failed to read logs: %v", err), nil
}
return mcp.NewToolResultText(string(logs)), nil
}
// handleListFilters lists available and active filters
func (s *K8sGptMCPServer) handleListFilters(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
coreFilters, additionalFilters, integrationFilters := analyzer.ListFilters()
active := viper.GetStringSlice("active_filters")
result := map[string]interface{}{
"coreFilters": coreFilters,
"additionalFilters": additionalFilters,
"integrationFilters": integrationFilters,
"activeFilters": active,
}
data, _ := json.MarshalIndent(result, "", " ")
return mcp.NewToolResultText(string(data)), nil
}
// handleAddFilters adds filters to enable specific analyzers
func (s *K8sGptMCPServer) handleAddFilters(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
var req struct {
Filters []string `json:"filters"`
}
if err := request.BindArguments(&req); err != nil {
return mcp.NewToolResultErrorf("Failed to parse request arguments: %v", err), nil
}
activeFilters := viper.GetStringSlice("active_filters")
for _, filter := range req.Filters {
if !contains(activeFilters, filter) {
activeFilters = append(activeFilters, filter)
}
}
viper.Set("active_filters", activeFilters)
if err := viper.WriteConfig(); err != nil {
return mcp.NewToolResultErrorf("Failed to save configuration: %v", err), nil
}
return mcp.NewToolResultText(fmt.Sprintf("Successfully added filters: %v", req.Filters)), nil
}
// handleRemoveFilters removes filters to disable specific analyzers
func (s *K8sGptMCPServer) handleRemoveFilters(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
var req struct {
Filters []string `json:"filters"`
}
if err := request.BindArguments(&req); err != nil {
return mcp.NewToolResultErrorf("Failed to parse request arguments: %v", err), nil
}
activeFilters := viper.GetStringSlice("active_filters")
newFilters := []string{}
for _, filter := range activeFilters {
if !contains(req.Filters, filter) {
newFilters = append(newFilters, filter)
}
}
viper.Set("active_filters", newFilters)
if err := viper.WriteConfig(); err != nil {
return mcp.NewToolResultErrorf("Failed to save configuration: %v", err), nil
}
return mcp.NewToolResultText(fmt.Sprintf("Successfully removed filters: %v", req.Filters)), nil
}
// handleListIntegrations lists available integrations
func (s *K8sGptMCPServer) handleListIntegrations(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
integrationProvider := integration.NewIntegration()
integrations := integrationProvider.List()
result := []map[string]interface{}{}
for _, integ := range integrations {
active, _ := integrationProvider.IsActivate(integ)
result = append(result, map[string]interface{}{
"name": integ,
"active": active,
})
}
data, _ := json.MarshalIndent(result, "", " ")
return mcp.NewToolResultText(string(data)), nil
}
// contains checks if a string slice contains a specific string
func contains(slice []string, item string) bool {
for _, s := range slice {
if s == item {
return true
}
}
return false
}

170
pkg/server/mcp_prompts.go Normal file
View File

@@ -0,0 +1,170 @@
/*
Copyright 2024 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package server
import (
"context"
"fmt"
"github.com/mark3labs/mcp-go/mcp"
)
// getTroubleshootPodPrompt returns a prompt for pod troubleshooting
func (s *K8sGptMCPServer) getTroubleshootPodPrompt(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
podName := ""
namespace := ""
if request.Params.Arguments != nil {
podName = request.Params.Arguments["podName"]
namespace = request.Params.Arguments["namespace"]
}
promptText := fmt.Sprintf(`You are troubleshooting a Kubernetes pod issue.
Pod: %s
Namespace: %s
Troubleshooting steps:
1. Use 'get-resource' tool to get pod details and check status, conditions, and events
2. Use 'list-events' tool with the pod name to see recent events
3. Use 'get-logs' tool to check container logs for errors
4. Check if the pod has multiple containers and inspect each
5. If the pod is in CrashLoopBackOff, use 'get-logs' with previous=true
6. Use 'analyze' tool with filters=['Pod'] to get AI-powered analysis
7. Check related resources like ConfigMaps, Secrets, and PVCs
Common issues to check:
- Image pull errors (check imagePullSecrets)
- Resource limits (CPU/memory)
- Liveness/readiness probe failures
- Volume mount issues
- Environment variable problems
- Network connectivity issues`, podName, namespace)
return &mcp.GetPromptResult{
Description: "Pod troubleshooting guide",
Messages: []mcp.PromptMessage{
{
Role: "user",
Content: mcp.TextContent{
Type: "text",
Text: promptText,
},
},
},
}, nil
}
// getTroubleshootDeploymentPrompt returns a prompt for deployment troubleshooting
func (s *K8sGptMCPServer) getTroubleshootDeploymentPrompt(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
deploymentName := ""
namespace := ""
if request.Params.Arguments != nil {
deploymentName = request.Params.Arguments["deploymentName"]
namespace = request.Params.Arguments["namespace"]
}
promptText := fmt.Sprintf(`You are troubleshooting a Kubernetes deployment issue.
Deployment: %s
Namespace: %s
Troubleshooting steps:
1. Use 'get-resource' tool to get deployment details and check replica status
2. Use 'list-resources' with resourceType='replicasets' to check ReplicaSets
3. Use 'list-resources' with resourceType='pods' and labelSelector to find deployment pods
4. Use 'list-events' tool to see deployment-related events
5. Use 'analyze' tool with filters=['Deployment','Pod'] for comprehensive analysis
6. Check pod status and logs for individual pod issues
7. Verify image availability and pull secrets
8. Check resource quotas and limits
Common deployment issues:
- Insufficient resources in the cluster
- Image pull failures
- Invalid configuration (ConfigMaps/Secrets)
- Failed rolling updates
- Readiness probe failures preventing rollout
- PVC binding issues
- Node selector/affinity constraints`, deploymentName, namespace)
return &mcp.GetPromptResult{
Description: "Deployment troubleshooting guide",
Messages: []mcp.PromptMessage{
{
Role: "user",
Content: mcp.TextContent{
Type: "text",
Text: promptText,
},
},
},
}, nil
}
// getTroubleshootClusterPrompt returns a prompt for general cluster troubleshooting
func (s *K8sGptMCPServer) getTroubleshootClusterPrompt(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
promptText := `You are performing a general Kubernetes cluster health check and troubleshooting.
Recommended troubleshooting workflow:
1. CLUSTER OVERVIEW:
- Use 'cluster-info' to get cluster version
- Use 'list-namespaces' to see all namespaces
- Use 'list-resources' with resourceType='nodes' to check node health
2. RESOURCE ANALYSIS:
- Use 'analyze' tool with explain=true for comprehensive AI-powered analysis
- Start with core resources: filters=['Pod','Deployment','Service']
- Add more filters as needed: ['Node','PersistentVolumeClaim','Job','CronJob']
3. EVENT INSPECTION:
- Use 'list-events' to see recent cluster events
- Filter by namespace for focused troubleshooting
- Look for Warning and Error events
4. SPECIFIC RESOURCE INVESTIGATION:
- Use 'list-resources' to find problematic resources
- Use 'get-resource' for detailed inspection
- Use 'get-logs' to examine container logs
5. CONFIGURATION CHECK:
- Use 'list-filters' to see available analyzers
- Use 'list-integrations' to check integrations (Prometheus, AWS, etc.)
- Use 'config' tool to modify settings if needed
Common cluster-wide issues:
- Node pressure (CPU, memory, disk)
- Network policies blocking traffic
- Storage provisioning problems
- RBAC permission issues
- Certificate expiration
- Control plane component failures
- Resource quota exhaustion
- DNS resolution problems
Use the available tools systematically to narrow down the issue.`
return &mcp.GetPromptResult{
Description: "General cluster troubleshooting guide",
Messages: []mcp.PromptMessage{
{
Role: "user",
Content: mcp.TextContent{
Type: "text",
Text: promptText,
},
},
},
}, nil
}

View File

@@ -54,6 +54,8 @@ type Config struct {
AnalyzeHandler *analyze.Handler
QueryHandler *query.Handler
Logger *zap.Logger
// Filters can be injected into the server to limit analysis to specific analyzers
Filters []string
metricsServer *http.Server
listener net.Listener
EnableHttp bool

View File

@@ -14,6 +14,8 @@ limitations under the License.
package util
import (
"encoding/base64"
"fmt"
"testing"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
@@ -23,6 +25,7 @@ import (
v1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/kubernetes/fake"
)
@@ -503,3 +506,74 @@ func TestLabelsIncludeAny(t *testing.T) {
})
}
}
func TestMaskString(t *testing.T) {
input := "mysecret"
masked := MaskString(input)
// decode base64 to compare properties
decoded, err := base64.StdEncoding.DecodeString(masked)
require.NoError(t, err)
require.Len(t, decoded, len(input))
// ensure it is not equal to input
require.NotEqual(t, input, string(decoded))
// ensure all runes are from anonymizePattern
allowed := make(map[rune]struct{})
for _, r := range anonymizePattern {
allowed[r] = struct{}{}
}
for _, r := range string(decoded) {
_, ok := allowed[r]
require.True(t, ok, "unexpected rune: %q", r)
}
}
func TestNewHeaders(t *testing.T) {
input := []string{
"X-Test: foo",
"X-Test: bar",
"Content-Type: application/json",
"InvalidHeader", // should be ignored
}
hs := NewHeaders(input)
// flatten to a map for easier assertions
got := map[string][]string{}
for _, h := range hs {
for k, v := range h {
got[k] = append(got[k], v...)
}
}
// expected values
require.Contains(t, got, "X-Test")
require.Contains(t, got, "Content-Type")
// order of values is not guaranteed
require.ElementsMatch(t, []string{"foo", "bar"}, got["X-Test"])
require.ElementsMatch(t, []string{"application/json"}, got["Content-Type"])
}
func TestLabelStrToSelector(t *testing.T) {
// empty case returns nil
require.Nil(t, LabelStrToSelector(""))
sel := LabelStrToSelector("key=value,foo=bar")
require.NotNil(t, sel)
// matches exact set
m := map[string]string{"key": "value", "foo": "bar"}
require.True(t, sel.Matches(labels.Set(m)))
// does not match different values
m2 := map[string]string{"key": "other", "foo": "bar"}
require.False(t, sel.Matches(labels.Set(m2)))
}
func TestCaptureOutput(t *testing.T) {
out := CaptureOutput(func() {
fmt.Print("hello world")
})
require.Equal(t, "hello world", out)
}
func TestContains(t *testing.T) {
require.True(t, Contains("abcdef", "bcd"))
require.False(t, Contains("abcdef", "xyz"))
}